emGee Software Solutions Custom Database Applications

Share this

Tuts+ Code - Web Development

Updated: 1 day 10 hours ago

Set Up Routing in PHP Applications Using the Symfony Routing Component

Fri, 07/13/2018 - 07:00

Today, we'll go through the Symfony Routing component, which allows you to set up routing in your PHP applications.

What Is the Symfony Routing Component?

The Symfony Routing Component is a very popular routing component which is adapted by several frameworks and provides a lot of flexibility should you wish to set up routes in your PHP application.

If you've built a custom PHP application and are looking for a feature-rich routing library, the Symfony Routing Component is more than a worth a look. It also allows you to define routes for your application in the YAML format.

Starting with installation and configuration, we'll go through real-world examples to demonstrate a variety of options the component has for route configuration. In this article, you'll learn:

  • installation and configuration
  • how to set up basic routes
  • how to load routes from the YAML file
  • how to use the all-in-one router
Installation and Configuration

In this section, we're going to install the libraries that are required in order to set up routing in your PHP applications. I assume that you've installed Composer in your system as we'll need it to install the necessary libraries that are available on Packagist.

Once you've installed Composer, go ahead and install the core Routing component using the following command.

$composer require symfony/routing

Although the Routing component itself is sufficient to provide comprehensive routing features in your application, we'll go ahead and install a few other components as well to make our life easier and enrich the existing core routing functionality.

To start with, we'll go ahead and install the HttpFoundation component, which provides an object-oriented wrapper for PHP global variables and response-related functions. It makes sure that you don't need to access global variables like $_GET, $_POST and the like directly.

$composer require symfony/http-foundation

Next, if you want to define your application routes in the YAML file instead of the PHP code, it's the YAML component that comes to the rescue as it helps you to convert YAML strings to PHP arrays and vice versa.

$composer require symfony/yaml

Finally, we'll install the Config component, which provides several utility classes to initialize and deal with configuration values defined in the different types of file like YAML, INI, XML, etc. In our case, we'll use it to load routes from the YAML file.

$composer require symfony/config

So that's the installation part, but how are you supposed to use it? In fact, it's just a matter of including the autoload.php file created by Composer in your application, as shown in the following snippet.

<?php require_once './vendor/autoload.php'; // application code ?>Set Up Basic Routes

In the previous section, we went through the installation of the necessary routing components. Now, you're ready to set up routing in your PHP application right away.

Let's go ahead and create the basic_routes.php file with the following contents.

<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { // Init basic route $foo_route = new Route( '/foo', array('controller' => 'FooController') ); // Init route with dynamic placeholders $foo_placeholder_route = new Route( '/foo/{id}', array('controller' => 'FooController', 'method'=>'load'), array('id' => '[0-9]+') ); // Add Route object(s) to RouteCollection object $routes = new RouteCollection(); $routes->add('foo_route', $foo_route); $routes->add('foo_placeholder_route', $foo_placeholder_route); // Init RequestContext object $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); // Init UrlMatcher object $matcher = new UrlMatcher($routes, $context); // Find the current route $parameters = $matcher->match($context->getPathInfo()); // How to generate a SEO URL $generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }

Setting up routing using the Symfony Routing component usually goes through a series of steps as listed below.

  • Initialize the Route object for each of your application routes.
  • Add all Route objects to the RouteCollection object.
  • Initialize the RequestContext object which holds the current request context information.
  • Initialize the UrlMatcher object by passing the RouteCollection object and the RequestContext object.
Initialize the Route Object for Different Routes

Let's go ahead and define a pretty basic foo route.

$foo_route = new Route( '/foo', array('controller' => 'FooController') );

The first argument of the Route constructor is the URI path, and the second argument is the array of custom attributes that you want to return when this particular route is matched. Typically, it would be a combination of the controller and method that you would like to call when this route is requested.

Next, let's have a look at the parameterized route.

$foo_placeholder_route = new Route( '/foo/{id}', array('controller' => 'FooController', 'method'=>'load'), array('id' => '[0-9]+') );

The above route can match URIs like foo/1, foo/123 and similar. Please note that we've restricted the {id} parameter to numeric values only, and hence it won't match URIs like foo/bar since the {id} parameter is provided as a string.

Add All Route Objects to the RouteCollection Object

The next step is to add route objects that we've initialized in the previous section to the RouteCollection object.

$routes = new RouteCollection(); $routes->add('foo_route', $foo_route); $routes->add('foo_placeholder_route', $foo_placeholder_route);

As you can see, it's pretty straightforward as you just need to use the add method of the RouteCollection object to add route objects. The first argument of the add method is the name of the route, and the second argument is the route object itself.

Initialize the RequestContext Object

Next, we need to initialize the RequestContext object, which holds the current request context information. We'll need this object when we initialize the UrlMatcher object as we'll go through it in a moment.

$context = new RequestContext(); $context->fromRequest(Request::createFromGlobals());Initialize the UrlMatcher Object

Finally, we need to initialize the UrlMatcher object along with routes and context information.

// Init UrlMatcher object $matcher = new UrlMatcher($routes, $context);

Now, we have everything we could match our routes against.

How to Match Routes

It's the match method of the UrlMatcher object which allows you to match any route against a set of predefined routes.

The match method takes the URI as its first argument and tries to match it against predefined routes. If the route is found, it returns custom attributes associated with that route. On the other hand, it throws the ResourceNotFoundException exception if there's no route associated with the current URI.

$parameters = $matcher->match($context->getPathInfo());

In our case, we've provided the current URI by fetching it from the $context object. So, if you're accessing the http://your-domain/basic_routes.php/foo URL, the $context->getPathInfo() returns foo, and we've already defined a route for the foo URI, so it should return us the following.

Array ( [controller] => FooController [_route] => foo_route )

Now, let's go ahead and test the parameterized route by accessing the http://your-domain/basic_routes.php/foo/123 URL.

Array ( [controller] => FooController [method] => load [id] => 123 [_route] => foo_placeholder_route )

It worked if you can see that the id parameter is bound with the appropriate value 123.

Next, let's try to access a non-existent route like http://your-domain/basic_routes.php/unknown-route, and you should see the following message.

No routes found for "/unknown-route".

So that's how you can find routes using the match method.

Apart from this, you could also use the Routing component to generate links in your application. Provided RouteCollection and RequestContext objects, the UrlGenerator allows you to build links for specific routes.

$generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, ));

The first argument of the generate method is the route name, and the second argument is the array that may contain parameters if it's the parameterized route. The above code should generate the /basic_routes.php/foo/123 URL.

Load Routes From the YAML File

In the previous section, we built our custom routes using the Route and RouteCollection objects. In fact, the Routing component offers different ways you could choose from to instantiate routes. You could choose from various loaders like YamlFileLoader, XmlFileLoader, and PhpFileLoader.

In this section, we'll go through the YamlFileLoader loader to see how to load routes from the YAML file.

The Routes YAML File

Go ahead and create the routes.yaml file with the following contents.

foo_route: path: /foo defaults: { controller: 'FooController::indexAction' } foo_placeholder_route: path: /foo/{id} defaults: { controller: 'FooController::loadAction' } requirements: id: '[0-9]+'An Example File

Next, go ahead and make the load_routes_from_yaml.php file with the following contents.

<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { // Load routes from the yaml file $fileLocator = new FileLocator(array(__DIR__)); $loader = new YamlFileLoader($fileLocator); $routes = $loader->load('routes.yaml'); // Init RequestContext object $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); // Init UrlMatcher object $matcher = new UrlMatcher($routes, $context); // Find the current route $parameters = $matcher->match($context->getPathInfo()); // How to generate a SEO URL $generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }

The only thing that's different in this case is the way we initialize routes!

$fileLocator = new FileLocator(array(__DIR__)); $loader = new YamlFileLoader($fileLocator); $routes = $loader->load('routes.yaml');

We've used the YamlFileLoader loader to load routes from the routes.yaml file instead of initializing it directly in the PHP itself. Apart from that, everything is the same and should produce the same results as that of the basic_routes.php file.

The All-in-One Router

Lastly in this section, we'll go through the Router class, which allows you to set up routing quickly with fewer lines of code.

Go ahead and make the all_in_one_router.php file with the following contents.

<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Router; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { $fileLocator = new FileLocator(array(__DIR__)); $requestContext = new RequestContext(); $requestContext->fromRequest(Request::createFromGlobals()); $router = new Router( new YamlFileLoader($fileLocator), 'routes.yaml', array('cache_dir' => __DIR__.'/cache'), $requestContext ); // Find the current route $parameters = $router->match($requestContext->getPathInfo()); // How to generate a SEO URL $routes = $router->getRouteCollection(); $generator = new UrlGenerator($routes, $requestContext); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }

Everything is pretty much the same, except that we've instantiated the Router object along with the necessary dependencies.

$router = new Router( new YamlFileLoader($fileLocator), 'routes.yaml', array('cache_dir' => __DIR__.'/cache'), $requestContext );

With that in place, you can straight away use the match method of the Router object for route mapping.

$parameters = $router->match($requestContext->getPathInfo());

Also, you will need to use the getRouteCollection method of the Router object to fetch routes.

$routes = $router->getRouteCollection();Conclusion

Go ahead and explore the other options available in the Routing component—I would love to hear your thoughts!

Today, we explored the Symfony Routing component, which makes implementation of routing in PHP applications a breeze. Along the way, we created a handful of examples to demonstrate various aspects of the Routing component. 

I hope that you've enjoyed this article, and feel free to post your thoughts using the feed below!

Categories: Web Design

Creating Pretty Popup Messages Using SweetAlert2

Sat, 06/30/2018 - 06:36

Every now and then, you will have to show an alert box to your users to let them know about an error or notification. The problem with the default alert boxes provided by browsers is that they are not very attractive. When you are creating a website with great color combinations and fancy animation to improve the browsing experience of your users, the unstyled alert boxes will seem out of place.

In this tutorial, you will learn about a library called SweetAlert2 that allows us to create all kinds of alert messages which can be customized to match the look and feel of our own website.

Display Simple Alert Messages

Before you can show all those sweet alert messages to your users, you will have to install the library and include it in your project. If you are using npm or bower, you can install it by running the following commands:

npm install sweetalert2 bower install sweetalert2

You can also get a CDN link for the latest version of the library and include it in your webpage using script tags:

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@7.12.15/dist/sweetalert2.all.min.js"></script>

Besides the JavaScript file, you will also have to load a CSS file which is used to style all the alert boxes created by the library:

<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/sweetalert2@7.12.15/dist/sweetalert2.min.css'>

Once you have installed the library, creating a sweet alert is actually very easy. All you have to do is call the swal() function. Just make sure that the function is called after the DOM has loaded.

There are two ways to create a sweet alert using the swal() function. You can either pass the title, body text and icon value in three different arguments or you can pass a single argument as an object with different values as its key-value pairs. Passing everything in an object is useful when you want to specify values for multiple arguments.

When a single argument is passed and it is a string, the sweet alert will only show a title and an OK button. Users will be able to click anywhere outside the alert or on the OK button in order to dismiss it.

When two arguments are passed, the first one becomes the title and the second one becomes the text inside the alert. You can also show an icon in the alert box by passing a third argument. This can have any of the five predefined values: warning, error, success, info, and question. If you don't pass the third argument, no icon will be shown inside the alert message.

document.querySelector(".first").addEventListener('click', function(){ swal("Our First Alert"); }); document.querySelector(".second").addEventListener('click', function(){ swal("Our First Alert", "With some body text!"); }); document.querySelector(".third").addEventListener('click', function(){ swal("Our First Alert", "With some body text and success icon!", "success"); });

Configuration Options to Customize Alerts

If you simply want to show some basic information inside an alert box, the previous example will do just fine. However, the library can actually do a lot more than just simply show users some text inside an alert message. You can change every aspect of these alert messages to suit your own needs.

We have already covered the title, the text, and the icons inside a sweet alert message. There is also an option to change the buttons inside it and control their behavior. By default, an alert will only have a single confirm button with text that says "OK". You can change the text inside the confirm button by setting the value of the confirmButtonText property. If you also want to show a cancel button in your alert messages, all you have to do is set the value of showCancelButton to true. The text inside the cancel button can be changed using the cancelButtonText property.

Each of these buttons can be given a different background color using the confirmButtonColor and cancelButtonColor properties. The default color for the confirm button is #3085d6, while the default color for the cancel button is #aaa. If you want to apply any other customization on the confirm or cancel buttons, you can simply use the confirmButtonClass and cancelButtonClass properties to add a new class to them. Once the classes have been added, you will be able to use CSS to change the appearance of those buttons. You can also add a class on the main modal itself by using the customClass property.

If you interacted with the alert messages in the first example, you might have noticed that the modals can be closed by pressing either the Enter or Escape key. Similarly, you can also click anywhere outside the modal in order to dismiss it. This happens because the value of allowEnterKey, allowEscapeKey, and allowOutsideClick is set to true by default.

When you show two different buttons inside a modal, the confirm button is the one which is in focus by default. You can remove the focus from the confirm button by setting the value of focusConfirm to false. Similarly, you can also set the focus on the cancel button by setting the value of focusCancel to true.

The confirm button is always shown on the left side by default. You have the option to reverse the positions of the confirm and cancel buttons by setting the value of reverseButtons to true.

Besides changing the position and color of buttons inside the alert messages, you can also change the background and position of the alert message or the backdrop around it. Not only that, but the library also allows you to show your own custom icons or images in the alert messages. This can be helpful in a lot of situations.

You can customize the backdrop of a sweet alert using the backdrop property. This property accepts either a Boolean or a string as its value. By default, the backdrop of an alert message consists of mostly transparent gray color. You can hide it completely by setting the value of backdrop to false. Similarly, you can also show your own images in the background by setting the backdrop value as a string. In such cases, the whole value of the backdrop string is assigned to the CSS background property. The background of a sweet alert message can be controlled using the background property. All alert messages have a completely white background by default.

All the alert messages pop up at the center of the window by default. However, you can make them pop up from a different location using the position property. This property can have nine different values with self-explanatory names: top, top-start, top-end, center, center-start, center-end, bottom, bottom-start, and bottom-end.

You can disable the animation when a modal pops up by setting the value of the animation property to false. The library also provides a timer property which can be used to auto-close the timer once a specific number of milliseconds have passed.

In the following example, I have used different combinations of all the properties discussed in this section to create four different alert messages. This should demonstrate how you can completely change the appearance and behavior of a modal created by the SweetAlert2 library.

document.querySelector(".first").addEventListener("click", function() { swal({ title: "Show Two Buttons Inside the Alert", showCancelButton: true, confirmButtonText: "Confirm", confirmButtonColor: "#00ff99", cancelButtonColor: "#ff0099" }); }); document.querySelector(".second").addEventListener("click", function() { swal({ title: "Are you sure about deleting this file?", type: "info", showCancelButton: true, confirmButtonText: "Delete It", confirmButtonColor: "#ff0055", cancelButtonColor: "#999999", reverseButtons: true, focusConfirm: false, focusCancel: true }); }); document.querySelector(".third").addEventListener("click", function() { swal({ title: "Profile Picture", text: "Do you want to make the above image your profile picture?", imageUrl: "https://images3.imgbox.com/4f/e6/wOhuryw6_o.jpg", imageWidth: 550, imageHeight: 225, imageAlt: "Eagle Image", showCancelButton: true, confirmButtonText: "Yes", cancelButtonText: "No", confirmButtonColor: "#00ff55", cancelButtonColor: "#999999", reverseButtons: true, }); }); document.querySelector(".fourth").addEventListener("click", function() { swal({ title: "Alert Set on Timer", text: "This alert will disappear after 3 seconds.", position: "bottom", backdrop: "linear-gradient(yellow, orange)", background: "white", allowOutsideClick: false, allowEscapeKey: false, allowEnterKey: false, showConfirmButton: false, showCancelButton: false, timer: 3000 }); });

Important SweetAlert2 Methods

Initializing different sweet alert messages to show them to users is one thing, but sometimes you will also need access to methods which control the behavior of those alert messages after initialization. Fortunately, the SweetAlert2 library provides many methods that can be used to show or hide a modal as well as get its title, text, image, etc.

You can check if a modal is visible or hidden using the isVisible() method. You can also programmatically close an open modal by using the close() or closeModal() methods. If you happen to use the same set of properties for multiple alert messages during their initialization, you can simply call the setDefaults({configurationObject}) method in the beginning to set the value of all those properties at once. The library also provides a resetDefaults() method to reset all the properties to their default values.

You can get the title, content, and image of a modal using the getTitle(), getContent(), and getImage() methods. Similarly, you can also get the HTML that makes up the confirm and cancel buttons using the getConfirmButton() and getCancelButton() methods.

There are a lot of other methods which can be used to perform other tasks like programmatically clicking on the confirm or cancel buttons.

Final Thoughts

The SweetAlert2 library makes it very easy for developers to create custom alert messages to show to their users by simply setting the values of a few properties. This tutorial was aimed at covering the basics of this library so that you can create your own custom alert messages quickly. 

To prevent the post from getting too big, I have only covered the most commonly used methods and properties. If you want to read about all the other methods and properties which can be used to create advanced alert messages, you should go through the detailed documentation of the library.

Don't forget to check out the other JavaScript resources we have available in the Envato Market, as well.

Feel free to let me know if there is anything that you would like me to clarify in this tutorial.

Categories: Web Design

Create Interactive Gradient Animations Using Granim.js

Thu, 06/28/2018 - 06:36

Gradients can instantly improve the look and feel of a website, if used carefully with the right color combination. CSS has also come a long way when it comes to applying a gradient on any element and animating it. In this tutorial, we will move away from CSS and create gradient animations using a JavaScript library called Granim.js.

This library draws and animates gradients on a given canvas according to the parameters you set when creating a Granim instance. There are different methods which can be used to make your gradient respond to different user events like a button click. In this tutorial, we will learn about this library in detail and create some simple but nice gradient animation effects.

Create Solid Color Gradient Animations

Before we begin creating any gradient, you will have to include the library in your project. For this, you can either download Granim.js from GitHub or link directly to a CDN. The library version that I am using in this tutorial is 1.1. Some methods that we will discuss here were only added in version 1.1, so using an older library version when following this tutorial will not always give the expected result. Keeping these points in mind, let's create our first gradient using Granim.js.

Every time you create a new Granim instance, you can pass it an object of key-value pairs, where the key is the name of a particular property and the value is the value of the property. The element property is used to specify the CSS selector or DOM node which will point to the canvas on which you want to apply a particular gradient.

When you create a gradient animation where the colors change from a relatively light value to a darker value, it might become impossible to read some text that you have positioned on the canvas. For example, the initial gradient applied on an element might be a combination of yellow and light green. In such cases, the text of the canvas would have to be darker for users to be able to read it properly. 

Similarly, the gradient might consist of dark red and black at some other point, and in such cases the dark text would not be easy to read. Granim.js solves this problem for you by allowing you to specify a container element on which you can add the dark and light classes to style the text or other elements accordingly. The value of the elToSetClassOn property is set to body by default, but you can also specify any other container element. The dark and light class names are updated automatically based on the average color of the gradient.

The elToSetClassOn property does not work by itself. You will also have to specify a name for the Granim instance that you created using the name property. If you set the name to something like first-gradient, the name of the classes applied on the container element will become first-gradient-light or first-gradient-dark based on how light or dark the gradient currently is. This way, any element which needs to change its color based on the lightness or darkness of the gradient will be able to do so with ease.

The direction in which a gradient should be drawn can be specified using the direction property. It has four valid values: diagonal, left-right, top-bottom, and radial. The gradients that you create will not move in those particular directions—they will just be drawn that way. The position of the gradient doesn't change during the animation; only its colors do.

There is also a states property, which accepts an object as its value. Each state specified inside the states object will have a name and a set of key-value pairs. You can use the gradients property to specify different colors which should make up a particular gradient. You can set the value of this property to be equal to an array of gradients. 

Granim.js will automatically create an animation where the colors of the gradient change from one set to another. The transition between different gradients takes 5,000 milliseconds by default. However, you can speed up or slow down the animation by setting an appropriate value for the transitionSpeed property.

After the gradients start animating, they will have to come to an end at one point or another. You can specify if the gradient should then just stop there or start animating again from the beginning using the loop property. This is set to true by default, which means that the gradient would keep animating.

Each color in a gradient can have a different opacity, which can be specified using the opacity property. This property accepts an array to determine how opaque each color is going to be. For two gradient colors, the value can be [0.1, 0.8]. For three gradient colors, the value can be [1, 0.5, 0.75], etc.

You also have the option to specify the time it takes for the gradient animation to go from one state to another using the stateTransitionSpeed. This is different from the transitionSpeed property, which controls the animation speed inside the same state.

In the following code snippet, I have created two different Granim instances to draw different gradients. In the first case, we have only specified a single gradient, so there is not any actual animation and the colors don't change at all.

var firstGranim = new Granim({ element: "#first", name: "first-gradient", direction: "diagonal", opacity: [1, 1], states: { "default-state": { gradients: [["#8BC34A", "#FF9800"]] } } }); var secondGranim = new Granim({ element: "#second", name: "second-gradient", elToSetClassOn: ".wrapper", direction: "top-bottom", opacity: [1, 1], states: { "default-state": { gradients: [["#9C27B0", "#E91E63"], ["#009688", "#8BC34A"]], transitionSpeed: 2000 } } });

Animate Gradients Over an Image

Another common use of the Granim.js library would be to animate a gradient over an image drawn on the canvas. You can specify different properties to control how the image is drawn on the canvas using the image property. It accepts an object with key-value pairs as its value. You can use the source property to specify the path from which the library should get the image to draw it on the canvas.

Any image that you draw on the canvas will be drawn so that its center coincides with the center of the canvas. However, you can use the position property to specify a different position to draw the image. This property accepts an array of two elements as its value. The first element can have the values left, center, and right. The second element can have the values top, center, and bottom. 

These properties are generally useful when you know that the size of the canvas and the image won't match. In these situations, you can use this property to specify the part of the image that should appear on the canvas.

If the images and the canvas have different dimensions, you can also stretch the image so that it fits properly inside the canvas. The stretchMode property also accepts an array of two elements as its value. Three valid values for both these elements are stretch, stretch-if-smaller, and stretch-if-larger.

A gradient with blend mode set to normal will completely hide the image underneath it. The only way to show an image below a gradient of solid colors would be to choose a different blend mode. You can read about all the possible blend mode values for a canvas on MDN.

I would like to point out that the ability to animate a gradient over an image was only added in version 1.1 of the Granim.js library. So you will have to use any version higher than that if you want this feature to work properly.

var firstGranim = new Granim({ element: "#first", name: "first-gradient", direction: "diagonal", opacity: [1, 1], image: { source: "path/to/rose_image.jpg", position: ["center", "center"], blendingMode: "lighten" }, states: { "default-state": { gradients: [["#8BC34A", "#FF9800"], ["#FF0000", "#000000"]] } } }); var secondGranim = new Granim({ element: "#second", name: "second-gradient", elToSetClassOn: ".wrapper", direction: "top-bottom", opacity: [1, 1], image: { source: "path/to/landscape.jpg", stretchMode: ["stretch", "stretch"], blendingMode: "overlay" }, states: { "default-state": { gradients: [["#9C27B0", "#E91E63"], ["#009688", "#8BC34A"]], transitionSpeed: 2000 } } });

Methods to Control Gradient Animation Playback

Up to this point, we did not have any control over the playback of the gradient animation once it was instantiated. We could not pause/play it or change its state, direction, etc. The Granim.js library has different methods which let you accomplish all these tasks with ease.

You can play or pause any animation using the play() and pause() methods. Similarly, you can change the state of the gradient animation using the changeState('state-name') method. The state-name here has to be one of the state names that you defined when instantiating the Granim instance.

More methods were added in version 1.1 which allow you to change the direction and blend mode of an animation on the fly using the changeDirection('direction-name') and changeBlendingMode('blending-mode-name') methods.

In the following code snippet, I am using a button click event to call all these methods, but you can use any other event to call them.

var firstGranim = new Granim({ element: "#first", name: "first-gradient", elToSetClassOn: ".wrapper", direction: "top-bottom", opacity: [1, 1], isPausedWhenNotInView: true, image : { source: 'path/to/landscape.jpg', stretchMode: ["stretch", "stretch"], blendingMode: 'overlay' }, states: { "default-state": { gradients: [["#9C27B0", "#E91E63"], ["#009688", "#8BC34A"]], transitionSpeed: 2000 }, "green-state": { gradients: [["#4CAF50", "#CDDC39"], ["#FFEB3B", "#8BC34A"]], transitionSpeed: 2000 }, "red-state": { gradients: [["#E91E63", "#FF5722"], ["#F44336", "#FF9800"]], transitionSpeed: 2000 } } }); $(".play").on("click", function(){ firstGranim.play(); }); $(".pause").on("click", function(){ firstGranim.pause(); }); $(".diagonal").on("click", function(){ firstGranim.changeDirection('diagonal'); }); $(".radial").on("click", function(){ firstGranim.changeDirection('radial'); }); $(".red-state").on("click", function(){ firstGranim.changeState('red-state'); }); $(".green-state").on("click", function(){ firstGranim.changeState('green-state'); }); $(".color-dodge").on("click", function(){ firstGranim.changeBlendingMode('color-dodge'); }); $(".color-burn").on("click", function(){ firstGranim.changeBlendingMode('color-burn'); }); $(".lighten").on("click", function(){ firstGranim.changeBlendingMode('lighten'); }); $(".darken").on("click", function(){ firstGranim.changeBlendingMode('darken'); });

Final Thoughts

In this tutorial, I have covered the basics of the Granim.js library so that you can get started with it as quickly as possible. There are a few other methods and properties that you might find useful when creating these gradient animations. You should read the official documentation in order to read about them all.

If you’re looking for additional JavaScript resources to study or to use in your work, check out what we have available in the Envato Market.

If you have any questions related to this tutorial, feel free to let me know in the comments.

Categories: Web Design

How to Build Complex, Large-Scale Vue.js Apps With Vuex

Tue, 06/12/2018 - 05:47

It's so easy to learn and use Vue.js that anyone can build a simple application with that framework. Even novices, with the help of Vue's documentation, can do the job. However, when complexity comes into play, things get a bit more serious. The truth is that multiple, deeply nested components with shared state can quickly turn your application into an unmaintainable mess.

The main problem in a complex application is how to manage the state between components without writing spaghetti code or producing side effects. In this tutorial you'll learn how to solve that problem by using Vuex: a state management library for building complex Vue.js applications.

What Is Vuex?

Vuex is a state management library specifically tuned for building complex, large-scale Vue.js applications. It uses a global, centralized store for all the components in an application, taking advantage of its reactivity system for instant updates.

The Vuex store is designed in such a way that it is not possible to change its state from any component. This ensures that the state can only be mutated in a predictable manner. Thus your store becomes a single source of truth: every data element is only stored once and is read-only to prevent the application's components from corrupting the state that is accessed by other components.

Why Do You Need Vuex?

You may ask: Why do I need Vuex in the first place? Can't I just put the shared state in a regular JavaScript file and import it into my Vue.js application?

You can, of course, but compared to a plain global object, the Vuex store has some significant advantages and benefits:

  • The Vuex store is reactive. Once components retrieve a state from it, they will reactively update their views every time the state changes.
  • Components cannot directly mutate the store's state. The only way to change the store's state is by explicitly committing mutations. This ensures every state change leaves a trackable record, which makes the application easier to debug and test.
  • You can easily debug your application thanks to the Vuex integration with Vue's DevTools extension.
  • The Vuex store gives you a bird's eye view of how everything is connected and affected in your application.
  • It's easier to maintain and synchronize the state between multiple components, even if the component hierarchy changes.
  • Vuex makes direct cross-component communication possible.
  • If a component is destroyed, the state in the Vuex store will remain intact.
Getting Started With Vuex

Before we get started, I want to make several things clear. 

First, to follow this tutorial, you need to have a good understanding of Vue.js and its components system, or at least minimal experience with the framework. 

Also, the aim of this tutorial is not to show you how to build an actual complex application; the aim is to focus your attention more on Vuex concepts and how you can use them to build complex applications. For that reason, I'm going to use very plain and simple examples, without any redundant code. Once you fully grasp the Vuex concepts, you will be able to apply them on any level of complexity.

Finally, I'll be using ES2015 syntax. If you are not familiar with it, you can learn it here.

And now, let's get started!

Setting Up a Vuex Project

The first step to get started with Vuex is to have Vue.js and Vuex installed on your machine. There are several ways to do that, but we'll use the easiest one. Just create an HTML file and add the necessary CDN links:

<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <!-- Put the CSS code here --> </head> <body> <!-- Put the HTML template code here --> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> <script> // Put the Vue code here </script> </body> </html>

I used some CSS to make the components look nicer, but you don't need to worry about that CSS code. It only helps you to gain a visual notion about what is going on. Just copy and paste the following inside the <head> tag:

<style> #app { background-color: yellow; padding: 10px; } #parent { background-color: green; width: 400px; height: 300px; position: relative; padding-left: 5px; } h1 { margin-top: 0; } .child { width: 150px; height: 150px; position:absolute; top: 60px; padding: 0 5px 5px; } .childA { background-color: red; left: 20px; } .childB { background-color: blue; left: 190px; } </style>

Now, let's create some components to work with. Inside the <script> tag, right above the closing </body> tag, put the following Vue code:

Vue.component('ChildB',{ template:` <div class="child childB"> <h1> Score: </h1> </div>` }) Vue.component('ChildA',{ template:` <div class="child childA"> <h1> Score: </h1> </div>` }) Vue.component('Parent',{ template:` <div id="parent"> <childA/> <childB/> <h1> Score: </h1> </div>` }) new Vue ({ el: '#app' })

Here, we have a Vue instance, a parent component, and two child components. Each component has a heading "Score:" where we'll output the app state.

The last thing you need to do is to put a wrapping <div> with id="app" right after the opening <body>, and then place the parent component inside:

<div id="app"> <parent/> </div>

The preparation work is now done, and we're ready to move on.

Exploring VuexState Management

In real life, we deal with complexity by using strategies to organize and structure the content we want to use. We group related things together in different sections, categories, etc. It's like a book library, in which the books are categorized and put in different sections so that we can easily find what we are looking for. Vuex arranges the application data and logic related to state in four groups or categories: state, getters, mutations, and actions.

State and mutations are the base for any Vuex store:

  • state is an object that holds the state of the application data.
  • mutations is also an object containing methods which affect the state.

Getters and actions are like logical projections of state and mutations:

  • getters contain methods used to abstract the access to the state, and to do some preprocessing jobs, if needed (data calculating, filtering, etc.).
  • actions are methods used to trigger mutations and execute asynchronous code.

Let's explore the following diagram to make things a bit clearer:

On the left side, we have an example of a Vuex store, which we'll create later on in this tutorial. On the right side, we have a Vuex workflow diagram, which shows how the different Vuex elements work together and communicate with each other.

In order to change the state, a particular Vue component must commit mutations (e.g. this.$store.commit('increment', 3)), and then, those mutations change the state (score becomes 3). After that, the getters are automatically updated thanks to Vue's reactive system, and they render the updates in the component's view (with this.$store.getters.score). 

Mutations cannot execute asynchronous code, because this would make it impossible to record and track the changes in debug tools like Vue DevTools. To use asynchronous logic, you need to put it in actions. In this case, a component will first dispatch actions (this.$store.dispatch('incrementScore', 3000)) where the asynchronous code is executed, and then those actions will commit mutations, which will mutate the state. 

Create a Vuex Store Skeleton

Now that we've explored how Vuex works, let's create the skeleton for our Vuex store. Put the following code above the ChildB component registration:

const store = new Vuex.Store({ state: { }, getters: { }, mutations: { }, actions: { } })

To provide global access to the Vuex store from every component, we need to add the store property in the Vue instance:

new Vue ({ el: '#app', store // register the Vuex store globally })

Now, we can access the store from every component with the this.$store variable.

So far, if you open the project with CodePen in the browser, you should see the following result.

State Properties

The state object contains all of the shared data in your application. Of course, if needed, each component can have its own private state too.

Imagine that you want to build a game application, and you need a variable to store the game's score. So you put it in the state object:

state: { score: 0 }

Now, you can access the state's score directly. Let's go back to the components and reuse the data from the store. In order to be able to reuse reactive data from the store's state, you should use computed properties. So let's create a score() computed property in the parent component:

computed: { score () { return this.$store.state.score } }

In the parent component's template, put the {{ score }} expression:

<h1> Score: {{ score }} </h1>

And now, do the same for the two child components.

Vuex is so smart that it will do all the work for us to reactively update the score property whenever the state changes. Try to change the score's value and see how the result updates in all three components.

Creating Getters

It is, of course, good that you can reuse the this.$store.state keyword inside the components, as you saw above. But imagine the following scenarios:

  1. In a large-scale application, where multiple components access the state of the store by using this.$store.state.score, you decide to change the name of score. This means that you have to change the name of the variable inside each and every component that uses it! 
  2. You want to use a computed value of the state. For example, let's say you want to give players a bonus of 10 points when the score reaches 100 points. So, when the score hits 100 points, 10 points bonus are added. This means each component has to contain a function that reuses the score and increments it by 10. You will have repeated code in each component, which is not good at all!

Fortunately, Vuex offers a working solution to handle such situations. Imagine the centralized getter that accesses the store's state and provides a getter function to each of the state's items. If needed, this getter can apply some computation to the state's item. And if you need to change the names of some of the state's properties, you only change them in one place, in this getter. 

Let's create a score() getter:

getters: { score (state){ return state.score } }

A getter receives the state as its first argument, and then uses it to access the state's properties.

Note: Getters also receive getters as the second argument. You can use it to access the other getters in the store.

In all components, modify the score() computed property to use the score() getter instead of the state's score directly.

computed: { score () { return this.$store.getters.score } }

Now, if you decide to change the score to result, you need to update it only in one place: in the score() getter. Try it out in this CodePen!

Creating Mutations

Mutations are the only permissible way to change the state. Triggering changes simply means committing mutations in component methods.

A mutation is pretty much an event handler function that is defined by name. Mutation handler functions receive a state as a first argument. You can pass an additional second argument too, which is called the payload for the mutation. 

Let's create an increment() mutation:

mutations: { increment (state, step) { state.score += step } }

Mutations cannot be called directly! To perform a mutation, you should call the commit() method with the name of the corresponding mutation and possible additional parameters. It might be just one, like the step in our case, or there might be multiple ones wrapped in an object.

Let's use the increment() mutation in the two child components by creating a method named changeScore():

methods: { changeScore (){ this.$store.commit('increment', 3); } }

We are committing a mutation instead of changing this.$store.state.score directly, because we want to explicitly track the change made by the mutation. This way, we make our application logic more transparent, traceable, and easy to reason about. In addition, it makes it possible to implement tools, like Vue DevTools or Vuetron, that can log all mutations, take state snapshots, and perform time-travel debugging.

Now, let's put the changeScore() method into use. In each template of the two child components, create a button and add a click event listener to it:

<button @click="changeScore">Change Score</button>

When you click the button, the state will be incremented by 3, and this change will be reflected in all components. Now we have effectively achieved direct cross-component communication, which is not possible with the Vue.js built-in "props down, events up" mechanism. Check it out in our CodePen example.

Creating Actions

An action is just a function that commits a mutation. It changes the state indirectly, which allows for the execution of asynchronous operations. 

Let's create an incrementScore() action:

actions: { incrementScore: ({ commit }, delay) => { setTimeout(() => { commit('increment', 3) }, delay) } }

Actions get the context as the first parameter, which contains all methods and properties from the store. Usually, we just extract the parts we need by using ES2015 argument destructuring. The commit method is one we need very often. Actions also get a second payload argument, just like mutations.

In the ChildB component, modify the changeScore() method:

methods: { changeScore (){ this.$store.dispatch('incrementScore', 3000); } }

To call an action, we use the dispatch() method with the name of the corresponding action and additional parameters, just as with mutations.

Now, the Change Score button from the ChildA component will increment the score by 3. The identical button from the ChildB component will do the same, but after a delay of 3 seconds. In the first case, we're executing synchronous code and we use a mutation, but in the second case we're executing asynchronous code, and we need to use an action instead. See how it all works in our CodePen example.

Vuex Mapping Helpers

Vuex offers some useful helpers which can streamline the process of creating state, getters, mutations, and actions. Instead of writing those functions manually, we can tell Vuex to create them for us. Let's see how it works.

Instead of writing the score() computed property like this:

computed: { score () { return this.$store.state.score } }

We just use the mapState() helper like this:

computed: { ...Vuex.mapState(['score']) }

And the score() property is created automatically for us.

The same is true for the getters, mutations, and actions. 

To create the score() getter, we use the mapGetters() helper:

computed: { ...Vuex.mapGetters(['score']) }

To create the changeScore() method, we use the mapMutations() helper like this:

methods: { ...Vuex.mapMutations({changeScore: 'increment'}) }

When used for mutations and actions with the payload argument, we must pass that argument in the template where we define the event handler:

<button @click="changeScore(3)">Change Score</button>

If we want changeScore() to use an action instead of a mutation, we use mapActions() like this:

methods: { ...Vuex.mapActions({changeScore: 'incrementScore'}) }

Again, we must define the delay in the event handler:

<button @click="changeScore(3000)">Change Score</button>

Note: All mapping helpers return an object. So, if we want to use them in combination with other local computed properties or methods, we need to merge them into one object. Fortunately, with the object spread operator (...), we can do it without using any utility. 

In our CodePen, you can see an example of how all mapping helpers are used in practice.

Making the Store More Modular

It seems that the problem with complexity constantly obstructs our way. We solved it before by creating the Vuex store, where we made the state management and component communication easy. In that store, we have everything in one place, easy to manipulate and easy to reason about. 

However, as our application grows, this easy-to-manage store file becomes larger and larger and, as a result, harder to maintain. Again, we need some strategies and techniques for improving the application structure by returning it to its easy-to-maintain form. In this section, we'll explore several techniques which can help us in this undertaking.

Using Vuex Modules

Vuex allows us to split the store object into separate modules. Each module can contain its own state, mutations, actions, getters, and other nested modules. After we've created the necessary modules, we register them in the store.

Let's see it in action:

const childB = { state: { result: 3 }, getters: { result (state) { return state.result } }, mutations: { increase (state, step) { state.result += step } }, actions: { increaseResult: ({ commit }, delay) => { setTimeout(() => { commit('increase', 6) }, delay) } } } const childA = { state: { score: 0 }, getters: { score (state) { return state.score } }, mutations: { increment (state, step) { state.score += step } }, actions: { incrementScore: ({ commit }, delay) => { setTimeout(() => { commit('increment', 3) }, delay) } } } const store = new Vuex.Store({ modules: { scoreBoard: childA, resultBoard: childB } })

In the above example, we created two modules, one for each child component. The modules are just plain objects, which we register as scoreBoard and resultBoard in the modules object inside the store. The code for childA is the same as that in the store from the previous examples. In the code for childB, we add some changes in values and names.

Let's now tweak the ChildB component to reflect the changes in the resultBoard module. 

Vue.component('ChildB',{ template:` <div class="child childB"> <h1> Result: {{ result }} </h1> <button @click="changeResult()">Change Result</button> </div>`, computed: { result () { return this.$store.getters.result } }, methods: { changeResult () { this.$store.dispatch('increaseResult', 3000); } } })

In the ChildA component, the only thing we need to modify is the changeScore() method:

Vue.component('ChildA',{ template:` <div class="child childA"> <h1> Score: {{ score }} </h1> <button @click="changeScore()">Change Score</button> </div>`, computed: { score () { return this.$store.getters.score } }, methods: { changeScore () { this.$store.dispatch('incrementScore', 3000); } } })

As you can see, splitting the store into modules makes it much more lightweight and maintainable, while still keeping its great functionality. Check out the updated CodePen to see it in action.

Namespaced Modules

If you want or need to use one and the same name for a particular property or method in your modules, then you should consider namespacing them. Otherwise you may observe some strange side effects, such as executing all the actions with the same names, or getting the wrong state's values. 

To namespace a Vuex module, you just set the namespaced property to true.

const childB = { namespaced: true, state: { score: 3 }, getters: { score (state) { return state.score } }, mutations: { increment (state, step) { state.score += step } }, actions: { incrementScore: ({ commit }, delay) => { setTimeout(() => { commit('increment', 6) }, delay) } } } const childA = { namespaced: true, state: { score: 0 }, getters: { score (state) { return state.score } }, mutations: { increment (state, step) { state.score += step } }, actions: { incrementScore: ({ commit }, delay) => { setTimeout(() => { commit('increment', 3) }, delay) } } }

In the above example, we made the property and method names the same for the two modules. And now we can use a property or method prefixed with the name of the module. For example, if we want to use the score() getter from the resultBoard module, we type it like this: resultBoard/score. If we want the score() getter from the scoreBoard module, then we type it like this: scoreBoard/score. 

Let's now modify our components to reflect the changes we made. 

Vue.component('ChildB',{ template:` <div class="child childB"> <h1> Result: {{ result }} </h1> <button @click="changeResult()">Change Result</button> </div>`, computed: { result () { return this.$store.getters['resultBoard/score'] } }, methods: { changeResult () { this.$store.dispatch('resultBoard/incrementScore', 3000); } } }) Vue.component('ChildA',{ template:` <div class="child childA"> <h1> Score: {{ score }} </h1> <button @click="changeScore()">Change Score</button> </div>`, computed: { score () { return this.$store.getters['scoreBoard/score'] } }, methods: { changeScore () { this.$store.dispatch('scoreBoard/incrementScore', 3000); } } })

As you can see in our CodePen example, we can now use the method or property we want and get the result we expect.

Splitting the Vuex Store Into Separate Files

In the previous section, we improved the application structure to some extent by separating the store into modules. We made the store cleaner and more organized, but still all of the store code and its modules lie in the same big file. 

So the next logical step is to split the Vuex store into separate files. The idea is to have an individual file for the store itself and one for each of its objects, including the modules. This means having separate files for the state, getters, mutations, actions, and for each individual module (store.js, state.js, getters.js, etc.) You can see an example of this structure at the end of the next section.

Using Vue Single File Components

We've made the Vuex store as modular as we can. The next thing we can do is to apply the same strategy to the Vue.js components too. We can put each component in a single, self-contained file with a .vue extension. To learn how this works, you can visit the Vue Single File Components documentation page

So, in our case, we'll have three files: Parent.vue, ChildA.vue, and ChildB.vue. 

Finally, if we combine all three techniques, we'll end up with the following or similar structure:

├── index.html └── src ├── main.js ├── App.vue ├── components │ ├── Parent.vue │ ├── ChildA.vue │ ├── ChildB.vue └── store ├── store.js ├── state.js ├── getters.js ├── mutations.js ├── actions.js └── modules ├── childA.js └── childB.js

In our tutorial GitHub repo, you can see the completed project with the above structure.

Recap

Let's recap some main points you need to remember about Vuex:

Vuex is a state management library that helps us to build complex, large-scale applications. It uses a global, centralized store for all the components in an application. To abstract the state, we use getters. Getters are pretty much like computed properties and are an ideal solution when we need to filter or calculate something on runtime.

The Vuex store is reactive, and components cannot directly mutate the store's state. The only way to mutate the state is by committing mutations, which are synchronous transactions. Each mutation should perform only one action, must be as simple as possible, and is only responsible for updating a piece of the state.

Asynchronous logic should be encapsulated in actions. Each action can commit one or more mutations, and one mutation can be committed by more than one action. Actions can be complex, but they never change the state directly.

Finally, modularity is the key to maintainability. To deal with complexity and make our code modular, we use the "divide and conquer" principle and the code splitting technique.

Conclusion

That's it! You already know the main concepts behind Vuex, and you are ready to start applying them in practice.  

For the sake of brevity and simplicity, I intentionally omitted some details and features of Vuex, so you'll need to read the full Vuex documentation to learn everything about Vuex and its feature set.

Categories: Web Design

Creating Stylish and Responsive Progress Bars Using ProgressBar.js

Mon, 06/11/2018 - 06:34

Nothing on the web happens instantly. The only difference is in the time it takes for a process to complete. Some processes can happen in a few milliseconds, while others can take up to several seconds or minutes. For example, you might be editing a very large image uploaded by your users, and this process can take some time. In such cases, it is a good idea to let the visitors know that the website is not stuck somewhere but it is actually working on your image and making some progress.

One of the most common ways to show readers how much a process has progressed is to use progress bars. In this tutorial, you will learn how to use the ProgressBar.js library to create different progress bars with simple and complex shapes.

Creating a Basic Progress Bar

Once you have included the library in your project, creating a progress bar using this library is easy. ProgressBar.js is supported in all major browsers, including IE9+, which means that you can use it in any website you are creating with confidence. You can get the latest version of the library from GitHub or directly use a CDN link to add it in your project.

To avoid any unexpected behavior, please make sure that the container of the progress bar has the same aspect ratio as the progress bar. In the case of a circle, the aspect ratio of the container should be 1:1 because the width will be equal to the height. In the case of a semicircle, the aspect ratio of the container should be 2:1 because the width will be double the height. Similarly, in the case of a simple line, the container should have an aspect ratio of 100:strokeWidth for the line.

When creating progress bars with a line, circle, or semicircle, you can simply use the ProgressBar.Shape() method to create the progress bar. In this case, the Shape can be a Circle, Line, or SemiCircle. You can pass two parameters to the Shape() method. The first parameter is a selector or DOM node to identify the container of the progress bar. The second parameter is an object with key-value pairs which determine the appearance of the progress bar.

You can specify the color of the progress bar using the color property. Any progress bar that you create will have a dark gray color by default. The thickness of the progress bar can be specified using the strokeWidth property. You should keep in mind that the width here is not in pixels but in terms of a percentage of the canvas size. For instance, if the canvas is 200px wide, a strokeWidth value of 5 will create a line which is 10px thick.

Besides the main progress bar, the library also allows you to draw a trailing line which will show readers the path on which the progress bar will move. The color of the trail line can be specified using the trailColor property, and its width can be specified using the trailWidth property. Just like strokeWidth, the trailWidth property also computes the width in percentage terms.

The total time taken by the progress bar to go from its initial state to its final state can be specified using the duration property. By default, a progress bar will complete its animation in 800 milliseconds.

You can use the easing property to specify how a progress bar should move during the animation. All progress bars will move with a linear speed by default. To make the animation more appealing, you can set this value to something else like easeIn, easeOut, easeInOut, or bounce.

After specifying the initial parameter values, you can animate the progress bars using the animate() method. This parameter accepts three parameters. The first parameter is the amount up to which you want to animate the progress line. The two other parameters are optional. The second parameter can be used to override any animation property values that you set during initialization. The third parameter is a callback function to do something else once the animation ends.

In the following example, I have created three different progress bars using all the properties we have discussed so far.

var lineBar = new ProgressBar.Line('#line-container', { color: 'orange', strokeWidth: 2, trailWidth: 0.5 }); lineBar.animate(1, { duration: 1000 }); var circleBar = new ProgressBar.Circle('#circle-container', { color: 'white', strokeWidth: 2, trailWidth: 10, trailColor: 'black', easing: 'easeInOut' }); circleBar.animate(0.75, { duration: 1500 }); var semiBar = new ProgressBar.SemiCircle('#semi-container', { color: 'violet', strokeWidth: 2, trailWidth: 0.5, easing: 'bounce' }); semiBar.animate(1, { duration: 3000 });

Animating Text Values With the Progress Bar

The only thing that changes with the animation of the progress bars in the above example is their length. However, ProgressBar.js also allows you to change other physical attributes like the width and color of the stroking line. In such cases, you will have to specify the initial values for the progress bar inside the from parameter and the final values inside the to parameter when initializing the progress bars.

You can also tell the library to create an accompanying text element with the progress bar to show some textual information to your users. The text can be anything from a static value to a numerical value indicating the progress of the animation. The text parameter will accept an object as its value. 

This object can have a value parameter to specify the initial text to be shown inside the element. You can also provide a class name to be added to the text element using the className parameter. If you want to apply some inline styles to the text element, you can specify them all as a value of the style parameter. All the default styles can be removed by setting the value of style to null. It is important to remember that the default values only apply if you have not set a custom value for any CSS property inside style.

The value inside the text element will stay the same during the whole animation if you don't update it yourself. Luckily, ProgressBar.js also provides a step parameter which can be used to define a function to be called with each animation step. Since this function will be called multiple times each second, you need to be careful with its use and keep the calculations inside it simple.

var lineBar = new ProgressBar.Line("#line-container", { strokeWidth: 4, trailWidth: 0.5, from: { color: "#FF9900" }, to: { color: "#00FF99" }, text: { value: '0', className: 'progress-text', style: { color: 'black', position: 'absolute', top: '-30px', padding: 0, margin: 0, transform: null } }, step: (state, shape) => { shape.path.setAttribute("stroke", state.color); shape.setText(Math.round(shape.value() * 100) + ' %'); } }); lineBar.animate(1, { duration: 4000 }); var circleBar = new ProgressBar.Circle("#circle-container", { color: "white", strokeWidth: 2, trailWidth: 25, trailColor: "black", easing: "easeInOut", from: { color: "#FF9900", width: 1 }, to: { color: "#FF0099", width: 25 }, text: { value: '0', className: 'progress-text', style: { color: 'black', position: 'absolute', top: '45%', left: '42%', padding: 0, margin: 0, transform: null } }, step: (state, shape) => { shape.path.setAttribute("stroke", state.color); shape.path.setAttribute("stroke-width", state.width); shape.setText(Math.round(shape.value() * 100) + ' %'); } }); circleBar.animate(0.75, { duration: 1500 }); var semiBar = new ProgressBar.SemiCircle("#semi-container", { color: "violet", strokeWidth: 2, trailWidth: 8, trailColor: "black", easing: "bounce", from: { color: "#FF0099", width: 1 }, to: { color: "#FF9900", width: 2 }, text: { value: '0', className: 'progress-text', style: { color: 'black', position: 'absolute', top: '45%', left: '50%', padding: 0, margin: 0, transform: null } }, step: (state, shape) => { shape.path.setAttribute("stroke", state.color); shape.path.setAttribute("stroke-width", state.width); shape.setText(Math.round(shape.value() * 100) + ' %'); } }); semiBar.animate(0.75, { duration: 2000 });

Creating Progress Bars With Custom Shapes

Sometimes, you might want to create progress bars with different shapes that match the overall theme of your website. ProgressBar.js allows you to create progress bars with custom shapes using the Path() method. This method works like Shape() but provides fewer parameters to customize the progress bar animation. You can still provide a duration and easing value for the animation. If you want to animate the color and width of the stroke used for drawing the custom path, you can do so inside the from and to parameters.

The library does not provide any way to draw a trail for the custom path, as it did for simple lines and circles. However, you can create the trail yourself fairly easily. In the following example, I have created a triangular progress bar using the Path() method.

Before writing the JavaScript code, we will have to define our custom SVG path in HTML. Here is the code I used to create a simple triangle:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" width="300" height="300" viewBox="0 0 300 300"> <path d="M 50 50 L 200 100 L 200 300 z" fill="none" stroke="#ddd" stroke-width="1"/> <path id="triangle" d="M 50 50 L 200 100 L 200 300 z" fill="none" stroke="blue" stroke-width="5"/> </svg>

You might have noticed that I created two different path elements. The first path has a light gray color which acts like the trail we saw with simple progress bars in the previous section. The second path is the one that we animate with our code. We have given it an id which is used to identify it in the JavaScript code below.

var path = new ProgressBar.Path("#triangle", { duration: 6000, from: { color: "#ff0000", width: 2 }, to: { color: "#0099ff", width: 10 }, strokeWidth: 4, easing: "easeInOut", step: (state, shape) => { shape.path.setAttribute("stroke", state.color); shape.path.setAttribute("stroke-width", state.width); } }); path.animate(1);

Final Thoughts

As you saw in this tutorial, ProgressBar.js allows you to easily create different kinds of progress bars with ease. It also gives you the option to animate different attributes of the progress bar like its width and color. 

Not only that, but you can also use this library to change the value of an accompanying text element in order to show the progress in textual form. This tutorial covers everything that you need to know to create simple progress bars. However, you can go through the documentation to learn more about the library.

If there is anything that you would like me to clarify in this tutorial, feel free to let me know in the comments.

Categories: Web Design

Set Up an OAuth2 Server Using Passport in Laravel

Fri, 06/08/2018 - 06:30

In this article, we’re going to explore how you could set up a fully fledged OAuth2 server in Laravel using the Laravel Passport library. We’ll go through the necessary server configurations along with a real-world example to demonstrate how you could consume OAuth2 APIs.

I assume that you’re familiar with the basic OAuth2 concepts and flow as we’re going to discuss them in the context of Laravel. In fact, the Laravel Passport library makes it pretty easy to quickly set up an OAuth2 server in your application. Thus, other third-party applications are able to consume APIs provided by your application.

In the first half of the article, we’ll install and configure the necessary libraries, and the second half goes through how to set up demo resources in your application and consume them from third-party applications.

Server Configurations

In this section, we're going to install the dependencies that are required in order to make the Passport library work with Laravel. After installation, there's quite a bit of configuration that we'll need to go through so that Laravel can detect the Passport library.

Let's go ahead and install the Passport library using composer.

$composer require laravel/passport

That's pretty much it as far as the Passport library installation is concerned. Now let's make sure that Laravel knows about it.

Working with Laravel, you're probably aware of the concept of a service provider that allows you to configure services in your application. Thus, whenever you want to enable a new service in your Laravel application, you just need to add an associated service provider entry in the config/app.php.

If you're not aware of Laravel service providers yet, I would strongly recommend that you do yourself a favor and go through this introductory article that explains the basics of service providers in Laravel.

In our case, we just need to add the PassportServiceProvider provider to the list of service providers in config/app.php as shown in the following snippet.

... ... 'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ Laravel\Tinker\TinkerServiceProvider::class, /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, Laravel\Passport\PassportServiceProvider::class, ], ... ...

Next, we need to run the migrate artisan command, which creates the necessary tables in a database for the Passport library.

$php artisan migrate

To be precise, it creates following the tables in the database.

oauth_access_tokens oauth_auth_codes oauth_clients oauth_personal_access_clients oauth_refresh_tokens

Next, we need to generate a pair of public and private keys that will be used by the Passport library for encryption. As expected, the Passport library provides an artisan command to create it easily.

$php artisan passport:install

That should have created keys at storage/oauth-public.key and storage/oauth-private.key. It also creates some demo client credentials that we'll get back to later.

Moving ahead, let's oauthify the existing User model class that Laravel uses for authentication. To do that, we need to add the HasApiTokens trait to the User model class. Let's do that as shown in the following snippet.

<?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use HasApiTokens; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; }

The HasApiTokens trait contains helper methods that are used to validate tokens in the request and check the scope of resources being requested in the context of the currently authenticated user.

Further, we need to register the routes provided by the Passport library with our Laravel application. These routes will be used for standard OAuth2 operations like authorization, requesting access tokens, and the like.

In the boot method of the app/Providers/AuthServiceProvider.php file, let's register the routes of the Passport library.

... ... /** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Passport::routes(); } ... ...

Last but not least, we need to change the api driver from token to passport in the config/auth.php file, as we're going to use the Passport library for the API authentication.

'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],

So far, we've done everything that's required as far as the OAuth2 server configuration is concerned.

Set Up the Demo Resources

In the previous section, we did all the hard work to set up the OAuth2 authentication server in our application. In this section, we'll set up a demo resource that could be requested over the API call.

We will try to keep things simple. Our demo resource returns the user information provided that there's a valid uid parameter present in the GET request.

Let's create a controller file app/Http/Controllers/UserController.php with the following contents.

<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use App\User; class UserController extends Controller { public function get(Request $request) { $user_id = $request->get("uid", 0); $user = User::find($user_id); return $user; } }

As usual, you need to add an associated route as well, which you are supposed to add in the routes/web.php file. But what we are talking about is the API route, and thus it needs special treatment.

The API routes are defined in the routes/api.php file. So, let's go ahead and add our custom API route as shown in the following snippet.

<?php use Illuminate\Http\Request; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); // custom API route Route::middleware('auth:api')->get('/user/get', 'UserController@get');

Although we've defined it as /user/get, the effective API route is /api/user/get, and that's what you should use when you request a resource over that route. The api prefix is automatically handled by Laravel, and you don't need to worry about that!

In the next and last section, we'll discuss how you could create client credentials and consume the OAuth2 API.

How to Consume OAuth2 APIs

Now that we've set up the OAuth2 server in our application, any third party can connect to our server with OAuth and consume the APIs available in our application.

First of all, third-party applications must register with our application in order to be able to consume APIs. In other words, they are considered as client applications, and they will receive a client id and client secret upon registration.

The Passport library provides an artisan command to create client accounts without much hassle. Let's go ahead and create a demo client account.

$php artisan passport:client Which user ID should the client be assigned to?: > 1 What should we name the client?: > Demo OAuth2 Client Account Where should we redirect the request after authorization? [http://localhost/auth/callback]: > http://localhost/oauth2_client/callback.php New client created successfully. Client ID: 1 Client secret: zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01

When you run the artisan passport:client command, it asks you a few questions before creating the client account. Out of those, there's an important one that asks you the callback URL.

The callback URL is the one where users will be redirected back to the third-party end after authorization. And that's where the authorization code that is supposed to be used in exchange for the access token will be sent. We are about to create that file in a moment.

Now, we're ready to test OAuth2 APIs in the Laravel application.

For demonstration purposes, I'll create the oauth2_client directory under the document root in the first place. Ideally, these files will be located at the third-party end that wants to consume APIs in our Laravel application.

Let's create the oauth2_client/auth_redirection.php file with the following contents.

<?php $query = http_build_query(array( 'client_id' => '1', 'redirect_uri' => 'http://localhost/oauth2_client/callback.php', 'response_type' => 'code', 'scope' => '', )); header('Location: http://your-laravel-site-url/oauth/authorize?'.$query);

Make sure to change the client_id and redirect_uri parameters to reflect your own settings—the ones that you used while creating the demo client account.

Next, let's create the oauth2_client/callback.php file with the following contents.

<?php // check if the response includes authorization_code if (isset($_REQUEST['code']) && $_REQUEST['code']) { $ch = curl_init(); $url = 'http://your-laravel-site-url/oauth/token'; $params = array( 'grant_type' => 'authorization_code', 'client_id' => '1', 'client_secret' => 'zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01', 'redirect_uri' => 'http://localhost/oauth2_client/callback.php', 'code' => $_REQUEST['code'] ); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $params_string = ''; if (is_array($params) && count($params)) { foreach($params as $key=>$value) { $params_string .= $key.'='.$value.'&'; } rtrim($params_string, '&'); curl_setopt($ch,CURLOPT_POST, count($params)); curl_setopt($ch,CURLOPT_POSTFIELDS, $params_string); } $result = curl_exec($ch); curl_close($ch); $response = json_decode($result); // check if the response includes access_token if (isset($response->access_token) && $response->access_token) { // you would like to store the access_token in the session though... $access_token = $response->access_token; // use above token to make further api calls in this session or until the access token expires $ch = curl_init(); $url = 'http://your-laravel-site-url/api/user/get'; $header = array( 'Authorization: Bearer '. $access_token ); $query = http_build_query(array('uid' => '1')); curl_setopt($ch,CURLOPT_URL, $url . '?' . $query); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $result = curl_exec($ch); curl_close($ch); $response = json_decode($result); var_dump($result); } else { // for some reason, the access_token was not available // debugging goes here } }

Again, make sure to adjust the URLs and client credentials according to your setup in the above file.

How It Works Altogether

In this section, we'll test it altogether from the perspective of an end user. As an end user, there are two applications in front of you:

  1. The first one is the Laravel application that you already have an account with. It holds your information that you could share with other third-party applications.
  2. The second one is the demo third-party client application, auth_redirection.php and callback.php, that wants to fetch your information from the Laravel application using the OAuth API.

The flow starts from the third-party client application. Go ahead and open the http://localhost/oauth2_client/auth_redirection.php URL in your browser, and that should redirect you to the Laravel application. If you're not already logged into the Laravel application, the application will ask you to do so in the first place.

Once the user is logged in, the application displays the authorization page.

If the user authorizes that request, the user will be redirected back to the third-party client application at http://localhost/oauth2_client/callback.php along with the code as the GET parameter that contains the authorization code.

Once the third-party application receives the authorization code, it could exchange that code with the Laravel application to get the access token. And that's exactly what it has done in the following snippet of the oauth2_client/callback.php file.

$ch = curl_init(); $url = 'http://your-laravel-site-url/oauth/token'; $params = array( 'grant_type' => 'authorization_code', 'client_id' => '1', 'client_secret' => 'zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01', 'redirect_uri' => 'http://localhost/oauth2_client/callback.php', 'code' => $_REQUEST['code'] ); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $params_string = ''; if (is_array($params) && count($params)) { foreach($params as $key=>$value) { $params_string .= $key.'='.$value.'&'; } rtrim($params_string, '&'); curl_setopt($ch,CURLOPT_POST, count($params)); curl_setopt($ch,CURLOPT_POSTFIELDS, $params_string); } $result = curl_exec($ch); curl_close($ch); $response = json_decode($result);

Next, the third-party application checks the response of the CURL request to see if it contains a valid access token in the first place.

As soon as the third-party application gets the access token, it could use that token to make further API calls to request resources as needed from the Laravel application. Of course, the access token needs to be passed in every request that's requesting resources from the Laravel application.

We've tried to mimic the use-case in that the third-party application wants to access the user information from the Laravel application. And we've already built an API endpoint, http://your-laravel-site-url/api/user/get, in the Laravel application that facilitates it.

// check if the response includes access_token if (isset($response->access_token) && $response->access_token) { // you would like to store the access_token in the session though... $access_token = $response->access_token; // use above token to make further api calls in this session or until the access token expires $ch = curl_init(); $url = 'http://your-laravel-site-url/api/user/get'; $header = array( 'Authorization: Bearer '. $access_token ); $query = http_build_query(array('uid' => '1')); curl_setopt($ch,CURLOPT_URL, $url . '?' . $query); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $result = curl_exec($ch); curl_close($ch); $response = json_decode($result); var_dump($result); }

So that's the complete flow of how you're supposed to consume the OAuth2 APIs in Laravel.

And with that, we’ve reached the end of this article.

Conclusion

Today, we explored the Passport library in Laravel, which allows us to set up an OAuth2 server in an application very easily. 

For those of you who are either just getting started with Laravel or looking to expand your knowledge, site, or application with extensions, we have a variety of things you can study in Envato Market.

Don't hesitate to share your thoughts and queries using the feed below!

Categories: Web Design

Getting Started With Redux: Connecting Redux With React

Thu, 05/31/2018 - 12:01

This is the third part of the series on Getting Started With Redux, and in this tutorial, we're going to learn how to connect a Redux store with React. Redux is an independent library that works with all the popular front-end libraries and frameworks. And it works flawlessly with React because of its functional approach.

You don't need to have followed the previous parts of this series for this tutorial to make sense. If you're here to learn about using React with Redux, you can take the Quick Recap below and then check out the code from the previous part and start from there. 

Quick Recap

In the first post, we learned about the Redux workflow and answered the question, Why Redux? We created a very basic demo application and showed you how the various components of Redux—actions, reducers, and the store— are connected.

In the previous post, we started building a contact list application that lets you add contacts and then displays them as a list. We created a Redux store for our contact list, and we added a few reducers and actions. We attempted to dispatch actions and retrieve the new state using store methods like store.dispatch() and store.getState().

By the end of this article, you'll have learned:

  1. the difference between container components and presentational components
  2. about the react-redux library
  3. how to bind react and redux using connect()
  4. how to dispatch actions using mapDispatchToProps
  5. how to retrieve state using mapStateToProps

The code for the tutorial is available on GitHub in the react-redux-demo repo. Grab the code from the v2 branch and use that as a starting point for this tutorial. If you're curious to know how the application looks by the end of this tutorial, try the v3 branch. Let's get started.

Designing a Component Hierarchy: Smart vs. Dumb Components

This is a concept that you've probably heard of before, but let's have a quick look at the difference between smart and dumb components. Recall that we created two separate directories for components, one named containers/ and the other components/. The benefit of this approach is that the behavior logic is separated from the view.

The presentational components are said to be dumb because they are concerned about how things look. They are decoupled from the business logic of the application and receive data and callbacks from a parent component exclusively via props. They don't care if your application is connected to a Redux store if the data is coming from the local state of the parent component. 

The container components, on the other hand, deal with the behavioral part and should contain very limited DOM markup and style. They pass the data that needs to be rendered to the dumb components as props. 

I've covered the topic in depth in another tutorial, Stateful vs. Stateless Components in React.

Moving on, let's see how we're going to organize our components.

Presentational Components

Here are the presentational components that we'll be using in this tutorial. 

components/AddContactForm.jsximport React from 'react'; const AddContactForm = ({onInputChange, onFormSubmit}) => ( <form> <div className="form-group"> <label htmlFor="emailAddress">Email address</label> <input type="email" class="form-control" name="email" onChange={onInputChange} placeholder="name@example.com" /> </div> {/* Some code omitted for brevity */} <div className="form-group"> <label htmlFor="physicalAddress">Address</label> <textarea className="form-control" name="address" onChange={onInputChange} rows="3"></textarea> </div> <button type="submit" onClick={onFormSubmit} class="btn btn-primary"> Submit </button> </form> ) export default AddContactForm;

This is an HTML form for adding a new contact. The component receives onInputChange and onFormSubmit callbacks as props. The onInputChange event is triggered when the input value changes and onFormSubmit when the form is being submitted.

components/ContactList.jsxconst ContactList = (props) => { return( <ul className="list-group" id="contact-list"> {props.contactList.map( (contact) => <li key={contact.email} className="list-group-item"> <ContactCard contact = {contact}/> </li> )} </ul>) } export default ContactList;

This component receives an array of contact objects as props, hence the name ContactList. We use the Array.map() method to extract individual contact details and then pass on that data to <ContactCard />.

components/ContactCard.jsxconst ContactCard = ({contact}) => { return( <div> <div className="col-xs-4 col-sm-3"> {contact.photo !== undefined ? <img src={contact.photo} alt={contact.name} className="img-fluid rounded-circle" /> : <img src="img/profile_img.png" alt ={contact.name} className="img-fluid rounded-circle" />} </div> <div className="col-xs-8 col-sm-9"> <span className="name">{contact.name + ' ' + contact.surname}</span><br/> {/* Some code omitted for brevity */} </div> </div> ) } export default ContactCard;

This component receives a contact object and displays the contact's name and image. For practical applications, it might make sense to host JavaScript images in the cloud.

Container Components

We're also going to construct bare-bones container components.

containers/Contacts.jsxclass Contacts extends Component { constructor(props) { super(props); this.returnContactList = this.returnContactList.bind(this); } returnContactList() { // Retrieve contactlist from the store } render() { return ( <div> <AddContact/> <br /> <ContactList contactList= {this.returnContactList()} /> </div> ); } } export default Contacts;

The returnContactList() function retrieves the array of contact objects and passes it to the ContactList component. Since returnContactList() retrieves the data from the store, we'll leave that logic blank for the moment.

containers/AddContacts.jsxclass AddContact extends Component { constructor(props) { super(props); /* Function binding goes here. Omitted for brevity */ } showAddContactBox() { /* Logic for toggling ContactForm */ } handleInputChange(event) { const target = event.target; const value = target.value; const name = target.name; /* Logic for handling Input Change */ } handleSubmit(e) { e.preventDefault(); /* Logic for hiding the form and update the state */ } /* Renders the AddContactForm */ renderForm() { return( <div className="col-sm-8 offset-sm-2"> <AddContactForm onFormSubmit={this.handleSubmit} onInputChange={this.handleInputChange} /> </div> ) } render() { return( <div> { /* A conditional statement goes here that checks whether the form should be displayed or not */} </div> ) } } export default AddContact;

We've created three bare-bones handler methods that correspond to the three actions. They all dispatch actions to update the state. In the render method, we've left out the logic for showing/hiding the form because we need to fetch the state. 

Now let's see how to bind react and redux together

The react-redux Library

React bindings are not available in Redux by default. You will need to install an extra library called react-redux first. 

npm install --save react-redux

The library exports just two APIs that you need to remember, a <Provider /> component and a higher-order function known as connect(). 

The Provider Component

Libraries like Redux need to make the store data accessible to the whole React component tree, starting from the root component. The Provider pattern allows the library to pass the data from top to bottom. The code below demonstrates how Provider magically adds the state to all the components in the component tree. 

Demo Codeimport { Provider } from 'react-redux' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )

The entire app needs to have access to the store. So we wrap the provider around the app component and then add the data that we need to the tree's context. The descendants of the component then have access to the data. 

The connect() Method 

Now that we've provided the store to our application, we need to connect React to the store. The only way that you can communicate with the store is by dispatching actions and by retrieving the state. We've previously used store.dispatch() to dispatch actions and store.getState() to retrieve the latest snapshot of the state. The connect() lets you do exactly this, but with the help of two methods known as mapDispatchToProps and mapStateToProps. I have demonstrated this concept in the example below:

Demo Codeimport {connect} from 'react-redux' const AddContact = ({newContact, addContact}) => { return ( <div> {newContact.name} <br /> {newContact.email} <br /> {newContact.phone} <br /> Are you sure you want to add this contact? <span onClick={addContact}> Yes </span> </div> ) } const mapStateToProps = state => { return { newContact : state.contacts.newContact } } const mapDispatchToProps = dispatch => { return { addContact : () => dispatch(addContact()) } } export default connect( mapStateToProps, mapDispatchToProps )(AddContact)

mapStateToProps and mapDispatchToProps both return an object, and the key of this object becomes a prop of the connected component. For instance, state.contacts.newContact is mapped to props.newContact. The action creator addContact() is mapped to props.addContact.  

But for this to work, you need the last line in the code snippet above. 

export default connect( mapStateToProps, mapDispatchToProps )(AddContact)

Instead of exporting the AddContact component directly, we're exporting a connected component. The connect provides addContact and newContact as props to the <AddContact/> component. 

How to Connect React and Redux

Next, we're going to cover the steps that you need to follow to connect React and Redux.

Install the react-redux Library

Install the react-redux library if you haven't already. You can use NPM or Yarn to install it. 

npm install react-redux --save Provide the Store to Your App Component

Create the store first. Then, make the store object accessible to your component tree by passing it as a prop to <Provider />.

index.jsimport React from 'react'; import {render}from 'react-dom'; import { Provider } from 'react-redux' import App from './App'; import configureStore from './store' const store = configureStore(); render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )Connect React Containers to Redux

The connect function is used to bind React containers to Redux. What that means is that you can use the connect feature to:

  1. subscribe to the store and map its state to your props
  2. dispatch actions and map the dispatch callbacks into your props

Once you've connected your application to Redux, you can use this.props to access the current state and also to dispatch actions. I am going to demonstrate the process on the AddContact component. AddContact needs to dispatch three actions and get the state of two properties from the store. Let's have a look at the code.

First, import connect into AddContact.jsx.

import { connect } from 'react-redux';

Second, create two methods: mapStateToProps and mapDispatchToProps.

function mapStateToProps(state) { return { isHidden : state.ui.isAddContactFormHidden, newContact: state.contacts.newContact } } function mapDispatchToProps(dispatch) { return { onFormSubmit: (newContact) => { dispatch(addContact(newContact)); }, onInputChange: (name,value) => { dispatch(handleInputChange(name,value)); }, onToggle: () => { dispatch(toggleContactForm()); } } }

mapStateToProps receives the state of the store as an argument. It returns an object that describes how the state of the store is mapped into your props. mapDispatchToProps returns a similar object that describes how the dispatch actions are mapped to your props. 

Finally, we use connect to bind the AddContact component to the two functions as follows:

export default connect(mapStateToProps, mapDispatchToProps) (AddContact) Update the Container Components to Use the Props

The component's props are now equipped to read state from the store and dispatch actions. The logic for handeInputChange, handleSubmit and showAddContactBox should be updated as follows:

showAddContactBox() { const { onToggle } = this.props; onToggle(); } handleInputChange(event) { const target = event.target; const value = target.value; const name = target.name; const { onInputChange } = this.props; onInputChange(name,value); } handleSubmit(e) { e.preventDefault(); this.props.onToggle(); this.props.onFormSubmit(); }

We've defined the handler methods, but there is still one part missing—the conditional statement inside the render function.

render() { return( <div> { this.props.isHidden === false ? this.renderForm(): <button onClick={this.showAddContactBox} className="btn"> Add Contact </button>} </div> ) }

If isHidden is false, the form is rendered. Otherwise, a button gets rendered. 

Displaying the Contacts

We've completed the most challenging part. Now, all that's left is to display these contacts as a list. The Contacts container is the best place for that logic. 

import React, { Component } from 'react'; import { connect } from 'react-redux'; /* Component import omitted for brevity */ class Contact extends Component { constructor(props) { super(props); this.returnContactList = this.returnContactList.bind(this); } returnContactList() { return this.props.contactList; } render() { return ( <div> <br /> <AddContact/> <br /> <ContactList contactList= {this.returnContactList()} /> </div> ); } } function mapStateToProps(state) { return { contactList : state.contacts.contactList, } } export default connect(mapStateToProps, null) (Contact);

We've gone through the same procedure that we followed above to connect the Contacts component with the Redux store. The mapStateToProps function maps the store object to the contactList props. We then use connect to bind the props value to the Contact component. The second argument to the connect is null because we don't have any actions to be dispatched. That completes the integration of our app with the state of the Redux store. 

What Next?

In the next post, we'll take a deeper look at middleware and start dispatching actions that involve fetching data from the server. Share your thoughts in the comments!

Categories: Web Design

Getting Started With Redux: Learn by Example

Mon, 05/28/2018 - 09:00

Redux helps you manage state by setting the state up at a global level. In the previous tutorial, we had a good look at the Redux architecture and the integral components of Redux such as actions, action creators, the store, and reducers. 

In this second post of the series, we are going to bolster our understanding of Redux and build on top of what we already know. We will start by creating a realistic Redux application—a contact list—that's more complex than a basic counter. This will help you strengthen your understanding of the single store and multiple reducers concept which I introduced in the previous tutorial. Then later we'll talk about binding your Redux state with a React application and the best practices that you should consider while creating a project from scratch. 

However, it's okay if you haven't read the first post—you should still be able to follow along as long as you know the Redux basics. The code for the tutorial is available in the repo, and you can use that as a starting point. 

Creating a Contact List Using Redux

We're going to build a basic contact list with the following features:

  • display all contacts
  • search for contacts
  • fetch all contacts from the server
  • add a new contact
  • push the new contact data into the server

Here's what our application is going to look like:

Final product — Contact list View


Final Product — Add contact view

Covering everything in one stretch is hard. So in this post we're going to focus on just the Redux part of adding a new contact and displaying the newly added contact. From a Redux perspective, we'll be initializing the state, creating the store, adding reducers and actions, etc. 

In the next tutorial, we'll learn how to connect React and Redux and dispatch Redux actions from a React front-end. In the final part, we'll shift our focus towards making API calls using Redux. This includes fetching the contacts from the server and making a server request while adding new contacts. Apart from that, we'll also create a search bar feature that lets you search all the existing contacts. 

Create a Sketch of the State Tree

You can download the react-redux demo application from my GitHub repository. Clone the repo and use the v1 branch as a starting point. The v1 branch is very similar to the create-react-app template. The only difference is that I've added a few empty directories to organise Redux. Here's the directory structure.

. ├── package.json ├── public ├── README.md ├── src │ ├── actions │ ├── App.js │ ├── components │ ├── containers │ ├── index.js │ ├── reducers │ └── store └── yarn.lock

Alternatively, you can create a new project from scratch. Either way, you will need to have installed a basic react boilerplate and redux before you can get started. 

It's a good idea to have a rough sketch of the state tree first. In my opinion, this will save you lots of time in the long run. Here's a rough sketch of the possible state tree. 

const initialState = { contacts: { contactList: [], newContact: { name: '', surname: '', email: '', address: '', phone: '' }, ui: { //All the UI related state here. eg: hide/show modals, //toggle checkbox etc. } } }  

Our store needs to have two properties—contacts and ui. The contacts property takes care of all contacts-related state, whereas the ui handles UI-specific state. There is no hard rule in Redux that prevents you from placing the ui object as a sub-state of contacts. Feel free to organize your state in a way that feels meaningful to your application. 

The contacts property has two properties nested inside it—contactlist and newContact. The contactlist is an array of contacts, whereas newContact temporarily stores contact details while the contact form is being filled. I am going to use this as a starting point for building our awesome contact list app. 

How to Organize Redux

Redux doesn't have an opinion about how you structure your application. There are a few popular patterns out there, and in this tutorial, I will briefly talk about some of them. But you should pick one pattern and stick with it until you fully understand how all the pieces are connected together.

The most common pattern that you'll find is the Rails-style file and folder structure. You'll have several top-level directories like the ones below:

  • components: A place to store the dumb React components. These components do not care whether you're using Redux or not.
  • containers: A directory for the smart React components that dispatch actions to the Redux store. The binding between redux and react will be taking place here. 
  • actions: The action creators will go inside this directory. 
  • reducers: Each reducer gets an individual file, and you'll be placing all the reducer logic in this directory.
  • store: The logic for initializing the state and configuring the store will go here. 

The image below demonstrates how our application might look if we follow this pattern:

The Rails style should work for small and mid-sized applications. However, when your app grows, you can consider moving towards the domain-style approach or other popular alternatives that are closely related to domain-style. Here, each feature will have a directory of its own, and everything related to that feature (domain) will be inside it. The image below compares the two approaches, Rails-style on the left and domain-style on the right. 

For now, go ahead and create directories for components, containers, store, reducers, and action. Let's start with the store. 

Single Store, Multiple Reducers

Let's create a prototype for the store and the reducer first. From our previous example, this is how our store would look: 

const store = createStore( reducer, { contacts: { contactlist: [], newContact: { } }, ui: { isContactFormHidden: true } }) const reducer = (state, action) => { switch(action.type) { case "HANDLE_INPUT_CHANGE": break; case "ADD_NEW_CONTACT": break; case "TOGGLE_CONTACT_FORM": break; } return state; }

The switch statement has three cases that correspond to three actions that we will be creating. Here is a brief explanation of what the actions are meant for. 

  • HANDLE_INPUT_CHANGE: This action gets triggered when the user inputs new values into the contact form.
  • ADD_NEW_CONTACT: This action gets dispatched when the user submits the form.
  • TOGGLE_CONTACT_FORM: This is a UI action that takes care of showing/hiding the contact form. 

Although this naive approach works, as the application grows, using this technique will have a few shortcomings.

  1. We're using a single reducer. Although a single reducer sounds okay for now, imagine having all your business logic under one very large reducer.  
  2. The code above doesn't follow the Redux structure that we've discussed in the previous section.

To fix the single reducer issue, Redux has a method called combineReducers that lets you create multiple reducers and then combine them into a single reducing function. The combineReducers function enhances readability. So I am going to split the reducer into two—a contactsReducer and a uiReducer. 

In the example above, createStore accepts an optional second argument which is the initial state. However, if we are going to split the reducers, we can move the whole initialState to a new file location, say reducers/initialState.js. We will then import a subset of initialState into each reducer file. 

Splitting the Reducer 

Let's restructure our code to fix both the issues. First, create a new file called store/createStore.js and add the following code:

import {createStore} from 'redux'; import rootReducer from '../reducers/'; /*Create a function called configureStore */ export default function configureStore() { return createStore(rootReducer); }

Next, create a root reducer in reducers/index.js as follows:

import { combineReducers } from 'redux' import contactsReducer from './contactsReducer'; import uiReducer from './uiReducer'; const rootReducer =combineReducers({ contacts: contactsReducer, ui: uiReducer, }) export default rootReducer;

Finally, we need to create the code for the contactsReducer and uiReducer.

reducers/contactsReducer.jsimport initialState from './initialState'; export default function contactReducer(state = initialState.contacts, action) { switch(action.type) { /* Add contacts to the state array */ case "ADD_CONTACT": { return { ...state, contactList: [...state.contactList, state.newContact] } } /* Handle input for the contact form. The payload (input changes) gets merged with the newContact object */ case "HANDLE_INPUT_CHANGE": { return { ...state, newContact: { ...state.newContact, ...action.payload } } } default: return state; } }reducers/uiReducer.jsimport initialState from './initialState'; export default function uiReducer(state = initialState.ui, action) { switch(action.type) { /* Show/hide the form */ case "TOGGLE_CONTACT_FORM": { return { ...state, isContactFormHidden: !state.isContactFormHidden } } default: return state; } }

When you're creating reducers, always keep the following in mind: a reducer needs to have a default value for its state, and it always needs to return something. If the reducer fails to follow this specification, you will get errors.

Since we've covered a lot of code, let's have a look at the changes that we've made with our approach:

  1. The combineReducers call has been introduced to tie together the split reducers.
  2. The state of the ui object will be handled by uiReducer and the state of the contacts by the contactsReducer. 
  3. To keep the reducers pure, spread operators have been used. The three dot syntax is part of the spread operator. If you're not comfortable with the spread syntax, you should consider using a library like Immutability.js.
  4. The initial value is no longer specified as an optional argument to createStore. Instead, we've created a separate file for it called initialState.js. We're importing initialState and then setting the default state by doing state = initialState.ui. 
State Initialization

Here's the code for the reducers/initialState.js file.

const initialState = { contacts: { contactList: [], newContact: { name: '', surname: '', email: '', address: '', phone: '' }, }, ui: { isContactFormHidden: true } } export default initialState;Actions and Action Creators

Let's add a couple of actions and action creators for adding handling form changes, adding a new contact, and toggling the UI state. If you recall, action creators are just functions that return an action. Add the following code in actions/index.js.

export const addContact =() => { return { type: "ADD_CONTACT", } } export const handleInputChange = (name, value) => { return { type: "HANDLE_INPUT_CHANGE", payload: { [name]: value} } } export const toggleContactForm = () => { return { type: "TOGGLE_CONTACT_FORM", } }

Each action needs to return a type property. The type is like a key that determines which reducer gets invoked and how the state gets updated in response to that action. The payload is optional, and you can actually call it anything you want. 

In our case, we've created three actions.

The TOGGLE_CONTACT_FORM doesn't need a payload because every time the action is triggered, the value of ui.isContactFormHidden gets toggled. Boolean-valued actions do not require a payload. 

The HANDLE_INPUT_CHANGE action is triggered when the form value changes. So, for instance, imagine that the user is filling the email field. The action then receives "email" and "bob@example.com" as inputs, and the payload handed over to the reducer is an object that looks like this:

{ email: "bob@example.com" }

The reducer uses this information to update the relevant properties of the newContact state. 

Dispatching Actions and Subscribing to the Store

The next logical step is to dispatch the actions. Once the actions are dispatched, the state changes in response to that. To dispatch actions and to get the updated state tree, Redux offers certain store actions. They are:

  • dispatch(action): Dispatches an action that could potentially trigger a state change. 
  • getState(): Returns the current state tree of your application.
  • subscriber(listener): A change listener that gets called every time an action is dispatched and some part of the state tree is changed. 

Head to the index.js file and import the configureStore function and the three actions that we created earlier:

import React from 'react'; import {render}from 'react-dom'; import App from './App'; /* Import Redux store and the actions */ import configureStore from './store/configureStore'; import {toggleContactForm, handleInputChange} from './actions';

Next, create a store object and add a listener that logs the state tree every time an action is dispatched:

const store = configureStore(); //Note that subscribe() returns a function for unregistering the listener const unsubscribe = store.subscribe(() => console.log(store.getState()) )

Finally, dispatch some actions:

/* returns isContactFormHidden returns false */ store.dispatch(toggleContactForm()); /* returns isContactFormHidden returns false */ store.dispatch(toggleContactForm()); /* updates the state of contacts.newContact object */ store.dispatch(handleInputChange('email', 'manjunath@example.com')) unsubscribe;

If everything is working right, you should see this in the developer console.

That's it! In the developer console, you can see the Redux store being logged, so you can see how it changes after each action.

Summary

We've created a bare-bones Redux application for our awesome contact list application. We learned about reducers, splitting reducers to make our app structure cleaner, and writing actions for mutating the store. 

Towards the end of the post, we subscribed to the store using the store.subscribe() method. Technically, this isn't the best way to get things done if you're going to use React with Redux. There are more optimized ways to connect the react front-end with Redux. We'll cover those in the next tutorial.  

Categories: Web Design

Introduction to API Calls With React and Axios

Mon, 05/28/2018 - 05:37

This tutorial will teach you how to use Axios to fetch data and then how to manipulate it and eventually display it on your page with filtering functionality. You will learn how to use the map, filter and includes methods along the way. On top of that, you will be creating a Higher-Order Component (HOC) to handle the loading state of the fetched data from the API endpoint.

Let's start with a clean React app. I assume you use create-react-app, and the filenames will be in accordance with its outputs.

We only need to install the Axios module for this tutorial.

Go to your project directory through the terminal window and then type in npm install axios -save in order to install Axios for your project locally.

Fetching the Data

We will be using the Random User Generator API to fetch random user information to use in our application.

Let's add the Axios module to our application by importing it into our App.js file.

import axios from 'axios'

The Random User Generator API offers a bunch of options for creating various types of data. You can check the documentation for further information, but for this tutorial, we will keep it simple.

We want to fetch ten different users, and we only need the name, surname, and a unique ID, which is required for React when creating lists of elements. Also, to make the call a bit more specific, let's include the nationality option as an example.

Below is the API that we will make a call for.

Note that I didn't use the id option provided in the API due to the fact that it sometimes returns null for some users. So, just to make sure that there will be a unique value for each user, I included the registered option in the API.

https://randomuser.me/api/?results=10&inc=name,registered&nat=fr

You can copy and paste it into your browser and you will see the returned data in JSON format.

Now, the next thing is to make an API call through Axios.

First of all, let's create a state so that we can store the fetched data.

Inside our App component, add a class constructor and then create the state.

constructor(props){ super(props) this.state = { users: [], store: [] } }

Here you see users and store states. One will be used for filtering purposes and will not be edited, and the other one will hold the filter results that will be shown in the DOM.

Now go ahead and create the componentDidMount() lifecycle hook.

Inside this lifecycle hook, we will fetch the data, and then by using the map method, we will create new intermediate data that we will use inside the setState method.

If you check the result of the API call in your browser, you will see that there are first and last key-value pairs inside the name object but no key-value pair for a full name. So we will be combining first and last to create a full name inside a new JavaScript Object. Note that JSON and JavaScript Object are different things, although they basically work the same way.

Let's move step by step.

Add the following code to your App component.

componentDidMount(){ axios.get('https://randomuser.me/api/?results=10&inc=name,registered&nat=fr') .then(json => console.log(json)) }

When you check the console, you will see an object output. If you open up this object, there is another object inside it named data, and inside data, there is an array named results.

Let's further change the console.log(json).

componentDidMount(){ axios.get('https://randomuser.me/api/?results=10&inc=name,registered&nat=fr') .then(json => console.log(json.data.results[0].name.first)) }

Here we reached the name of the first value inside the results array.

Now let's use the built-in map method of JavaScript in order to iterate through each element inside the array and create a new array of JavaScript Objects with a new structure.

componentDidMount(){ axios.get('https://randomuser.me/api/?results=10&inc=name,registered&nat=fr') .then(json => json.data.results.map(result => ( { name: `${result.name.first} ${result.name.last}`, id: result.registered }))) .then(newData => console.log(newData)) }

Here, we called the map method on json.data.results, which is an array, and then referred each element of the array as result (notice the singular/plural difference). Then, by using the key-value pair of each object inside the array, we created another object with name and id key-value pairs.

At the end, we used another then method in order to be able to refer to our new data. We referred it as newData, and then we just logged to the console to see if everything went as planned.

You should see a new array with objects having name and id pairs.

Storing the Data

Instead of logging the result to the console, we have to store it. In order to do that, we will use setState.

componentDidMount(){ axios.get('https://randomuser.me/api/?results=10&inc=name,registered&nat=fr') .then(json => json.data.results.map(result => ( { name: `${result.name.first} ${result.name.last}`, id: result.registered }))) .then(newData => this.setState({users: newData, store: newData})) .catch(error => alert(error)) }

Here, we initially set both users and store data with our new newData array.

We need two variables due to the fact that we need to store the original data and should never lose it. By using the information inside the store state, we can filter the data and then populate the users state and show it on the page. This will be clearer when we implement the filtering functionality.

Last but not least, we added catch to actually catch any possible errors during fetching and display the error as an alert message.

Filtering Functionality

The idea of filtering is quite simple. We have our store state, and it always keeps the original data without changing. Then, by using the filter function on this state, we only get the matching elements and then assign them to the users state.

this.state.store.filter(item => item.name.toLowerCase().includes(e.target.value.toLowerCase()))

The filter method requires a function as an argument, a function to be run for each element in the array. Here we refer each element inside the array as item. Then we take the name key of each item and convert it to lower case in order to make our filtering functionality case insensitive. 

After we have the name key for the item, we check if this one includes the search string we typed in. includes is another built-in JavaScript method. We pass the search string typed in the input field as an argument to includes, and it returns if this string is included in the variable it was called on. Again, we convert the input string to lower case so that it does not matter whether you type upper or lower case inputs. 

In the end, the filter method returns the matching elements. So we simply take these elements and store them inside the users state through setState.

Below you can find the final version of the function we created.

filterNames(e){ this.setState({users: this.state.store.filter(item => item.name.toLowerCase().includes(e.target.value.toLowerCase()))}) }Creating the Components

Although for this small example we could put everything inside the App component, let's take advantage of React and make some small functional/stateless components.

Let's add the following structure to the render method of the App component.

render() { const {users} = this.state return ( <div className="Card"> <div className="header">NAME LIST</div> <SearchBar searchFunc={(e) => this.filterNames(e)}/> <List usernames={users}/> </div> ); }

For the time being, we will be just focusing on the functionality. Later, I will provide the CSS file I have created.

Notice that we have the searchFunc prop for the SearchBar component and the usernames prop for the List component.

Note that we use the users state instead of the store state to show the data because the users state is the one containing the filtered results.

The SearchBar Component

This component is quite straightforward. It only takes the filterNames function as a prop and calls this function when the input field changes.

import React from 'react' const SearchBar = props => { return( <div> <input className="searchBar" type="text" placeholder="search user" onChange={props.searchFunc}/> </div> ) } export default SearchBar The List Component

This one will simply list the names of the users.

import React, {Component} from 'react' import LoadingHOC from './LoadingHOC' import '../styles/main.css' const List = (props) =>{ const{usernames} = props return( <ul> {usernames.map(user => <li key={user.id}>{user.name}</li>)} </ul> ) } export default LoadingHOC(List)

Here, we again used the map method to get each item in the array and create a <li> item out of it. Note that when you use map to create a list of items, you need to use a key in order for React to keep track of each list item.

Notice that we wrapped List with another component named LoadingHOC before exporting it. This is how Higher-Order Components (HOCs) work. 

What we did here is to pass our component as an argument to another component before exporting it. So this LoadingHOC component will be enhancing our component with new features.

The LoadingHOC Component

As I briefly explained before, a HOC takes a component as an input and then exports an enhanced version of the input component.

import React, {Component} from 'react' import spinner from '../spinner.gif' const LoadingHOC = (WrappedState) =>{ return( class LoadingHOC extends Component{ render(){ return this.props.usernames.length === 0 ? <img className="isLoading" src={spinner}/> : <WrappedState {...this.props}/> } } ) } export default LoadingHOC

Inside the HOC, we can directly access the props of the input component. So we just check whether the length of the usernames prop is 0 or not. If it is 0, this means that the data has yet to be fetched because it is an empty array by default. So we just show a spinner GIF that we imported. Otherwise, we just show the input component itself.

It's imported in order not to forget to pass any props and states back to the input component with a spread operator. Otherwise, your component would be deprived of them.

CSS File

Below you can find the CSS file specific to this example.

body, html { -webkit-font-smoothing: antialiased; margin: 0; padding: 0; background-color: #f1f1f1; font-family: 'Raleway', sans-serif; -webkit-text-size-adjust: 100%; } body { display: flex; justify-content: center; font-size: 1rem/16; margin-top: 50px; } li, ul { list-style: none; margin: 0; padding: 0; } ul { margin-top: 10px; } li { font-size: 0.8rem; margin-bottom: 8px; text-align: center; color: #959595; } li:last-of-type { margin-bottom: 50px; } .Card { font-size: 1.5rem; font-weight: bold; display: flex; flex-direction: column; align-items: center; width: 200px; border-radius: 10px; background-color: white; box-shadow: 0 5px 3px 0 #ebebeb; } .header { position: relative; font-size: 20px; margin: 12px 0; color: #575757; } .header::after { content: ''; position: absolute; left: -50%; bottom: -10px; width: 200%; height: 1px; background-color: #f1f1f1; } .searchBar { text-align: center; margin: 5px 0; border: 1px solid #c4c4c4; height: 20px; color: #575757; border-radius: 3px; } .searchBar:focus { outline-width: 0; } .searchBar::placeholder { color: #dadada; } .isLoading { margin: 30px 0; width: 150px; filter: opacity(0.3); } Conclusion

Throughout this tutorial, we took a quick look at the Random User Generator API as a source of random data. Then we fetched the data from an API endpoint and restructured the results inside a new JavaScript Object with the map method.

The next thing was to create a filtering function with the filter and includes methods. Finally, we created two different components and enhanced one of them with a Higher-Order Component (HOC) by introducing a loading indicator when the data is not there yet.

Over the last couple of years, React has grown in popularity. In fact, we have a number of items in Envato Market that are available for purchase, review, implementation, and so on. If you’re looking for additional resources around React, don’t hesitate to check them out.

Categories: Web Design

A Beginner's Guide to Regular Expressions in JavaScript

Fri, 05/25/2018 - 06:00

Everyone working with JavaScript will have to deal with strings at one point or other. Sometimes, you will just have to store a string inside another variable and then pass it over. Other times, you will have to inspect it and see if it contains a particular substring.

However, things are not always this easy. There will be times when you will not be looking for a particular substring but a set of substrings which follow a certain pattern.

Let's say you have to replace all occurrences of "Apples" in a string with "apples". You could simply use theMainString.replace("Apples", "apples"). Nice and easy.

Now let's say you have to replace "appLes" with "apples" as well. Similarly, "appLES" should become "apples" too. Basically, all case variations of "Apple" need to be changed to "apple". Passing simple strings as an argument will no longer be practical or efficient in such cases.

This is where regular expressions come in—you could simply use the case-insensitive flag i and be done with it. With the flag in place, it doesn't matter if the original string contained "Apples", "APPles", "ApPlEs", or "Apples". Every instance of the word will be replaced with "apples".

Just like the case-insensitive flag, regular expressions offer a lot of other features which will be covered in this tutorial.

Using Regular Expressions in JavaScript

You have to use a slightly different syntax to indicate a regular expression inside different String methods. Unlike a simple string, which is enclosed in quotes, a regular expression consists of a pattern enclosed between slashes. Any flags that you use in a regular expression will be appended after the second slash.

Going back to the previous example, here is what the replace() method would look like with a regular expression and a simple string.

"I ate Apples".replace("Apples", "apples"); // I ate apples "I ate Apples".replace(/Apples/i, "apples"); // I ate apples "I ate aPPles".replace("Apples", "apples"); // I ate aPPles "I ate aPPles".replace(/Apples/i, "apples"); // I ate apples

As you can see, the regular expression worked in both cases. We will now learn more about flags and special characters that make up the pattern inside a regular expression.

Backslash in Regular Expressions

You can turn normal characters into special characters by adding a backslash before them. Similarly, you can turn special characters into normal characters by adding a backslash before them.

For example, d is not a special character. However, \d is used to match a digit character in a string. Similarly, D is not a special character either, but \D is used to match non-digit characters in a string.

Digit characters include 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9. When you use \d inside a regular expression, it will match any of these nine characters. When you use \D inside a regular expression, it will match all the non-digit characters.

The following example should make things clear.

"L8".replace(/\d/i, "E"); // LE "L8".replace(/\D/i, "E"); // E8 "LLLLL8".replace(/\D/i, "E"); // ELLLL8

You should note that only the first matched character is replaced in the third case. You can also use flags to replace all the matches. We will learn about such flags later.

Just like \d and \D, there are other special character sequences as well.

  1. You can use \w to match any "word" character in a string. Here, word character refers to A-Z, a-z, 0-9, and _. So, basically, it will match all digits, all lowercase and uppercase alphabets, and the underscore.
  2. You can use \W to match any non-word character in a string. It will match characters like %, $, #, ₹, etc.
  3. You can use \s to match a single white space character, which includes space, tab, form feed, and line feed. Similarly, you can use \S to match all other characters besides white space.
  4. You can also look for a specific white space character using \f, \n, \r, \t, and \v, which stand for form feed, line feed, carriage return, horizontal tab, and vertical tab.

Sometimes, you will face situations where you need to replace a word with its substitute, but only if it is not part of a larger word. For example, consider the following sentence:

"A lot of pineapple images were posted on the app".

In this case, we want to replace the word "app" with "board". However, using a simple regular expression pattern will turn "apple" into "boardle", and the final sentence would become:

"A lot of pineboardle images were posted on the app".

In such cases, you can use another special character sequence: \b. This checks for word boundaries. A word boundary is formed by use of any non-word characters like space, "$", "%", "#", etc. Watch out, though—it also includes accented characters like "ü".

"A lot of pineapple images were posted on the app".replace(/app/, "board"); // A lot of pineboardle images were posted on the app "A lot of pineapple images were posted on the app".replace(/\bapp/, "board"); // A lot of pineapple images were posted on the board

Similarly, you can use \B to match a non-word boundary. For example, you could use \B to only match "app" when it is within another word, like "pineapple".

Matching a Pattern "n" Number of Times

You can use ^ to tell JavaScript to only look at the beginning of the string for a match. Similarly, you can use $ to only look at the end of the string for a match.

You can use * to match the preceding expression 0 or more times. For example, /Ap*/ will match A, Ap, App, Appp, and so on.

In a similar manner, you can use + to match the preceding expression 1 or more times. For example, /Ap+/ will match Ap, App, Appp, and so on. The expression will not match the single A this time.

Sometimes, you only want to match a specific number of occurrences of a given pattern. In such cases, you should use the {n} character sequence, where n is a number. For instance, /Ap{2}/ will match App but not Ap. It will also match the first two 'p's in Appp and leave the third one untouched.

You can use {n,} to match at least 'n' occurrences of a given expression. This means that /Ap{2,}/ will match App but not Ap. It will also match all the 'p's in Apppp and replace them with your replacement string.

You can also use {n,m} to specify a minimum and maximum number and limit the number of times the given expression should be matched. For example, /Ap{2,4}/ will match App, Appp, and Apppp. It will also match the first four 'p's in Apppppp and leave the rest of them untouched.

"Apppppples".replace(/Ap*/, "App"); // Apples "Ales".replace(/Ap*/, "App"); // Apples "Appppples".replace(/Ap{2}/, "Add"); // Addppples "Appppples".replace(/Ap{2,}/, "Add"); // Addles "Appppples".replace(/Ap{2,4}/, "Add"); // AddplesUsing Parentheses to Remember Matches

So far, we have only replaced patterns with a constant string. For example, in the previous section, the replacement we used was always "Add". Sometimes, you will have to look for a pattern match inside the given string and then replace it with a part of the pattern.

Let's say you have to find a word with five or more letters in a string and then add an "s" at the end of the word. In such cases, you will not be able to use a constant string value as a replacement as the final value depends on the matching pattern itself.

"I like Apple".replace(/(\w{5,})/, '$1s'); // I like Apples "I like Banana".replace(/(\w{5,})/, '$1s'); // I like Bananas

This was a simple example, but you can use the same technique to keep more than one matching pattern in memory. The number of sub-patterns in the full match will be determined by the number of parentheses used.

Inside the replacement string, the first sub-match will be identified using $1, the second sub-match will be identified using $2, and so on. Here is another example to further clarify the usage of parentheses.

"I am looking for John and Jason".replace(/(\w+)\sand\s(\w+)/, '$2 and $1'); // I am looking for Jason and JohnUsing Flags With Regular Expressions

As I mentioned in the introduction, one more important feature of regular expressions is the use of special flags to modify how a search is performed. The flags are optional, but you can use them to do things like making a search global or case-insensitive.

These are the four commonly used flags to change how JavaScript searches or replaces a string.

  • g: This flag will perform a global search instead of stopping after the first match.
  • i: This flag will perform a search without checking for an exact case match. For instance, Apple, aPPLe, and apPLE are all treated the same during case-insensitive searches.
  • m: This flag will perform a multi-line search.
  • y: This flag will look for a match in the index indicated by the lastIndex property.

Here are some examples of regular expressions used with flags:

"I ate apples, you ate apples".replace(/apples/, "mangoes"); // "I ate mangoes, you ate apples" "I ate apples, you ate apples".replace(/apples/g, "mangoes"); // "I ate mangoes, you ate mangoes" "I ate apples, you ate APPLES".replace(/apples/, "mangoes"); // "I ate mangoes, you ate APPLES" "I ate apples, you ate APPLES".replace(/apples/gi, "mangoes"); // "I ate mangoes, you ate mangoes" var stickyRegex = /apples/y; stickyRegex.lastIndex = 3; "I ate apples, you ate apples".replace(stickyRegex, "mangoes"); // "I ate apples, you ate apples" var stickyRegex = /apples/y; stickyRegex.lastIndex = 6; "I ate apples, you ate apples".replace(stickyRegex, "mangoes"); // "I ate mangoes, you ate apples" var stickyRegex = /apples/y; stickyRegex.lastIndex = 8; "I ate apples, you ate apples".replace(stickyRegex, "mangoes"); // "I ate apples, you ate apples"Final Thoughts

The purpose of this tutorial was to introduce you to regular expressions in JavaScript and their importance. We began with the basics and then covered backslash and other special characters. We also learned how to check for a repeating pattern in a string and how to remember partial matches in a pattern in order to use them later.

Finally, we learned about commonly used flags which make regular expressions even more powerful. You can learn more about regular expressions in this article on MDN.

If there is anything that you would like me to clarify in this tutorial, feel free to let me know in the comments.

Categories: Web Design

Introduction to Popmotion: Custom Animation Scrubber

Fri, 05/25/2018 - 05:32

In the first part of the Popmotion introductory series, we learned how to use time-based animations like tween and keyframes. We also learned how to use those animations on the DOM, using the performant styler.

In part two, we learned how to use pointer tracking and record velocity. We then used that to power the velocity-based animations spring, decay, and physics.

In this final part, we're going to be creating a scrubber widget, and we're going to use it to scrub a keyframes animation. We'll make the widget itself from a combination of pointer tracking as well as spring and decay to give it a more visceral feel than run-of-the-mill scrubbers.

Try it for yourself:

Getting StartedMarkup

First, fork this CodePen for the HTML template. As before, because this is an intermediate tutorial, I won't go through everything.

The main twist of note is that the handle on the scrubber is made up of two div elements: .handle and .handle-hit-area.

.handle is the round blue visual indicator of where the scrubber handle is. We've wrapped it in an invisible hit area element to make grabbing the element easier for touchscreen users.

Import Functions

At the top of your JS panel, import everything we're going to use in this tutorial:

const { easing, keyframes, pointer, decay, spring, styler, transform, listen, value } = popmotion; const { pipe, clamp, conditional, linearSpring, interpolate } = transform;Select Elements

We're going to need three elements in this tutorial. We'll animate the .box, drag and animate the .handle-hit-area, and measure the .range.

Let's also create stylers for the elements we're going to animate:

const box = document.querySelector('.box'); const boxStyler = styler(box); const handle = document.querySelector('.handle-hit-area'); const handleStyler = styler(handle); const range = document.querySelector('.range');Keyframes Animation

For our scrubbable animation, we're going to make the .box move from left to right with keyframes. However, we could just as easily scrub a tween or timeline animation using the same method outlined later in this tutorial.

const boxAnimation = keyframes({ values: [0, -150, 150, 0], easings: [easing.backOut, easing.backOut, easing.easeOut], duration: 2000 }).start(boxStyler.set('x'));

Your animation will now be playing. But we don't want that! Let's pause it for now:

boxAnimation.pause();Dragging the x-axis

It's time to use pointer to drag our scrubber handle. In the previous tutorial, we used both x and y properties, but with a scrubber we only need x.

We prefer to keep our code reusable, and tracking a single pointer axis is quite a common use case. So let's create a new function called, imaginatively, pointerX.

It will work exactly like pointer except it'll take just a single number as its argument and output just a single number (x):

const pointerX = (x) => pointer({ x }).pipe(xy => xy.x);

Here, you can see we're using a method of pointer called pipe. pipe is available on all the Popmotion actions we've seen so far, including keyframes.

pipe accepts multiple functions. When the action is started, all output will be passed through each of these functions in turn, before the update function provided to start fires.

In this case, our function is simply:

xy => xy.x

All it is doing is taking the { x, y } object usually output by pointer and returning just the x axis.

Event Listeners

We need to know if the user has started pressing the handle before we start tracking with our new pointerX function.

In the last tutorial we used the traditional addEventListener function. This time, we're going to use another Popmotion function called listen. listen also provides a pipe method, as well as access to all action methods, but we're not going to use that here.

listen allows us to add event listeners to multiple events with a single function, similar to jQuery. So we can condense the previous four event listeners to two:

listen(handle, 'mousedown touchstart').start(startDrag); listen(document, 'mouseup touchend').start(stopDrag);Move the Handle

We'll be needing the handle's x velocity later on, so let's make it a value, which as we learned in the last tutorial allows us to query velocity. On the line after we define handleStyler, add:

const handleX = value(0, handleStyler.set('x'));

Now we can add our startDrag and stopDrag functions:

const startDrag = () => pointerX(handleX.get()) .start(handleX); const stopDrag = () => handleX.stop();

Right now, the handle can be scrubbed beyond the boundaries of the slider, but we'll come back to this later.

Scrubbing

Now we have a visually functional scrubber, but we're not scrubbing the actual animation.

Every value has a subscribe method. This allows us to attach multiple subscribers to fire when the value changes. We want to seek the keyframes animation whenever handleX updates.

First, measure the slider. On the line after we define range, add:

const rangeWidth = range.getBoundingClientRect().width;

keyframes.seek accepts a progress value as expressed from 0 to 1, whereas our handleX is set with pixel values from 0 to rangeWidth.

We can convert from the pixel measurement to a 0 to 1 range by dividing the current pixel measurement by rangeWidth. On the line after boxAnimation.pause(), add this subscribe method:

handleX.subscribe(v => boxAnimation.seek(v / rangeWidth));

Now, if you play with the scrubber, the animation will scrub successfully!

The Extra MileSpring Boundaries

The scrubber can still be pulled outside the boundaries of the full range. To solve this, we could simply use a clamp function to ensure we don't output values outside of 0, rangeWidth.

Instead, we're going to go the extra step and attach springs to the end of our slider. When a user pulls the handle beyond the permitted range, it will tug back towards it. If the user releases the handle while it's outside the range, we can use a spring animation to snap it back.

We'll make this process a single function that we can provide to the pointerX pipe method. By creating a single, reusable function, we can reuse this piece of code with any Popmotion animation, with configurable ranges and spring strengths.

First, let's apply a spring to the left-most limit. We'll use two transformers, conditional and linearSpring.

const springRange = (min, max, strength) => conditional( v => v < min, linearSpring(strength, min) );

conditional takes two functions, an assertion and a transformer. The assertion receives the provided value and returns either true or false. If it returns true, the second function will be provided the value to transform and return.

In this case, the assertion is saying, "If the provided value is smaller than min, pass this value through the linearSpring transformer." The linearSpring is a simple spring function that, unlike the physics or spring animations, has no concept of time. Provide it a strength and a target, and it will create a function that "attracts" any given value towards the target with the defined strength.

Replace our startDrag function with this:

const startDrag = () => pointerX(handleX.get()) .pipe(springRange(0, rangeWidth, 0.1)) .start(handleX);

We're now passing the pointer's x offset through our springRange function, so if you drag the handle past the left-most side, you'll notice it tugs back.

Applying the same to the right-most side is a matter of composing a second conditional with the first using the stand-alone pipe function:

const springRange = (min, max, strength) => pipe( conditional( v => v < min, linearSpring(strength, min) ), conditional( v => v > max, linearSpring(strength, max) ) );

Another benefit of composing a function like springRange is that it becomes very testable. The function it returns is, like all transformers, a pure function that takes a single value. You can test this function to see if it passes through values that lie within min and max unaltered, and if it applies springs to values that lie without.

If you let go of the handle while it lies outside the range, it should now spring back to within range. For that, we'll need to adjust the stopDrag function to fire a spring animation:

const stopDrag = () => { const x = handleX.get(); (x < 0 || x > rangeWidth) ? snapHandleToEnd(x) : handleX.stop(); };

Our snapHandleToEnd function looks like this:

const snapHandleToEnd = (x) => spring({ from: x, velocity: handleX.getVelocity(), to: x < 0 ? 0 : rangeWidth, damping: 30, stiffness: 5000 }).start(handleX);

You can see that to is set either as 0 or rangeWidth depending on which side of the slider the handle currently sits. By playing with damping and stiffness, you can play with a range of different spring-feels.

Momentum Scrolling

A nice touch on iOS scrubber that I always appreciated was that if you threw the handle, it would gradually slow down rather than come to a dead stop. We can replicate that easily using the decay animation.

In stopDrag, replace handleX.stop() with momentumScroll(x).

Then, on the line after the snapHandleToEnd function, add a new function called momentumScroll:

const momentumScroll = (x) => decay({ from: x, velocity: handleX.getVelocity() }).start(handleX);

Now, if you throw the handle, it will come to a gradual stop. It will also animate outside the range of the slider. We can stop this by passing the clamp transformer to the decay.pipe method:

const momentumScroll = (x) => decay({ from: x, velocity: handleX.getVelocity() }).pipe(clamp(0, rangeWidth)) .start(handleX);Conclusion

Using a combination of different Popmotion functions, we can create a scrubber that has a bit more life and playfulness than the usual.

By using pipe, we compose simple pure functions into more complex behaviours while leaving the composite pieces testable and reusable.

Next Steps

How about trying these challenges:

  • Make the momentum scroll end with a bounce if the handle hits either end of the scrubber.
  • Make the handle animate to any point on the scrubber when a user clicks on another part of the range bar.
  • Add full play controls, like a play/pause button. Update the scrubber handle position as the animation progresses.
Categories: Web Design

Introduction to Popmotion: Pointers and Physics

Wed, 05/23/2018 - 05:32

Welcome back to the Introduction to Popmotion tutorial series. In part 1, we discovered how to use tweens and keyframes to make precise, time-scheduled animations.

In Part 2, we're going to look at pointer tracking and velocity-based animations.

Pointer tracking allows us to create scrollable product shelves, custom value sliders, or drag-and-drop interfaces.

Velocity-based animations are different to a time-based animation like tween in that the primary property that affects how the animation behaves is velocity. The animation itself might take any amount of time.

We'll look at the three velocity-based animations in Popmotion, spring, decay, and physics. We'll use the velocity of the pointer tracking animation to start these animations, and that'll demonstrate how velocity-based animations can create engaging and playful UIs in a way that time-based animations simply can't.

First, open this CodePen to play along.

Pointer Tracking

Popmotion provides the pointer function to track and output the coordinates of either a mouse or single touch pointer.

Let's import this along with styler, which will allow us to set the position of the ball.

const { pointer, styler } = popmotion; const ball = document.querySelector('.ball'); const ballStyler = styler(ball);

For this example, we want to drag the ball. Let's add an event that will output the pointer's position to the ball:

let pointerTracker; const startTracking = () => { pointerTracker = pointer().start(ballStyler.set); }; ball.addEventListener('mousedown', startTracking); ball.addEventListener('touchstart', startTracking);

We'll also want some code to stop tracking when we release the ball:

const stopTracking = () => pointerTracker && pointerTracker.stop(); document.addEventListener('mouseup', stopTracking); document.addEventListener('touchend', stopTracking);

If you try and drag the ball now, there's an obvious problem. The ball jumps away when we touch it! Not a great user experience.

This is because, by default, pointer outputs the pointer's position relative to the page.

To output the pointer's position relative to another point, in this case the ball's x/y transform, we can simply pass that position to pointer like this:

const startTracking = () => { pointerTracker = pointer({ x: ballStyler.get('x'), y: ballStyler.get('y') }).start(ballStyler.set); };

Now you've made the ball, in very few lines of code, draggable! However, when the user releases the ball, it stops dead.

This isn't satisfying: Imagine a scrollable carousel of products that a user can drag to scroll. If it just stopped dead instead of momentum scrolling, it'd be less pleasurable to use.

It'd be harder, too, because the overall physical effort needed to scroll the carousel would be higher.

To enable animations like this, we first need to know the velocity of the object being thrown.

Track Velocity

Popmotion provides a function that can help us track velocity. It's called value. Let's import that:

const { pointer, styler, value } = popmotion;

To speak technically for a moment, all of Popmotion's animations are known as actions. Actions are reactive streams of values that can be started and stopped.

A value is, conversely, a reaction. It can't be stopped or started. It just passively responds when its update method is called. It can keep track of values and can be used to query their velocity.

So, after we define ballStyler, let's define a new value for ballXY:

const ballXY = value({ x: 0, y: 0 });

Whenever ballXY updates, we want to update ballStyler. We can pass a second argument to value, a function that will run whenever ballXY updates:

const ballXY = value({ x: 0, y: 0 }, ballStyler.set);

Now we can rewrite our pointer to update ballXY instead of ballStyler.set:

const startTracking = () => { pointer(ballXY.get()) .start(ballXY); };

Now, at any pointer, we can call ballXY.getVelocity() and we'll receive the velocities of both x and y, ready to plug into our velocity-based animations.

Velocity-Based Animations spring

The first velocity-based animation to introduce is spring. It's based on the same equations that govern Apple's CASpringAnimation, the spring animation behind all that iOS springy playfulness.

Import:

const { pointer, spring, styler, value } = popmotion;

Now, amend stopTracking so that instead of stopping the pointerTracker animation, it starts a spring animation like this:

const stopTracking = () => spring({ from: ballXY.get(), velocity: ballXY.getVelocity(), to: 0, stiffness: 100, damping: 20 }).start(ballXY);

We provide it with the ball's current position, its velocity, and a target, and the simulation is run. It changes depending on how the user has thrown the ball.

The cool thing about springs is they're expressive. By adjusting the mass, stiffness and damping properties, you can end up with radically different spring-feels.

For instance, if you only change the stiffness above to 1000, you can create a motion that feels like high-energy snapping. Then, by changing mass to 20, you create motion that looks almost like gravity.

There's a combination that will feel right and satisfying for your users, and appropriate to your brand, under almost any circumstance. By playing with different spring-feels, you can communicate different feelings, like a strict out-of-bounds snap or a softer affirmative bounce.

decay

The decay animation, as the name suggests, decays the provided velocity so that the animation gradually slows to a complete stop.

This can be used to create the momentum scrolling effect found on smartphones, like this:

Import the decay function:

const { decay, pointer, spring, styler, value } = popmotion;

And replace the stopTracking function with the following:

const stopTracking = () => decay({ from: ballXY.get(), velocity: ballXY.getVelocity() }).start(ballXY);

decay automatically calculates a new target based on the provided from and velocity props.

It's possible to adjust the feel of the deceleration by messing with the props outlined in the docs linked above but, unlike spring and physics, decay is designed to work out of the box. 

physics

Finally, we have the physics animation. This is Popmotion's Swiss Army knife of velocity-based animations. With it, you can simulate:

  • constant velocity
  • acceleration
  • springs
  • friction

spring and decay offer super-precise motion and a wider variety of "feels". Soon, they'll both also be scrubbable.

But both are immutable. Once you've started either, their properties are set in stone. Perfect for when we want to start an animation based on the initial from/velocity state, but not so good if we want ongoing interaction.

physics, instead, is an integrated simulation closer to that of a video game. It works by, once per frame, taking the current state and then modifying it based on the current properties at that point in time.

This allows it to be mutable, which means we can change those properties, which then changes the outcome of the simulation.

To demonstrate this, let's make a twist on classic pointer smoothing, with elastic smoothing.

Import physics:

const { pointer, spring, physics, styler, value } = popmotion;

This time, we're going to change the startTracking function. Instead of changing ballXY with pointer, we'll use physics:

const startTracking = () => { const physicsAnimation = physics({ from: ballXY.get(), to: ballXY.get(), velocity: ballXY.getVelocity(), restSpeed: false, friction: 0.6, springStrength: 400 }).start(ballXY); };

Here, we're setting from and velocity as normal. friction and springStrength both adjust the properties of the spring.

restSpeed: false overrides the default behaviour of the animation stopping when motion stops. We want to stop it manually in stopTracking.

On its own, this animation won't do anything because we set to, the spring's target, to the same as from. So let's reimplement the pointer tracking this time to change the spring target of physics. On the last line of startTracking, add:

pointerTracker = pointer(ballXY.get()).start((v) => { physicsAnimation.setSpringTarget(v); });

Here, we're using a similar pointer animation as before. Except this time, we're using it to change the target of another animation. In doing so, we create this elasticated pointer tracking:

Conclusion

Velocity-based animations paired with pointer tracking can create engaging and playful interfaces.

spring can be used to create a wide-variety of spring-feels, while decay is specifically tailored for momentum scroll animations. physics is more limited than either in terms of configurability, but also provides the opportunity to change the simulation in progress, opening new interaction possibilities.

In the next and final part of this introductory series on Popmotion, we're going to take everything we've learned in the first two parts and use them along with some light functional composition to create a scrubbable animation, along with a scrubber to do the scrubbing with!

Categories: Web Design

New Course: Connect to a Database With Laravel's Eloquent ORM

Tue, 05/22/2018 - 04:54
What You'll Be Creating

In our new course, Connect to a Database With Laravel's Eloquent ORM, you'll learn all about Eloquent, which makes it easy to connect to relational data in a database and work with it using object-oriented models in your Laravel app. It is simple to set up, easy to use, and packs a lot of power.

What You’ll Learn

In this course, Envato Tuts+ instructor Jeremy McPeak will teach you how to use Eloquent, Laravel's object-relational mapper (ORM). 

Follow along as Jeremy builds the data back-end for a simple guitar database app. You'll learn how to create data tables with migrations, how to create data models, and how to use Eloquent for querying and mutating data.

Watch the Introduction Take the Course

You can take our new course straight away with a subscription to Envato Elements. For a single low monthly fee, you get access not only to this course, but also to our growing library of over 1,000 video courses and industry-leading eBooks on Envato Tuts+. 

Plus you now get unlimited downloads from the huge Envato Elements library of 580,000+ creative assets. Create with unique fonts, photos, graphics and templates, and deliver better projects faster.

Categories: Web Design

How to Make a Real-Time Sports Application Using Node.js

Fri, 05/18/2018 - 05:32
What You'll Be Creating

In today's article I'm going to demonstrate how to make a web application that will display live game scores from the NHL. The scores will update automatically as the games progress.

This is a very exciting article for me, as it allows me the chance to bring two of my favorite passions together: development and sports.

The technologies that will be used to create the application are:

  1. Node.js
  2. Socket.io
  3. MySportsFeed.com

If you don't have Node.js installed, visit their download page now and set it up before continuing.

What Is Socket.io?

Socket.io is a technology that connects a client to a server. In this example, the client is a web browser and the server is the Node.js application. The server can have multiple clients connected to it at any given time.

Once the connection has been established, the server can send messages to all of the clients or an individual client. In return, the client can send a message to the server, allowing for bi-directional real-time communication.

Before Socket.io, web applications would commonly use AJAX, and both the client and server would poll each other looking for events. For example, every 10 seconds an AJAX call would occur to see if there were any messages to handle.

Polling for messages caused a significant amount of overhead on both the client and server as it would be constantly looking for messages when there were none.

With Socket.io, messages are received instantaneously, without needing to look for messages, reducing the overhead.

Sample Socket.io Application

Before we consume the real-time sports data, let's create an example application to demonstrate how Socket.io works.

To begin, I am going to create a new Node.js application. In a console window, I am going to navigate to C:\GitHub\NodeJS, create a new folder for my application, and create a new application:

cd \GitHub\NodeJS mkdir SocketExample cd SocketExample npm init

I used all the default settings.

Because we are making a web application, I'm going use an NPM package called Express to simplify the setup. In a command prompt, install it as follows: npm install express --save

And of course we will need to install the Socket.io package: npm install socket.io --save

Let's begin by creating the web server. Create a new file called index.js and place the following code within it to create the web server using Express:

var app = require('express')(); var http = require('http').Server(app); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); http.listen(3000, function(){ console.log('HTTP server started on port 3000'); });

If you are not familiar with Express, the above code example includes the Express library and creates a new HTTP server. In this example, the HTTP server is listening on port 3000, e.g. http://localhost:3000. A route is created at the root of the site "/". The result of the route returns an HTML file: index.html.

Before we create the index.html file, let's finish the server by setting up Socket.io. Append the following to your index.js file to create the Socket server:

var io = require('socket.io')(http); io.on('connection', function(socket){ console.log('Client connection received'); });

Similar to Express, the code begins by importing the Socket.io library. This is stored in a variable called io. Next, using the io variable, an event handler is created with the on function. The event being listened for is connection. This event is called each time a client connects to the server.

Let's now create our very basic client. Create a new file called index.html and place the following code within:

<!doctype html> <html> <head> <title>Socket.IO Example</title> </head> <body> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script> </body> </html>

The HTML above loads the Socket.io client JavaScript and initializes a connection to the server. To see the example, start your Node application: node index.js

Then, in your browser, navigate to http://localhost:3000. Nothing will appear on the page; however, if you look at the console where the Node application is running, two messages are logged:

  1. HTTP server started on port 3000
  2. Client connection received

Now that we have a successful socket connection, let's put it to use. Let's begin by sending a message from the server to the client. Then, when the client receives the message, it can send a response back to the server.

Let's look at the abbreviated index.js file:

io.on('connection', function(socket){ console.log('Client connection received'); socket.emit('sendToClient', { hello: 'world' }); socket.on('receivedFromClient', function (data) { console.log(data); }); });

The previous io.on function has been updated to include a few new lines of code. The first, socket.emit, sends the message to the client. The sendToClient is the name of the event. By naming events, you can send different types of messages so the client can interpret them differently. The second addition is the socket.on, which also contains an event name: receivedFromClient. This creates a function that accepts data from the client. In this case, the data is logged to the console window.

That completes the server-side amendments; it can now send and receive data from any connected clients.

Let's complete this example by updating the client to receive the sendToClient event. When it receives the event, it can respond with the receivedFromClient event back to the server.

This is accomplished in the JavaScript portion of the HTML, so in the index.html file, I have updated the JavaScript as follows:

var socket = io(); socket.on('sendToClient', function (data) { console.log(data); socket.emit('receivedFromClient', { my: 'data' }); });

Using the instantiated socket variable, we have very similar logic on the server with a socket.on function. For the client, it is listening to the sendToClient event. As soon as the client is connected, the server sends this message. When the client receives it, it is logged to the console in the browser. The client then uses the same socket.emit that the server used to send the original event. In this instance, the client sends back the receivedFromClient event to the server. When the server receives the message, it is logged to the console window.

Try it out for yourself. First, in a console, run your Node application: node index.js. Then load http://localhost:3000 in your browser.

Check the web browser console and you should see the following JSON data logged: {hello: "world"}

Then, in the command prompt where the Node application is running, you should see the following:

HTTP server started on port 3000 Client connection received { my: 'data' }

Both the client and server can use the JSON data received to perform specific tasks. We will learn more about that once we connect to the real-time sports data.

Sports Data

Now that we have mastered how to send and receive data to and from the client and server, this can be leveraged to provide real-time updates. I chose to use sports data, although the same theory is not limited to sports. Before I began this project, I researched different sports data. The one I settled on, because they offer free developer accounts, was MySportsFeeds (I am not affiliated with them in any way). To access the real-time data, I signed up for an account and then made a small donation. Donations start at $1 to have data updated every 10 minutes. This will be good for the example.

Once your account is set up, you can proceed to setting up access to their API. To assist with this, I am going to use their NPM package: npm install mysportsfeeds-node --save

After the package has been installed, API calls can be made as follows:

var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true); msf.authenticate("********", "*********"); var today = new Date(); msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true });

In the example above, be sure to replace the call to the authenticate function with your username and password.

The following code executes an API call to the get the NHL scoreboard for today. The fordate variable is what specifies today. I've also set force to true so that a response is always returned, even when the data has not changed.

With the current setup, the results of the API call get written to a text file. In the final example, this will be changed; however, for demonstration purposes, the results file can be reviewed in a text editor to understand the contents of the response. The results contain a scoreboard object. This object contains an array called gameScore. This object stores the result of each game. Each object contains a child object called game. This object provides the information about who is playing.

Outside of the game object, there are a handful of variables that provide the current state of the game. The data changes based on the state of the game. For example, when the game hasn't started, there are only a few variables that tell us the game is not in progress and has not started.

When the game is in progress, additional data is provided about the score, what period the game is in, and how much time is remaining. We will see this in action when we get to the HTML to show the game in the next section.

Real-Time Updates

We have all the pieces to the puzzle, so it is now time to put the puzzle together to reveal the final picture. Currently, MySportsFeeds has limited support for pushing data to us, so we will have to poll the data from them. Luckily, we know the data only changes once every 10 minutes, so we don't need to add overhead by polling for changes too frequently. Once we poll the data from them, we can push those updates from the server to all clients connected.

To perform the polling, I will use the JavaScript setInterval function to call the API (in my case) every 10 minutes to look for updates. When the data is received, an event is sent to all of the connected clients. When the clients receive the event, the game scores will be updated with JavaScript in the web browser.

MySportsFeeds will also be called when the Node application first starts up. This data will be used for any clients who connect before the first 10-minute interval. This is stored in a global variable. This same global variable is updated as part of the interval polling. This will ensure that when any new clients connect after the polling, they will have the latest data.

To assist with some code cleanliness in the main index.js file, I have created a new file called data.js. This file will contain a function that is exported (available in the index.js file) that performs the previous call to the MySportsFeeds API. Here are the full contents of that file:

var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true, null); msf.authenticate("*******", "******"); var today = new Date(); exports.getData = function() { return msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true }); };

A getData function is exported and returns the result of the call, which in this case is a Promise that will be resolved in the main application.

Now let's look at the final contents of the index.js file:

var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); var data = require('./data.js'); // Global variable to store the latest NHL results var latestData; // Load the NHL data for when client's first connect // This will be updated every 10 minutes data.getData().then((result) => { latestData = result; }); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); http.listen(3000, function(){ console.log('HTTP server started on port 3000'); }); io.on('connection', function(socket){ // when clients connect, send the latest data socket.emit('data', latestData); }); // refresh data setInterval(function() { data.getData().then((result) => { // Update latest results for when new client's connect latestData = result; // send it to all connected clients io.emit('data', result); console.log('Last updated: ' + new Date()); }); }, 300000);

The first seven lines of code above instantiate the required libraries and the global latestData variable. The final list of libraries used are: Express, Http Server created with Express, Socket.io, and the aforementioned data.js file just created.

With the necessities taken care of, the application populates the latestData for clients who will connect when the server is first started:

// Global variable to store the latest NHL results var latestData; // Load the NHL data for when client's first connect // This will be updated every 10 minutes data.getData().then((result) => { latestData = result; });

The next few lines set up a route for the root page of the website (http://localhost:3000/) and start the HTTP server to listen on port 3000.

Next, the Socket.io is set up to look for connections. When a new connection is received, the server emits an event called data with the contents of the latestData variable.

And finally, the final chunk of code creates the polling interval. When the interval occurs, the latestData variable is updated with the results of the API call. This data then emits the same data event to all clients.

// refresh data setInterval(function() { data.getData().then((result) => { // Update latest results for when new client's connect latestData = result; // send it to all connected clients io.emit('data', result); console.log('Last updated: ' + new Date()); }); }, 300000);

You may notice that when the client connects and an event is emitted, it is emitting the event with the socket variable. This approach will send the event to that connected client only. Inside the interval, the global io is used to emit the event. This will send the event to all clients.

That completes the server. Let's work on the client front-end. In an earlier example, I created a basic index.html file that set up the client connection that would log events from the server and send one back. I am going to extend that file to contain the completed example.

Because the server is sending us a JSON object, I am going to use jQuery and leverage a jQuery extension called JsRender. This is a templating library. It will allow me to create a template with HTML that will be used to display the contents of each NHL game in an easy-to-use, consistent manner. In a moment, you will see the power of this library. The final code is over 40 lines of code, so I am going to break it down into smaller chunks, and then display the full HTML together at the end.

This first part creates the template that will be used to show the game data:

<script id="gameTemplate" type="text/x-jsrender"> <div class="game"> <div> {{:game.awayTeam.City}} {{:game.awayTeam.Name}} at {{:game.homeTeam.City}} {{:game.homeTeam.Name}} </div> <div> {{if isUnplayed == "true" }} Game starts at {{:game.time}} {{else isCompleted == "false"}} <div>Current Score: {{:awayScore}} - {{:homeScore}}</div> <div> {{if currentIntermission}} {{:~ordinal_suffix_of(currentIntermission)}} Intermission {{else currentPeriod}} {{:~ordinal_suffix_of(currentPeriod)}}<br/> {{:~time_left(currentPeriodSecondsRemaining)}} {{else}} 1st {{/if}} </div> {{else}} Final Score: {{:awayScore}} - {{:homeScore}} {{/if}} </div> </div> </script>

The template is defined using a script tag. It contains the id of the template and a special script type called text/x-jsrender. The template defines a container div for each game that contains a class game to apply some basic styling. Inside this div, the templating begins.

In the next div, the away and home team are displayed. This is done by concatenating the city and team name together from the game object from the MySportsFeed data.

{{:game.awayTeam.City}} is how I define an object that will be replaced with a physical value when the template is rendered. This syntax is defined by the JsRender library.

Once the teams are displayed, the next chunk of code does some conditional logic. When the game is unPlayed, a string will be outputted that the game will start at {{:game.time}}.

When the game is not completed, the current score is displayed: Current Score: {{:awayScore}} - {{:homeScore}}. And finally, some tricky little logic to identify what period the hockey game is in or if it is in intermission.

If the variable currentIntermission is provided in the results, then I use a function I defined called ordinal_suffix_of, which will convert the period number to read: 1st (2nd, 3rd, etc.) Intermission.

When it is not in intermission, I look for the currentPeriod value. This also uses the ordinal_suffix_of  to show that the game is in the 1st (2nd, 3rd, etc.) period.

Beneath this, another function I defined called time_left is used to convert the number of seconds remaining into the number of minutes and seconds remaining in the period. For example: 10:12.

The final part of the code displays the final score because we know the game has completed.

Here is an example of what it looks like when there is a mix of finished games, in progress games, and games that have not started yet (I'm not a very good designer, so it looks as you would expect when a developer makes their own User Interface).

Next up is a chunk of JavaScript that creates the socket, the helper functions ordinal_suffix_of and time_left, and a variable that references the jQuery template created.

<script> var socket = io(); var tmpl = $.templates("#gameTemplate"); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; </script>

The final piece of code is the code to receive the socket event and render the template:

socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); });

I have a placeholder div with the id of data. The result of the template rendering (tmpl.render) writes the HTML to this container. What is really neat is that the JsRender library can accept an array of data, in this case data.scoreboard.gameScore, that iterates through each element in the array and creates one game per element.

Here is the final HTML and JavaScript all together:

<!doctype html> <html> <head> <title>Socket.IO Example</title> </head> <body> <div id="data"> </div> <script id="gameTemplate" type="text/x-jsrender"> <div class="game"> <div> {{:game.awayTeam.City}} {{:game.awayTeam.Name}} at {{:game.homeTeam.City}} {{:game.homeTeam.Name}} </div> <div> {{if isUnplayed == "true" }} Game starts at {{:game.time}} {{else isCompleted == "false"}} <div>Current Score: {{:awayScore}} - {{:homeScore}}</div> <div> {{if currentIntermission}} {{:~ordinal_suffix_of(currentIntermission)}} Intermission {{else currentPeriod}} {{:~ordinal_suffix_of(currentPeriod)}}<br/> {{:~time_left(currentPeriodSecondsRemaining)}} {{else}} 1st {{/if}} </div> {{else}} Final Score: {{:awayScore}} - {{:homeScore}} {{/if}} </div> </div> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; var tmpl = $.templates("#gameTemplate"); socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); }); </script> <style> .game { border: 1px solid #000; float: left; margin: 1%; padding: 1em; width: 25%; } </style> </body> </html>

Start the Node application and browse to http://localhost:3000 to see the results for yourself!

Every X minutes, the server will send an event to the client. The client will redraw the game elements with the updated data. So when you leave the site open and periodically look at it, you will see the game data refresh when games are currently in progress.

Conclusion

The final product uses Socket.io to create a server that clients connect to. The server fetches data and sends it to the client. When the client receives the data, it can seamlessly update the display. This reduces load on the server because the client only performs work when it receives an event from the server.

Sockets are not limited to one direction; the client can also send messages to the server. When the server receives the message, it can perform some processing.

Chat applications would commonly work this way. The server would receive a message from the client and then broadcast to all connected clients to show that someone has sent a new message.

Hopefully you enjoyed this article as I had a blast creating this real-time sports application for one of my favorite sports!

Categories: Web Design

How to Create a Custom Settings Panel in WooCommerce

Fri, 05/11/2018 - 05:29
What You'll Be Creating

WooCommerce is by far the leading ecommerce plugin for WordPress. At the time of writing, it has over 3 million active installations and is reportedly behind over 40% of all online stores.

One of the reasons for WooCommerce's popularity is its extendability. Like WordPress itself, WooCommerce is packed full of actions and filters that developers can hook into if they want to extend WooCommerce's default functionality.

A great example of this is the ability to create a custom data panel.

What's Covered in This Tutorial?

This tutorial is split into two parts. In part one, we're going to be looking at:

  • adding a custom panel to WooCommerce
  • adding custom fields to the panel
  • sanitizing and saving custom field values

Then in part two, we'll look at:

  • displaying custom fields on the product page
  • changing the product price depending on the value of custom fields
  • displaying custom field values in the cart and order
What Is a WooCommerce Custom Data Panel?

When you create a new product in WooCommerce, you enter most of the critical product information, like price and inventory, in the Product data section.

In the screenshot above, you can see that the Product data section is divided into panels: the tabs down the left, e.g. General, Inventory, etc., each open different panels in the main view on the right.

In this tutorial, we're going to look at creating a custom panel for product data and adding some custom fields to it. Then we'll look at using those custom fields on the front end and saving their values to customer orders.

In our example scenario, we're going to add a 'Giftwrap' panel which contains some custom fields:

  • a checkbox to include a giftwrapping option for the product on the product page
  • a checkbox to enable an input field where a customer can enter a message on the product page
  • an input field to add a price for the giftwrapping option; the price will be added to the product price in the cart

In the back end, it's going to look like this:

And on the front end, it will look something like this:

Create a New Plugin

Because we're extending functionality, we're going to create a plugin rather than adding our code to a theme. That means that our users will be able to retain this extra functionality even if they switch their site's theme. Creating a plugin is out of scope for this tutorial, but if you need some help, take a look at this Tuts+ Coffee Break Course on creating your first plugin: 

Our plugin is going to consist of two classes: one to handle stuff in the admin, and one to handle everything on the front end. Our plugin file structure is going to look like this:

Admin Class

First up, we need to create our class to handle everything on the back end. In a folder called classes, create a new file called class-tpwcp-admin.php.

This class will handle the following:

  • Create the custom tab (the tab is the clickable element down the left of the Product data section).
  • Add the custom fields to the custom panel (the panel is the element that's displayed when you click a tab).
  • Decide the product types where the panel will be enabled.
  • Sanitize and save the custom field values.

Paste the following code into that new file. We'll walk through it step by step afterwards.

<?php /** * Class to create additional product panel in admin * @package TPWCP */ // Exit if accessed directly if( ! defined( 'ABSPATH' ) ) { exit; } if( ! class_exists( 'TPWCP_Admin' ) ) { class TPWCP_Admin { public function __construct() { } public function init() { // Create the custom tab add_filter( 'woocommerce_product_data_tabs', array( $this, 'create_giftwrap_tab' ) ); // Add the custom fields add_action( 'woocommerce_product_data_panels', array( $this, 'display_giftwrap_fields' ) ); // Save the custom fields add_action( 'woocommerce_process_product_meta', array( $this, 'save_fields' ) ); } /** * Add the new tab to the $tabs array * @see https://github.com/woocommerce/woocommerce/blob/e1a82a412773c932e76b855a97bd5ce9dedf9c44/includes/admin/meta-boxes/class-wc-meta-box-product-data.php * @param $tabs * @since 1.0.0 */ public function create_giftwrap_tab( $tabs ) { $tabs['giftwrap'] = array( 'label' => __( 'Giftwrap', 'tpwcp' ), // The name of your panel 'target' => 'gifwrap_panel', // Will be used to create an anchor link so needs to be unique 'class' => array( 'giftwrap_tab', 'show_if_simple', 'show_if_variable' ), // Class for your panel tab - helps hide/show depending on product type 'priority' => 80, // Where your panel will appear. By default, 70 is last item ); return $tabs; } /** * Display fields for the new panel * @see https://docs.woocommerce.com/wc-apidocs/source-function-woocommerce_wp_checkbox.html * @since 1.0.0 */ public function display_giftwrap_fields() { ?> <div id='gifwrap_panel' class='panel woocommerce_options_panel'> <div class="options_group"> <?php woocommerce_wp_checkbox( array( 'id' => 'include_giftwrap_option', 'label' => __( 'Include giftwrap option', 'tpwcp' ), 'desc_tip' => __( 'Select this option to show giftwrapping options for this product', 'tpwcp' ) ) ); woocommerce_wp_checkbox( array( 'id' => 'include_custom_message', 'label' => __( 'Include custom message', 'tpwcp' ), 'desc_tip' => __( 'Select this option to allow customers to include a custom message', 'tpwcp' ) ) ); woocommerce_wp_text_input( array( 'id' => 'giftwrap_cost', 'label' => __( 'Giftwrap cost', 'tpwcp' ), 'type' => 'number', 'desc_tip' => __( 'Enter the cost of giftwrapping this product', 'tpwcp' ) ) ); ?> </div> </div> <?php } /** * Save the custom fields using CRUD method * @param $post_id * @since 1.0.0 */ public function save_fields( $post_id ) { $product = wc_get_product( $post_id ); // Save the include_giftwrap_option setting $include_giftwrap_option = isset( $_POST['include_giftwrap_option'] ) ? 'yes' : 'no'; // update_post_meta( $post_id, 'include_giftwrap_option', sanitize_text_field( $include_giftwrap_option ) ); $product->update_meta_data( 'include_giftwrap_option', sanitize_text_field( $include_giftwrap_option ) ); // Save the include_giftwrap_option setting $include_custom_message = isset( $_POST['include_custom_message'] ) ? 'yes' : 'no'; $product->update_meta_data( 'include_custom_message', sanitize_text_field( $include_custom_message ) ); // Save the giftwrap_cost setting $giftwrap_cost = isset( $_POST['giftwrap_cost'] ) ? $_POST['giftwrap_cost'] : ''; $product->update_meta_data( 'giftwrap_cost', sanitize_text_field( $giftwrap_cost ) ); $product->save(); } } } Create the Custom Tab

To create the custom tab, we hook into the woocommerce_product_data_tabs filter using our create_giftwrap_tab function. This passes the WooCommerce $tabs object in, which we then modify using the following parameters:

  • label: use this to define the name of your tab.
  • target: this is used to create an anchor link so needs to be unique.
  • class: an array of classes that will be applied to your panel.
  • priority: define where you want your tab to appear.

Product Types

At this stage, it's worth considering what product types we'd like our panel to be enabled for. By default, there are four WooCommerce product types: simple, variable, grouped, and affiliate. Let's say for our example scenario, we only want our Giftwrap panel to be enabled for simple and variable product types.

To achieve this, we add the show_if_simple and show_if_variable classes to the class parameter above. If we didn't want to enable the panel for variable product types, we'd just omit the show_if_variable class.

Add Custom Fields

The next hook we use is woocommerce_product_data_panels. This action allows us to output our own markup for the Giftwrap panel. In our class, the function display_giftwrap_fields creates a couple of div wrappers, inside which we use some WooCommerce functions to create custom fields. 

Note how the id attribute for our outer div, giftwrap_panel, matches the value we passed into the target parameter of our giftwrap tab above. This is how WooCommerce will know to display this panel when we click the Giftwrap tab.

WooCommerce Custom Field Functions

In our example, the two functions we're using to create our fields are:

  • woocommerce_wp_checkbox
  • woocommerce_wp_text_input

These functions are provided by WooCommerce specifically for the purpose of creating custom fields. They take an array of arguments, including:

  • id: this is the ID of your field. It needs to be unique, and we'll be referencing it later in our code.
  • label: this is the label as it will appear to the user.
  • desc_tip: this is the optional tool tip that appears when the user hovers over the question mark icon next to the label.

Note also that the woocommerce_wp_text_input function also takes a type argument, where you can specify number for a number input field, or text for a text input field. Our field will be used to input a price, so we specify it as number.

Save the Custom Fields

The final part of our admin class uses the woocommerce_process_product_meta action to save our custom field values.

In order to standardize and optimize how it stores and retrieves data, WooCommerce 3.0 adopted a CRUD (Create, Read, Update, Delete) method for setting and getting product data. You can find out more about the thinking behind this in the WooCommerce 3.0 announcement.

With this in mind, instead of the more familiar get_post_meta and update_post_meta methods that we might have used in the past, we now use the $post_id to create a WooCommerce $product object, and then apply the update_meta_data method to save data. For example:

$product = wc_get_product( $post_id ); $include_giftwrap_option = isset( $_POST['include_giftwrap_option'] ) ? 'yes' : 'no'; $product->update_meta_data( 'include_giftwrap_option', sanitize_text_field( $include_giftwrap_option ) ); $product->save();

Please note also that we're careful to sanitize our data before saving it to the database. There's more information on sanitizing data here: 

Main Plugin File

When you've created your readme.txt file and your main plugin file tutsplus-woocommerce-panel.php, you can add this code to your main file.

<?php /** * Plugin Name: Tutsplus WooCommerce Panel * Description: Add a giftwrap panel to WooCommerce products * Version: 1.0.0 * Author: Gareth Harris * Author URI: https://catapultthemes.com/ * Text Domain: tpwcp * WC requires at least: 3.2.0 * WC tested up to: 3.3.0 * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt */ // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Define constants */ if ( ! defined( 'TPWCP_PLUGIN_VERSION' ) ) { define( 'TPWCP_PLUGIN_VERSION', '1.0.0' ); } if ( ! defined( 'TPWCP_PLUGIN_DIR_PATH' ) ) { define( 'TPWCP_PLUGIN_DIR_PATH', plugin_dir_path( __FILE__ ) ); } require( TPWCP_PLUGIN_DIR_PATH . '/classes/class-tpwcp-admin.php' ); /** * Start the plugin. */ function tpwcp_init() { if ( is_admin() ) { $TPWCP = new TPWCP_Admin(); $TPWCP->init(); } } add_action( 'plugins_loaded', 'tpwcp_init' );

This will initiate your admin class.

When you activate your plugin on a site (along with WooCommerce) and then go to create a new product, you'll see your new Giftwrap panel available, along with custom fields. You can update the fields and save them... But you won't see anything on the front end yet.

Conclusion

Let's just recap what we've looked at so far in this article.

We've looked at an example scenario for adding a custom 'Giftwrap' panel to WooCommerce. We've created a plugin and added a class to create the panel. Within the class, we've also used WooCommerce functions to add custom fields, and then we've sanitized and saved those field values.

Categories: Web Design

How Laravel Broadcasting Works

Mon, 05/07/2018 - 05:26

Today, we are going to explore the concept of broadcasting in the Laravel web framework. It allows you to send notifications to the client side when something happens on the server side. In this article, we are going to use the third-party Pusher library to send notifications to the client side.

If you have ever wanted to send notifications from the server to the client when something happens on a server in Laravel, you're looking for the broadcasting feature.

For example, let's assume that you've implemented a messaging application that allows users of your system to send messages to each other. Now, when user A sends a message to user B, you want to notify user B in real time. You may display a popup or an alert box that informs user B about the new message!

It's the perfect use-case to walk through the concept of broadcasting in Laravel, and that's what we'll implement in this article.

If you are wondering how the server could send notifications to the client, it's using sockets under the hood to accomplish it. Let's understand the basic flow of sockets before we dive deeper into the actual implementation.

  • Firstly, you need a server that supports the web-sockets protocol and allows the client to establish a web socket connection.
  • You could implement your own server or use a third-party service like Pusher. We'll prefer the latter in this article.
  • The client initiates a web socket connection to the web socket server and receives a unique identifier upon successful connection.
  • Once the connection is successful, the client subscribes to certain channels at which it would like to receive events.
  • Finally, under the subscribed channel, the client registers events that it would like to listen to.
  • Now on the server side, when a particular event happens, we inform the web-socket server by providing it with the channel name and event name.
  • And finally, the web-socket server broadcasts that event to registered clients on that particular channel.

Don't worry if it looks like too much in a single go; you will get the hang of it as we move through this article.

Next, let's have a look at the default broadcast configuration file at config/broadcasting.php.

<?php return [ /* |-------------------------------------------------------------------------- | Default Broadcaster |-------------------------------------------------------------------------- | | This option controls the default broadcaster that will be used by the | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | | Supported: "pusher", "redis", "log", "null" | */ 'default' => env('BROADCAST_DRIVER', 'log'), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ 'connections' => [ 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], 'log' => [ 'driver' => 'log', ], 'null' => [ 'driver' => 'null', ], ], ];

By default, Laravel supports multiple broadcast adapters in the core itself.

In this article, we are going to use the Pusher broadcast adapter. For debugging purposes, you could also use the log adapter. Of course, if you're using the log adapter, the client won't receive any event notifications, and it'll only be logged to the laravel.log file.

From the next section onward, we'll right away dive into the actual implementation of the aforementioned use-case.

Setting Up the Prerequisites

In broadcasting, there are different types of channels—public, private, and presence. When you want to broadcast your events publicly, it's the public channel that you are supposed to use. Conversely, the private channel is used when you want to restrict event notifications to certain private channels.

In our use-case, we want to notify users when they get a new message. And to be eligible to receive broadcast notifications, the user must be logged in. Thus, we'll need to use the private channel in our case.

Core Authentication Feature

Firstly, you need to enable the default Laravel authentication system so that features like registration, login and the like work out of the box. If you're not sure how to do that, the official documentation provides a quick insight into that.

Pusher SDK—Installation and Configuration

As we're going to use the Pusher third-party service as our web-socket server, you need to create an account with it and make sure you have the necessary API credentials with your post registration. If you're facing any trouble creating it, don't hesitate to ask me in the comment section.

Next, we need to install the Pusher PHP SDK so that our Laravel application can send broadcast notifications to the Pusher web-socket server.

In your Laravel application root, run the following command to install it as a composer package.

$composer require pusher/pusher-php-server "~3.0"

Now, let's change the broadcast configuration file to enable the Pusher adapter as our default broadcast driver.

<?php return [ /* |-------------------------------------------------------------------------- | Default Broadcaster |-------------------------------------------------------------------------- | | This option controls the default broadcaster that will be used by the | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | | Supported: "pusher", "redis", "log", "null" | */ 'default' => env('BROADCAST_DRIVER', 'pusher'), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ 'connections' => [ 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => 'ap2', 'encrypted' => true ], ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], 'log' => [ 'driver' => 'log', ], 'null' => [ 'driver' => 'null', ], ], ];

As you can see, we've changed the default broadcast driver to Pusher. We've also added cluster and encrypted configuration options that you should have got from the Pusher account in the first place.

Also, it's fetching values from environment variables. So let's make sure that we do set the following variables in the .env file properly.

BROADCAST_DRIVER=pusher PUSHER_APP_ID={YOUR_APP_ID} PUSHER_APP_KEY={YOUR_APP_KEY} PUSHER_APP_SECRET={YOUR_APP_SECRET}

Next, I had to make a few changes in a couple of core Laravel files in order to make it compatible with the latest Pusher SDK. Of course, I don't recommend making any changes in the core framework, but I'll just highlight what needs to be done.

Go ahead and open the vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php file. Just replace the snippet use Pusher; with use Pusher\Pusher;.

Next, let's open the vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php file and make a similar change in the following snippet.

return new PusherBroadcaster( new \Pusher\Pusher($config['key'], $config['secret'], $config['app_id'], Arr::get($config, 'options', [])) );

Finally, let's enable the broadcast service in config/app.php by removing the comment in the following line.

App\Providers\BroadcastServiceProvider::class,

So far, we've installed server-specific libraries. In the next section, we'll go through client libraries that need to be installed as well.

Pusher and Laravel Echo Libraries—Installation and Configuration

In broadcasting, the responsibility of the client side is to subscribe to channels and listen for desired events. Under the hood, it accomplishes it by opening a new connection to the web-socket server.

Luckily, we don't have to implement any complex JavaScript stuff to achieve it as Laravel already provides a useful client library, Laravel Echo, that helps us deal with sockets on the client side. Also, it supports the Pusher service that we're going to use in this article.

You can install Laravel Echo using the NPM package manager. Of course, you need to install node and npm in the first place if you don't have them already. The rest is pretty simple, as shown in the following snippet.

$npm install laravel-echo

What we're interested in is the node_modules/laravel-echo/dist/echo.js file that you should copy to public/echo.js.

Yes, I understand, it's a bit of overkill to just get a single JavaScript file. If you don't want to go through this exercise, you can download the echo.js file from my GitHub.

And with that, we're done with our client libraries setup.

Back-End File Setup

Recall that we were talking about setting up an application that allows users of our application to send messages to each other. On the other hand, we'll send broadcast notifications to users that are logged in when they receive a new message from other users.

In this section, we'll create the files that are required in order to implement the use-case that we're looking for.

To start with, let's create the Message model that holds messages sent by users to each other.

$php artisan make:model Message --migration

We also need to add a few fields like to, from and message to our messages table. So let's change the migration file before running the migrate command.

<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMessagesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('messages', function (Blueprint $table) { $table->increments('id'); $table->integer('from', FALSE, TRUE); $table->integer('to', FALSE, TRUE); $table->text('message'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('messages'); } }

Now, let's run the migrate command that creates the messages table in the database.

$php artisan migrate

Whenever you want to raise a custom event in Laravel, you should create a class for that event. Based on the type of event, Laravel reacts accordingly and takes the necessary actions.

If the event is a normal event, Laravel calls the associated listener classes. On the other hand, if the event is of broadcast type, Laravel sends that event to the web-socket server that's configured in the config/broadcasting.php file.

As we're using the Pusher service in our example, Laravel will send events to the Pusher server.

Let's use the following artisan command to create a custom event class—NewMessageNotification.

$php artisan make:event NewMessageNotification

That should create the app/Events/NewMessageNotification.php class. Let's replace the contents of that file with the following.

<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use App\Message; class NewMessageNotification implements ShouldBroadcastNow { use SerializesModels; public $message; /** * Create a new event instance. * * @return void */ public function __construct(Message $message) { $this->message = $message; } /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel('user.'.$this->message->to); } }

The important thing to note is that the NewMessageNotification class implements the ShouldBroadcastNow interface. Thus, when we raise an event, Laravel knows that this event should be broadcast.

In fact, you could also implement the ShouldBroadcast interface, and Laravel adds an event into the event queue. It'll be processed by the event queue worker when it gets a chance to do so. In our case, we want to broadcast it right away, and that's why we've used the ShouldBroadcastNow interface.

In our case, we want to display a message the user has received, and thus we've passed the Message model in the constructor argument. In this way, the data will be passed along with the event.

Next, there is the broadcastOn method that defines the name of the channel on which the event will be broadcast. In our case, we've used the private channel as we want to restrict the event broadcast to logged-in users.

The $this->message->to variable refers to the ID of the user to which the event will be broadcast. Thus, it effectively makes the channel name like user.{USER_ID}.

In the case of private channels, the client must authenticate itself before establishing a connection with the web-socket server. It makes sure that events that are broadcast on private channels are sent to authenticated clients only. In our case, it means that only logged-in users will be able to subscribe to our channel user.{USER_ID}.

If you're using the Laravel Echo client library for channel subscription, you're in luck! It automatically takes care of the authentication part, and you just need to define the channel routes.

Let's go ahead and add a route for our private channel in the routes/channels.php file.

<?php /* |-------------------------------------------------------------------------- | Broadcast Channels |-------------------------------------------------------------------------- | | Here you may register all of the event broadcasting channels that your | application supports. The given channel authorization callbacks are | used to check if an authenticated user can listen to the channel. | */ Broadcast::channel('App.User.{id}', function ($user, $id) { return (int) $user->id === (int) $id; }); Broadcast::channel('user.{toUserId}', function ($user, $toUserId) { return $user->id == $toUserId; });

As you can see, we've defined the user.{toUserId} route for our private channel.

The second argument of the channel method should be a closure function. Laravel automatically passes the currently logged-in user as the first argument of the closure function, and the second argument is usually fetched from the channel name.

When the client tries to subscribe to the private channel user.{USER_ID}, the Laravel Echo library does the necessary authentication in the background using the XMLHttpRequest object, or more commonly known as XHR.

So far, we've finished with the setup, so let's go ahead and test it.

Front-End File Setup

In this section, we'll create the files that are required to test our use-case.

Let's go ahead and create a controller file at app/Http/Controllers/MessageController.php with the following contents.

<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Message; use App\Events\NewMessageNotification; use Illuminate\Support\Facades\Auth; class MessageController extends Controller { public function __construct() { $this->middleware('auth'); } public function index() { $user_id = Auth::user()->id; $data = array('user_id' => $user_id); return view('broadcast', $data); } public function send() { // ... // message is being sent $message = new Message; $message->setAttribute('from', 1); $message->setAttribute('to', 2); $message->setAttribute('message', 'Demo message from user 1 to user 2'); $message->save(); // want to broadcast NewMessageNotification event event(new NewMessageNotification($message)); // ... } }

In the index method, we're using the broadcast view, so let's create the resources/views/broadcast.blade.php view file as well.

<!DOCTYPE html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>Test</title> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <!-- Collapsed Hamburger --> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse"> <span class="sr-only">Toggle Navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <!-- Branding Image --> <a class="navbar-brand123" href="{{ url('/') }}"> Test </a> </div> <div class="collapse navbar-collapse" id="app-navbar-collapse"> <!-- Left Side Of Navbar --> <ul class="nav navbar-nav"> &nbsp; </ul> <!-- Right Side Of Navbar --> <ul class="nav navbar-nav navbar-right"> <!-- Authentication Links --> @if (Auth::guest()) <li><a href="{{ route('login') }}">Login</a></li> <li><a href="{{ route('register') }}">Register</a></li> @else <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> {{ Auth::user()->name }} <span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> <li> <a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> Logout </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> {{ csrf_field() }} </form> </li> </ul> </li> @endif </ul> </div> </div> </nav> <div class="content"> <div class="m-b-md"> New notification will be alerted realtime! </div> </div> </div> <!-- receive notifications --> <script src="{{ asset('js/echo.js') }}"></script> <script src="https://js.pusher.com/4.1/pusher.min.js"></script> <script> Pusher.logToConsole = true; window.Echo = new Echo({ broadcaster: 'pusher', key: 'c91c1b7e8c6ece46053b', cluster: 'ap2', encrypted: true, logToConsole: true }); Echo.private('user.{{ $user_id }}') .listen('NewMessageNotification', (e) => { alert(e.message.message); }); </script> <!-- receive notifications --> </body> </html>

And, of course, we need to add routes as well in the routes/web.php file.

Route::get('message/index', 'MessageController@index'); Route::get('message/send', 'MessageController@send');

In the constructor method of the controller class, you can see that we've used the auth middleware to make sure that controller methods are only accessed by logged-in users.

Next, there's the index method that renders the broadcast view. Let's pull in the most important code in the view file.

<!-- receive notifications --> <script src="{{ asset('js/echo.js') }}"></script> <script src="https://js.pusher.com/4.1/pusher.min.js"></script> <script> Pusher.logToConsole = true; window.Echo = new Echo({ broadcaster: 'pusher', key: 'c91c1b7e8c6ece46053b', cluster: 'ap2', encrypted: true, logToConsole: true }); Echo.private('user.{{ $user_id }}') .listen('NewMessageNotification', (e) => { alert(e.message.message); }); </script> <!-- receive notifications -->

Firstly, we load the necessary client libraries, Laravel Echo and Pusher, allowing us to open the web-socket connection to the Pusher web-socket server.

Next, we create the instance of Echo by providing Pusher as our broadcast adapter and other necessary Pusher-related information.

Moving further, we use the private method of Echo to subscribe to the private channel user.{USER_ID}. As we discussed earlier, the client must authenticate itself before subscribing to the private channel. Thus the Echo object performs the necessary authentication by sending the XHR in the background with necessary parameters. Finally, Laravel tries to find the user.{USER_ID} route, and it should match the route that we've defined in the routes/channels.php file.

If everything goes fine, you should have a web-socket connection open with the Pusher web-socket server, and it's listing events on the user.{USER_ID} channel! From now on, we'll be able to receive all incoming events on this channel.

In our case, we want to listen for the NewMessageNotification event and thus we've used the listen method of the Echo object to achieve it. To keep things simple, we'll just alert the message that we've received from the Pusher server.

So that was the setup for receiving events from the web-sockets server. Next, we'll go through the send method in the controller file that raises the broadcast event.

Let's quickly pull in the code of the send method.

public function send() { // ... // message is being sent $message = new Message; $message->setAttribute('from', 1); $message->setAttribute('to', 2); $message->setAttribute('message', 'Demo message from user 1 to user 2'); $message->save(); // want to broadcast NewMessageNotification event event(new NewMessageNotification($message)); // ... }

In our case, we're going to notify logged-in users when they receive a new message. So we've tried to mimic that behavior in the send method.

Next, we've used the event helper function to raise the NewMessageNotification event. Since the NewMessageNotification event is of ShouldBroadcastNow type, Laravel loads the default broadcast configuration from the config/broadcasting.php file. Finally, it broadcasts the NewMessageNotification event to the configured web-socket server on the user.{USER_ID} channel.

In our case, the event will be broadcast to the Pusher web-socket server on the user.{USER_ID} channel. If the ID of the recipient user is 1, the event will be broadcast over the user.1 channel.

As we discussed earlier, we already have a setup that listens to events on this channel, so it should be able to receive this event, and the alert box is displayed to the user!

Let's go ahead and walk through how you are supposed to test the use-case that we've built so far.

Open the URL http://your-laravel-site-domain/message/index in your browser. If you're not logged in yet, you'll be redirected to the login screen. Once you're logged in, you should see the broadcast view that we defined earlier—nothing fancy yet.

In fact, Laravel has done a quite a bit of work in the background already for you. As we've enabled the Pusher.logToConsole setting provided by the Pusher client library, it logs everything in the browser console for debugging purposes. Let's see what's being logged to the console when you access the http://your-laravel-site-domain/message/index page.

Pusher : State changed : initialized -> connecting Pusher : Connecting : {"transport":"ws","url":"wss://ws-ap2.pusher.com:443/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0&flash=false"} Pusher : Connecting : {"transport":"xhr_streaming","url":"https://sockjs-ap2.pusher.com:443/pusher/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0"} Pusher : State changed : connecting -> connected with new socket ID 1386.68660 Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"c91c1b7e8c6ece46053b:cd8b924580e2cbbd2977fd4ef0d41f1846eb358e9b7c327d89ff6bdc2de9082d","channel":"private-user.2"}} Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"private-user.2"} Pusher : No callbacks on private-user.2 for pusher:subscription_succeeded

It has opened the web-socket connection with the Pusher web-socket server and subscribed itself to listen to events on the private channel. Of course, you could have a different channel name in your case based on the ID of the user that you're logged in with. Now, let's keep this page open as we move to test the send method.

Next, let's open the http://your-laravel-site-domain/message/send URL in the other tab or in a different browser. If you're going to use a different browser, you need to log in to be able to access that page.

As soon as you open the http://your-laravel-site-domain/message/send page, you should be able to see an alert message in the other tab at http://your-laravel-site-domain/message/index.

Let's navigate to the console to see what has just happened.

Pusher : Event recd : {"event":"App\\Events\\NewMessageNotification","data":{"message":{"id":57,"from":1,"to":2,"message":"Demo message from user 1 to user 2","created_at":"2018-01-13 07:10:10","updated_at":"2018-01-13 07:10:10"}},"channel":"private-user.2"}

As you can see, it tells you that you've just received the App\Events\NewMessageNotification event from the Pusher web-socket server on the private-user.2 channel.

In fact, you can see what's happening out there at the Pusher end as well. Go to your Pusher account and navigate to your application. Under the Debug Console, you should be able to see messages being logged.

And that brings us to the end of this article! Hopefully, it wasn't too much in a single go as I've tried to simplify things to the best of my knowledge.

Conclusion

Today, we went through one of the least discussed features of Laravel—broadcasting. It allows you to send real-time notifications using web sockets. Throughout the course of this article, we built a real-world example that demonstrated the aforementioned concept.

Yes I know, it's a lot of stuff to digest in a single article, so feel free to use the comment feed below should you find yourself in trouble during implementation.

Categories: Web Design

Getting Started With Redux: Why Redux?

Fri, 05/04/2018 - 06:15

When you're learning React, you will almost always hear people say how great Redux is and that you should give it a try. The React ecosystem is growing at a swift pace, and there are so many libraries that you can hook up with React, such as flow, redux, middlewares, mobx, etc. 

Learning React is easy, but getting used to the entire React ecosystem takes time. This tutorial is an introduction to one of the integral components of the React ecosystem—Redux.

Basic Non-Redux Terminology

Here are some of the commonly used terminologies that you may not be familiar with, but they are not specific to Redux per se. You can skim through this section and come back here when/if something doesn't make sense.  

Pure Function

A pure function is just a normal function with two additional constraints that it has to satisfy: 

  1. Given a set of inputs, the function should always return the same output. 
  2. It produces no side effects.

For instance, here is a pure function that returns the sum of two numbers.

/* Pure add function */ const add = (x,y) => { return x+y; } console.log(add(2,3)) //5

Pure functions give a predictable output and are deterministic. A function becomes impure when it performs anything other than calculating its return value. 

For instance, the add function below uses a global state to calculate its output. In addition, the function also logs the value to the console, which is considered to be a side effect. 

const y = 10; const impureAdd = (x) => { console.log(`The inputs are ${x} and ${y}`); return x+y; } Observable Side Effects

"Observable side effects" is a fancy term for interactions made by a function with the outside world. If a function tries to write a value into a variable that exists outside the function or tries to call an external method, then you can safely call these things side effects. 

However, if a pure function calls another pure function, then the function can be treated as pure. Here are some of the common side effects:

  • making API calls
  • logging to console or printing data
  • mutating data
  • DOM manipulation
  • retrieving the current time
Container and Presentational Components

Splitting the component architecture into two is useful while working with React applications. You can broadly classify them into two categories: container components and presentational components. They are also popularly known as smart and dumb components. 

The container component is concerned with how things work, whereas presentational components are concerned with how things look. To understand the concepts better, I've covered that in another tutorial: Container vs. Presentational Components in React.

Mutable vs. Immutable Objects

A mutable object can be defined as follows:

mutable object is an object whose state can be modified after it is created.

Immutability is the exact opposite—an immutable object is an object whose state cannot be modified after it is created. In JavaScript, strings and numbers are immutable, but objects and arrays are not. The example demonstrates the difference better. 

/*Strings and numbers are immutable */ let a = 10; let b = a; b = 3; console.log(`a = ${a} and b = ${b} `); //a = 10 and b = 3 /* But objects and arrays are not */ /*Let's start with objects */ let user = { name: "Bob", age: 22, job: "None" } active_user = user; active_user.name = "Tim"; //Both the objects have the same value console.log(active_user); // {"name":"Tim","age":22,"job":"None"} console.log(user); // {"name":"Tim","age":22,"job":"None"} /* Now for arrays */ let usersId = [1,2,3,4,5] let usersIdDup = usersId; usersIdDup.pop(); console.log(usersIdDup); //[1,2,3,4] console.log(usersId); //[1,2,3,4]

To make objects immutable, use the Object.assign method to create a new method or the all new spread operator.

let user = { name: "Bob", age: 22, job: "None" } active_user = Object.assign({}, user, {name:"Tim"}) console.log(user); //{"name":"Bob","age":22,"job":"None"} console.log(active_user); //{"name":"Tim","age":22,"job":"None"} What Is Redux?

The official page defines Redux as follows:

Redux is a predictable state container for JavaScript applications. 

Although that accurately describes Redux, it's easy to get lost when you see the bigger picture of Redux for the first time. It has so many moving pieces that you need to fit together. But once you do, I promise you, you'll start loving Redux. 

Redux is a state management library that you can hook up with any JavaScript library, and not just React. However, it works very well with React because of React's functional nature. To understand this better, let's have a look at the state.

As you can see, a component's state determines what gets rendered and how it behaves. The application has an initial state, and any user interaction triggers an action that updates the state. When the state is updated, the page is rerendered.

With React, each component has a local state that is accessible from within the component, or you can pass them down as props to child components. We usually use the state to store:

  1. UI state and transitionary data. This includes a list of UI elements for navigation menu or form inputs in a controlled component.
  2. Application state such as data fetched from a server, the login state of the user, etc.

Storing application data in a component's state is okay when you have a basic React application with a few components. 

Component hierarchy of a basic application

However, most real-life apps will have lots more features and components. When the number of levels in the component hierarchy increases, managing the state becomes problematic. 

Sketch of a medium-sized applicationWhy Should You Use Redux?

Here is a very probable scenario that you might come across while working with React.

  1. You are building a medium-sized application, and you have your components neatly split into smart and dumb components. 
  2. The smart components handle the state and then pass them down to the dumb components. They take care of making API calls, fetching the data from the data source, processing the data, and then setting the state. The dumb components receive the props and return the UI representation. 
  3. When you're about to write a new component, it's not always clear where to place the state. You could let the state be part of a container that's an immediate parent of the presentational component. Better yet, you could move the state higher up in the hierarchy so that the state is accessible to multiple presentational components.
  4. When the app grows, you see that the state is scattered all over the place. When a component needs to access the state that it doesn't immediately have access to, you will try to lift the state up to the closest component ancestor. 
  5. After constant refactoring and cleaning up, you end up with most of the state holding places at the top of the component hierarchy. 
  6. Finally, you decide that it's a good idea to let a component at the top handle the state globally and then pass everything down. Every other component can subscribe to the props that they need and ignore the rest.

This is what I've personally experienced with React, and lots of other developers will agree. React is a view library, and it's not React's job to specifically manage state. What we are looking for is the Separation of Concerns principle. 

Redux helps you to separate the application state from React. Redux creates a global store that resides at the top level of your application and feeds the state to all other components. Unlike Flux, Redux doesn't have multiple store objects. The entire state of the application is within that store object, and you could potentially swap the view layer with another library with the store intact.

The components re-render every time the store is updated, with very little impact on performance. That's good news, and this brings tons of benefits along with it. You can treat all your React components as dumb, and React can just focus on the view side of things.

Now that we know why Redux is useful, let's dive into the Redux architecture.

The Redux Architecture

When you're learning Redux, there are a few core concepts that you need to get used to. The image below describes the Redux architecture and how everything is connected together. 

Redux in a nutshell

If you're used to Flux, some of the elements might look familiar. If not, that's okay too because we're going to cover everything from the base. First, make sure that you have redux installed:

npm install redux

Use create-react-app or your favorite webpack configuration to set up the development server. Since Redux is an independent state management, we're not going to plug in React yet. So remove the contents of index.js, and we'll play around with Redux for the rest of this tutorial.

Store

The store is one big JavaScript object that has tons of key-value pairs that represent the current state of the application. Unlike the state object in React that is sprinkled across different components, we have only one store. The store provides the application state, and every time the state updates, the view rerenders. 

However, you can never mutate or change the store. Instead, you create new versions of the store. 

(previousState, action) => newState

Because of this, you can do time travel through all the states from the time the app was booted on your browser.

The store has three methods to communicate with the rest of the architecture. They are:

  • Store.getState()—To access the current state tree of your application. 
  • Store.dispatch(action)—To trigger a state change based on an action. More about actions below.
  • Store.subscribe(listener)—To listen to any change in the state. It will be called every time an action is dispatched.

Let's create a store. Redux has a createStore method to create a new store. You need to pass it a reducer, although we don't know what that is. So I will just create a function called reducer. You may optionally specify a second argument that sets the initial state of the store. 

src/index.jsimport { createStore } from "redux"; // This is the reducer const reducer = () => { /*Something goes here */ } //initialState is optional. //For this demo, I am using a counter, but usually state is an object const initialState = 0 const store = createStore(reducer, initialState);

Now we're going to listen to any changes in the store, and then console.log() the current state of the store.

store.subscribe( () => { console.log("State has changed" + store.getState()); })

So how do we update the store? Redux has something called actions that make this happen.

Action/Action Creators

Actions are also plain JavaScript objects that send information from your application to the store. If you have a very simple counter with an increment button, pressing it will result in an action being triggered that looks like this:

{ type: "INCREMENT", payload: 1 }

They are the only source of information to the store. The state of the store changes only in response to an action. Each action should have a type property that describes what the action object intends to do. Other than that, the structure of the action is completely up to you. However, keep your action small because an action represents the minimum amount of information required to transform the application state. 

For instance, in the example above, the type property is set to "INCREMENT", and an additional payload property is included. You could rename the payload property to something more meaningful or, in our case, omit it entirely.  You can dispatch an action to the store like this.

store.dispatch({type: "INCREMENT", payload: 1});

While coding Redux, you won't normally use actions directly. Instead, you will be calling functions that return actions, and these functions are popularly known as action creators. Here is the action creator for the increment action that we discussed earlier.

const incrementCount = (count) => { return { type: "INCREMENT", payload: count } }

So, to update the state of the counter, you will need to dispatch the incrementCount action like this:

store.dispatch(incrementCount(1)); store.dispatch(incrementCount(1)); store.dispatch(incrementCount(1));

If you head to the browser console, you will see that it's working, partially. We get undefined because we haven't yet defined the reducer.

So now we have covered actions and the store. However, we need a mechanism to convert the information provided by the action and transform the state of the store. Reducers serve this purpose.

Reducers

An action describes the problem, and the reducer is responsible for solving the problem. In the earlier example, the incrementCount method returned an action that supplied information about the type of change that we wanted to make to the state. The reducer uses this information to actually update the state. There's a big point highlighted in the docs that you should always remember while using Redux:

Given the same arguments, a Reducer should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.

What this means is that a reducer should be a pure function. Given a set of inputs, it should always return the same output. Beyond that, it shouldn't do anything more. Also, a reducer is not the place for side effects such as making AJAX calls or fetching data from the API. 

Let's fill in the reducer for our counter.

// This is the reducer const reducer = (state = initialState, action) => { switch (action.type) { case "INCREMENT": return state + action.payload default: return state } }

The reducer accepts two arguments—state and action—and it returns a new state.

(previousState, action) => newState

The state accepts a default value, the initialState, which will be used only if the value of the state is undefined. Otherwise, the actual value of the state will be retained. We use the switch statement to select the right action. Refresh the browser, and everything works as expected. 

Let's add a case for DECREMENT, without which the counter is incomplete.

// This is the reducer const reducer = (state = initialState, action) => { switch (action.type) { case "INCREMENT": return state + action.payload case "DECREMENT": return state - action.payload default: return state } }

Here's the action creator.

const decrementCount = (count) => { return { type: "DECREMENT", payload: count } }

Finally, dispatch it to the store.

store.dispatch(incrementCount(4)); //4 store.dispatch(decrementCount(2)); //2

That's it!

Summary

This tutorial was meant to be a starting point for managing state with Redux. We've covered everything essential needed to understand the basic Redux concepts such as the store, actions, and reducers. Towards the end of the tutorial, we also created a working redux demo counter. Although it wasn't much, we learned how all the pieces of the puzzle fit together. 

Over the last couple of years, React has grown in popularity. In fact, we have a number of items in the marketplace that are available for purchase, review, implementation, and so on. If you’re looking for additional resources around React, don’t hesitate to check them out.

In the next tutorial, we will make use of the things we've learned here to create a React application using Redux. Stay tuned until then. Share your thoughts in the comments. 

Categories: Web Design

20 Useful PHP Scripts Available on CodeCanyon

Thu, 05/03/2018 - 12:18

For many, PHP is the lifeblood of web development.

It may be a general-purpose scripting language, but it powers WordPressDrupalMagento, and more; not to mention the thousands of individual PHP scripts available. If you've got a problem that needs an online solution, more than likely, you can solve it by creating a PHP script—or by downloading something already built.

PHP is clearly suited for web development. Take these 20 popular PHP scripts available on Envato Market, for example:

1. Vanguard - Login and User Management

If you run a website of any sort and are looking to introduce some type of login and authentication management, then take a look at Vanguard.

In short, Vanguard is a Laravel-based application that makes it possible to introduce user registration, login, and authentication (through a variety of techniques) to a pre-existing website.

Some of the features it offers include:

  • interactive dashboard
  • unlimited number of user roles
  • powerful admin panel
  • unlimited number of permissions
  • super easy installation using installation wizard
  • user activity log
  • Avatar upload with crop feature
  • built using Twitter Bootstrap
  • active sessions management (see and manage all your active sessions)
  • full Unicode support
  • client-side and server-side form validation
  • fully customizable from settings section
  • complete and detailed documentation
  • fully object-oriented and commented PHP and JavaScript code.
  • localization support—translate the application to any language (English, Serbian and German translations included)
  • with more

If you run a website and are trying to figure out how to introduce memberships without moving to a completely different platform, give Vanguard a try. Perhaps it'll give you exactly what you need.

2. Instagram Auto Post & Scheduler - Nextpost Instagram

Turn your Instagram into an automated marketing powerhouse using Instagram Auto Post & Scheduler - Nextpost Instagram. This online marketing tool allows you to auto-post, schedule, and manage your Instagram accounts from one place.

"With Nextpost, you can post and assess your posts in a single panel and save time managing multiple Instagram accounts."

Features include:

  • extendable
  • proxy support
  • schedule posts
  • easy installation and great UI
  • supports multiple Instagram accounts
  • supports photo, story, video, and albums
  • and much, much more

The list of features included are impressive and include just about anything you would ever want with an Instagram online marketing tool.

Instagram Auto Post & Scheduler - Nextpost Instagram is a must-have for any Instagram marketer.

3. PHP Login & User Management

There's no need to use an entire CMS to handle user logins and have private pages that can only be viewed by logged-in visitors to your website.

This can easily be done by leveraging PHP Login & User Management, a MySQL-powered website PHP login script. You can even change User Levels using the built-in Control Panel when you need different levels of page security.

This script includes:

  • captcha integration
  • profiles
  • social media login support
  • login expiration
  • lost password activation code email
  • welcome and activation emails
  • as well as many control panel features for Admin

With the installation wizard and the HTML5 Twitter Bootstrap design, you'll be up and running with solid PHP Login & User Management in no time.

4. Ultimate Client Manager - CRM - Pro Edition

If you need a CRM for your business, or maybe you want to up your freelance project management, instead of adding another monthly fee to your expenses, why not host your own customer and project management system?

More specifically, the Ultimate Client Manager - CRM - Pro Edition.

UCM Pro really does pack an impressive punch of features. You can:

  • enjoy industry-standard PGP/RSA encrypted fields
  • email support tickets
  • organize your leads, customers, projects, and invoices
  • have your customers log in and see their project status
  • enable subscription billing features to help organize and automate client billing
  • convert invoices into PDF documents
  • make multiple currency and tax rate adjustments
  • have customers and staff upload project files

This is only a fraction of the useful features you'll find. And while this CRM contender is robust enough to challenge many other subscription-based CRMs, it's the little things like being able to change your CRM theme that give the Ultimate Client Manager - CRM - Pro Edition that extra polish.

5. Perfex - Open Source CRM

When it comes to managing customer relationships, there are a wide variety of solutions. Truth be told, it's not a one-size-fits-all solution, which is why it's a good thing to have a number of choices.

And one of those is Perfex.

Since we're all looking for a different set of features as it relates to CRM systems, here are some of the things that Perfex offers its users:

  • Build professional, great-looking estimates and invoices.
  • Powerful support system with the ability to auto-import tickets.
  • Track time spent on tasks and bill your customers. Ability to assign multiple staff members on task and track time per assigned staff member.
  • Add task followers even if the staff member is not a project member. The staff member will be able to track the task progress without accessing the project.
  • Keep track of leads in one place and easily follow their progress. Ability to auto import leads from email, add notes, and create proposals. Organize your leads in stages and change stages easily with drag and drop.
  • Create good-looking proposals for leads or customers and increase sales.
  • Record your company/project expenses and have the ability to bill to your customers and auto-convert to invoices.
  • Know more about your customers with a powerful CRM.
  • And much, much more

You can view features, requirements, and more on the product page.

This particular product is inexpensive, available in the marketplace, and can be installed on any system that supports PHP and MySQL (which is nearly any popular, current host). 

6. PHP Live Chat Pro

Build your own PHP and MySQL chat without monthly fees using PHP Live Chat Pro. This useful PHP script boasts many useful features.

"Live Support Chat. PHP & MySQL based. For any website. No monthly fees."

Features include:

  • easy to install installation wizard
  • conversation history with filters
  • full translation support
  • supports file sharing
  • sound notifications
  • mobile support
  • geolocation
  • and more

From the desktop to mobile applications, this works with any website.

Start chatting it up with PHP Live Chat Pro.

7. Turbo Website Reviewer - In-Depth SEO Analysis Tool

Analyze SEO issues using the Turbo Website Reviewer - In-Depth SEO Analysis Tool and provide white-labeled PDF reports. With over 50 different checks, this tool checks key issues surrounding good SEO.

"Turbo Website Reviewer helps to identify your SEO mistakes and optimize your web page contents for a better search engine ranking."

Features include:

  • user management system
  • full multilingual support
  • powerful admin control
  • fully customizable
  • built-in analytics
  • easy installation
  • and more

With a side-by-side domain comparison, the Turbo Website Reviewer - In-Depth SEO Analysis Tool includes just about anything you would ever want to be included in an SEO analysis tool.

8. Super Store Finder

Customers can easily find your store in style with Super Store Finder.

By fully integrating Google API V3 and using Geo IP to detect the user location, Super Store Finder allows your customers to find your location quickly and easily from their smartphones.

The Twitter Bootstrap powered design—complete with modal popups, tabs, alerts, and more—looks great on the desktop or smartphone. But it's the feature set of this PHP script that really catches your eye:

  • results sorted from nearest to furthest
  • use Google Street View
  • unlimited locations
  • bulk CSV import
  • autofill search field
  • multi-language support
  • users can request to add locations
  • multiple admins
  • add your own map markers
  • and much, much, more

This is a great way to leverage Google Maps into your website for both desktop and mobile users, and includes enough unique features to use Super Store Finder for more than your stereotypical use cases.

9. MailWizz - Email Marketing Application

There's no need to keep monkeying around with your email marketing.

If you're serious about having your own email marketing application, this is a great place to start. In fact, the MailWizz - Email Marketing Application is robust and feature-rich enough for you to become an email service provider for your customers!

Autoresponders? Check.

Restful API and Web Hooks? Double check.

Powerful theming system, customizable list forms, and customer back end? Triple check.

You'll have no problem sending tens of thousands of emails in just an hour, or importing and exporting subscriber lists, reports, and stats; not to mention enjoying IP location services, and best of all, unlimited lists and subscribers.

The MailWhizz - Email Marketing Application includes support for many delivery servers, including SMTP Amazon SES, Directory Pickup, PHP's mail, and Sendmail.

10. Freelance Cockpit

If you're a freelancer, then you know the challenges of managing all of the overhead that comes with actually managing the business (aside from managing solutions for your clients).

Freelance Cockpit aims to help you do exactly that.

The application offers an all-in-one solution for managing projects, tasks, support, messaging, and so much more. Some of the features include:

  • Multi File Upload and File Commenting. On all projects you can upload any kind of files, like a screenshot of the mockup you made for a new web project, and share them with your client.
  • Client Management. Easily manage your clients with all the details you need.
  • Client Portal. Your clients can view the status of their projects and invoices.
  • Invoice Management. Creating and sending invoices was never that easy!
  • Expenses. Track all your expenses.
  • Estimates. Send estimates to your clients.
  • Recurring Invoices. Create recurring invoices.
  • Calendar. Beautiful calendar with optional Google calendar integration.
  • Item Management. Manage your items/products.
  • Reports. A nice chart to view your income and expenses in a given period.
  • User Activity Widget. See who is online.
  • Email Notifications. Get email notifications on new messages, project assignment, etc.
  • User Access Levels. Control the access of your agents to the different modules.
  • Quick Access. Quickly open a project or start/stop the timer using the Quick Access widget.
  • Database backup. Never lose any data again!
  • And more.

If you're a freelancer, or even a small business, and you're looking for an all-in-one solution to help manage the overhead for all things that you're doing related to your business, check out Freelance Cockpit.

11. Coin Table - Cryptocurrency Market CMS

Keep up to date and share the current exchange rate for over 1,000 different cryptocurrencies with the Coin Table - Cryptocurrency Market CMS. Easily manage it within its own admin panel and create multiple authenticated users.

"Coin Table is a Content Management System built for Cryptocurrency Real-time Information."

Customizable pages include:

  • home
  • table
  • currency
  • converter

Features include:

  • supports all the major social networks
  • plug and play custom HTML ads
  • convert to 156 currencies
  • and more

Keep up with Bitcoin and cryptocurrency with Coin Table - Cryptocurrency Market CMS.

12. Premium URL Shortener

If you've used Bit.ly very much—especially if you're using a custom domain—you'll find there's a giant leap between their free and paid service. That makes something like Premium URL Shortener a "no-brainer".

This PHP URL shortener was built with performance in mind, and that's exactly what it does. It comes complete with a powerful dashboard, admin, and geotargeting, and it's fully social-media ready. You'll not only enjoy using Premium URL Shortener, but maybe even take advantage of the new built-in membership system.

13. ContactMe - Responsive AJAX Contact Form - HTML5 PHP

There's hardly anything more useful than a good contact form. Look no further! ContactMe - Responsive AJAX Contact Form - HTML5 PHP is an excellent solution.

"Extremely customizable Contact Form, in a easy and quick way you can create THOUSANDS of different Contact Forms to fit your needs!"

Forms include:

  • general
  • send files
  • hotel contact
  • job application
  • restaurant contact
  • and many more

With over 28 combinations ready to use, you'll be up and running quickly with a good-looking contact form.

Features include:

  • no database required
  • easy to customize
  • add attachments
  • no page reload
  • supports CC
  • and more

ContactMe - Responsive AJAX Contact Form - HTML5 PHP is perfect for every developer's toolkit.

14. PHP Live Support Chat

Set up a live support chat system with PHP Live Support Chat. This PHP and SQL-based solution brings real-time chat to your website.

(Yes, it also includes a high-quality emoticon set.)

On the customer/user side, PHP Live Support Chat offers:

  • a well-designed chat window
  • avatars and emoticons to keep communication clear
  • and mobile support

From the customer support side, you'll benefit from the:

  • chat logs
  • desktop notifications
  • prepared messages
  • and more!

PHP Live Support Chat is easy to install, supports multiple users at once, and has unlimited usage.

15. VTGram - For Instagram Marketing

Ever since Instagram introduced videos, anyone and everyone who uses the service has seen the sponsored posts.

But what if you were able to leverage the platform to market your own product without needing to use the sponsorship features they provide? Or what if you were able to target people, likes, comments, posts, etc., all from within a single application.

Enter VTGram.

Just some of the features include:

  • Auto Post: You can post an image and/or video. The world’s first Instagram video posting tool post from your desktop—PC or Mac.
  • Auto Direct Message: You can send direct messages to your followers easily.
  • Auto Comment: Search and comment all the posts you want in one click.
  • Auto Like: Search and like all the posts you want in one click.
  • Auto Follow: The fastest and most economical way for you if you want to increase following.
  • Auto Unfollow: One reason that you want to unfollow all friends. Tools can also help you.
  • Auto Follow Back: You’re tired of follow back. This feature will help you save time.
  • Search: Search top hashtags and users in the quickest way.
  • Social Login: Supported login via Facebook, Google, and Twitter.

You can purchase, read more, see the requirements, and even test drive the application all from its page in the Envato marketplace.

16. phpDolphin - Social Network Platform

With the popularity of Facebook, many people have found themselves leaving the popular social network for smaller, niche online communities.

With the phpDolphin - Social Network Platform, you can host your very own social network.

Facebook users will find themselves right at home with:

  • likes
  • profile pages
  • news feed
  • groups
  • sharing
  • and much more

As Admin, you'll have full control to manage users, groups, and reports. You can even add phpDolphin plugins to extend your social network's features—Dislike Plugin, anyone?

phpDolphin is very robust, so don't let its Facebook "cloned" design deter you from it.

17. Ninja Media Script - Viral Fun Media Sharing Site

Creating your own media sharing site has never been so easy—or looked this good!

The Ninja Media Script - Viral Fun Media Sharing Site delivers lots of features and solid design.

Built with Laravel 4, Bootstrap 3, Font Awesome 4, and more, this PHP script is easy to install, customizable, and fully responsive.

Users can log in and register with their email address, Facebook, or Google, and then upload images and videos that can then be approved by a site admin or published directly.

Add a logo, use a watermark, choose your layout—you're dealing with a ninja.

Features include:

  • add pages
  • commenting
  • likes
  • NSFW functionality
  • translation ready
  • and more

Any YouTube, Vimeo, Vine, GIF, or JPG could go viral with the Ninja Media Script - Viral Fun Media Sharing Site PHP script.

18. FileGator

Copy, move, rename, edit, delete and upload files online with FileGator.

Without using a database—or Flash—you can run this powerful PHP file manager and Ajax uploader.

Share, zip, and manage multiple files online with your own file manager.

With FileGator's fast and easy-to-use UI, you can:

  • use Google's URL shortener for email links
  • have multiple user and guest accounts
  • search files and folders
  • create archives with zip
  • unzip and decompress files online
  • and much more

Easy to install, easy to use, easy to download FileGator.

19. Stock Manager Advance with Point of Sale Module

You can update product stock, purchases, and sales with an Internet connection and Stock Manager Advance with Point of Sale Module.

Manage multiple warehouses, generate reports, and more.

Features:

  • works well with touch screens
  • print order and bill
  • supports Stripe and PayPal Pro
  • calendar and calculator
  • staff and customer notifications
  • and more

Manage your standard, combo, and digital products with Stock Manager Advance with Point of Sale Module.

20. Rise Project Manager

Project management is one of those areas of running a business that some prefer more than others. If you're a freelancer, it comes with the territory; if you're part of a larger business, then it may be your role.

Regardless, finding the best way to manage said projects can be tough. Perhaps Rise is a viable solution?

Straight from the product page:

Ultimate Project Manager is the best way to manage your projects, clients and team members. You can easily collaborate with your team and monitor your work. It’s easy to use & install.

And it has a ton of features, to boot. Some of the examples include:

  • Projects. Manage all your projects using some amazing tools. Create tasks in projects and assign your team members on the tasks. Create milestones to estimate the timeframe. Upload files by dragging and dropping in projects and discuss with your team. Let your team members comment on tasks and get notifications for important events. See activity logs for projects.
  • Clients. It’s very simple to add your clients in Rise. You’ll get detailed information about contacts, projects, invoices, payments, estimates, tickets and notes of each client. You can allow your clients to use the client portal. Each client will get a separate dashboard to see their projects. Let your clients create tasks for the projects and get feedback instantly.
  • Team members. Assign tasks to your team members and monitor the status easily. You can set different permissions on their access.
  • Invoices. Send invoices to your clients by email with a PDF copy of the invoice. And get paid online via Stripe and PayPal.
  • Estimates. Create estimate request forms according to your needs and let your clients request estimates. Review the estimate requests and submit your estimates to clients.
  • Tickets. Let your clients create support tickets and get notification by web and email. Assign team members to tickets and track the status.
  • Expenses. Track all your expenses and get information about your project cost easily.
  • Event calendar. Create your personal events list and share events with team members.
  • Messaging. Send private messages to team members and clients.

And there's clearly much, much more.

If you find yourself in this role, then I highly recommend checking out what Rise has to offer and see if it fits the bill. In a field that's got a lot of competition, this particular product may hit the right price point.

Conclusion

You can clearly see how versatile PHP is—it can be used for anything from simple solutions to full social networks and project management.

On Envato Tuts+, you'll find all kinds of helpful resources to learn PHP, like PHP tutorialscode eBooks, and video code courses. I particularly enjoy the video code courses. They have beginner PHP courses, like Introduction to WordPress Plugin Development and PHP Fundamentals, or more advanced video courses such as PHP Object Oriented Programming Fundamentals and Go Further With WooCommerce Themes. No matter your learning style, you'll be sure to find helpful PHP code courses.

And if you're curious to know what other PHP scripts are out there, take a peek at what's on offer at Envato Market.

Categories: Web Design

How to Find and Fix Poor Page Load Times With Raygun

Thu, 04/26/2018 - 07:43

In this tutorial, we'll focus on finding and fixing poor page load times with Raygun. But before we do that, let's discuss why slightly longer page load times can be such a big deal.

One of the most important things that you can do to make a good first impression on potential customers or clients visiting your website is improve its loading speed.

Imagine a customer who just heard about your company from a friend. You sell a product online which users can purchase by visiting your website. If different website pages are taking a long time to load and you are not selling that product exclusively, there is a good chance that the customer will abandon your site and go somewhere else.

You did not just miss out on your first sale here, you also missed the opportunity to have a loyal customer who would have purchased more products in the future. 

That's the thing with the Internet—people are just a few clicks away from leaving your website and buying something from your competitors. Faster loading pages can give you an edge over competitors and increase your revenue.

How Can Raygun Help?

Raygun relies on Real User Monitoring Insights (RUM Insights) to improve a website's performance and page load time. The term "Real User Monitoring" is the key here. You could use tools like WebPagetest and Google Page Speed Insights to optimize individual pages, but those results will not be based on real user data. On the other hand, the data provided by Raygun is based on real users who visited your website.

Raygun also presents the information in a more organized manner by telling you things like the average page speed for the website, the most requested pages, and the slowest pages. This way, you can prioritize which page or section of the website needs to be optimized first.

You can also see how fast the website is loading for users in different countries or for users with different browsers. Similarly, you can compare the speed of your website on mobile vs. desktop. 

Another advantage of Raygun is that it will show you how the website is performing for different users. For example, the website may be loading slowly for one of your most valuable clients. In such cases, you would definitely like to know about it and do something to improve their experience before it is too late.

We will learn how to do all that with Raygun in the next few sections of this article.

Integrating Raygun Into Your Website

You need to sign up for an account before integrating Raygun into your website. This account will give you access to all Raygun features for free for 14 days.

Once you have registered successfully, you can click on the Create Application button to create a new application. You can fill out a name for your application on the next screen and then check some boxes to receive notifications about errors and real user monitoring insights.

Now you just have to select your development platform or framework. In this case, we are using JavaScript.

Finally, you will get some code that you have to add on all the pages you want to monitor. Instead of placing the following code in your website, you could also download the production or development version of the library and include it yourself.

<script type="text/javascript"> !function(a,b,c,d,e,f,g,h){a.RaygunObject=e,a[e]=a[e]||function(){ (a[e].o=a[e].o||[]).push(arguments)},f=b.createElement(c),g=b.getElementsByTagName(c)[0], f.async=1,f.src=d,g.parentNode.insertBefore(f,g),h=a.onerror,a.onerror=function(b,c,d,f,g){ h&&h(b,c,d,f,g),g||(g=new Error(b)),a[e].q=a[e].q||[],a[e].q.push({ e:g})}}(window,document,"script","//cdn.raygun.io/raygun4js/raygun.min.js","rg4js"); </script>

Once you have added the above code snippet before the closing </head> tag, you have to place the following snippet just before the closing <body> tag.

<script type="text/javascript"> rg4js('apiKey', 'YOUR_API_KEY'); rg4js('enableCrashReporting', true); rg4js('enablePulse', true); </script>

If you don't add any more code, Raygun will now start collecting anonymous data. This means that you will be able to know how your website is performing for different users, but there will be no way to identify those users.

There is an easy fix for this problem. All you have to do is add the following code in your webpages and Raygun will take care of the rest.

rg4js('setUser', { identifier: 'unique_id', isAnonymous: false, email: 'users_email@domain.com', firstName: 'Firstname', fullName: 'Firstname Lastname' });

You will have to include these three pieces of code in all the pages that you want to track. Once done, the data will start showing up in the dashboard for you to analyze.

Finding Pages With Poor Load Times

The Real User Monitoring section in the Raygun dashboard has a lot of tabs to present the data in different formats. We will briefly go over all these tabs to show you how the information presented in them can be used to find pages with poor load times.

The Live tab will give you an overview of your site's performance in real time. It has different metrics like Health Score to show you how the site is currently performing. You can read more about all these metrics in the documentation for the Live tab on the Raygun website.

It also has a world map to point out the countries of your currently active users. You will also find a list of most recent requests to your website by different users. Here is an image showing the most recent requests to our website.

The performance tab has five useful metrics to give you a quick overview of the website page load times. For example, a median load time of 1.41 seconds means that 50% of your pages load before 1.41 seconds have passed. Similarly, a P90 Load Time of 6.78 seconds tells you that 90% of the time, the website loads before 6.48 seconds.

This should give you a rough idea of the performance of a website and how slow it is for the slowest 10% of users.

The performance tab also has a list of the slowest and most requested pages at the bottom. Knowing the most popular and the slowest pages can be very helpful when you want to prioritize which sections of your website need to be fixed first.

Even though all pages in a website should load as quickly as possible, some of these pages are more important than others. Therefore, you might be interested in finding out the performance of a particular page on a website. You can do so by simply typing the page you are looking for in the input field. This will give you information about the median, average, and P90 load time of a particular page. Here is the data for the home page of our website.

You can use the Sessions tab to see session-related information like the total number of sessions, total number of users, and median session length. The sessions table will give you a quick overview of the last 150 sessions with information like the country, duration, total page views, and the last visited page for a session.

Clicking on the magnifying glass will show you more details of a particular session like the pages the user visited, the load time of those pages, and the browser/device used during the session.

The Users tab will give you an overview of the satisfaction level of different users with your website. This can be very helpful when you want to see how a particular user is interacting with your website and if or why their page load time is more than expected.

There are three other tabs to show information about all the page views in terms of browsers, platforms, and geography. This way you will be able to know if a webpage is loading slowly only on a particular browser or platform. You will also have a rough idea of the distribution of users. For instance, knowing if most of your clients are from a particular country or use a particular browser can be very handy.

Raygun lists the percentage of visitors from a particular continent at the top of the Geo tab. After that, it provides a map with the distribution of load times. Countries with the slowest load times are filled with red, and countries with quick load times are filled with green.

If you are consistently getting poor loading times from a particular country, it might be worth your time to look closely and find out the reason.

Fixing Poor Page Load Times

In the previous section, we learned how to use all the data collected by Raygun to figure out which pages are taking a long time to load or if there are any countries where our page load times are longer than usual.

Now it is time to see how we can use Raygun to discover issues which might be causing a particular page or the whole website to load slower than usual.

Improving poor page load time of a website can be pretty overwhelming, especially if the website is very complicated or if it has a lot of pages. The trouble is in finding what to improve and where to start.

Luckily, Raygun can offer you some general insights to fix your website. You can click on the Insights options under the Real User Monitoring menu, and Raygun will scan your website for any potential issues. You can find a list of all these rules in the official Raygun documentation. Fixing all the listed issues will significantly speed up your website.

Besides following these general guidelines, you might also want to isolate the pages that have been loading slowly. Once you have isolated them, Raygun can show you the time they take to resolve DNS, latency, SSL handshake, etc. This will give you a good idea of the areas where you can make improvements to reduce the page load time. The following image should make it clear.

You can also filter the data in order to get a more accurate picture of the load time for a particular page and various factors affecting it. The above image showed you the average latency for all requests made to the "About Us" page. However, you can click on the Add Filter button at the top and only see the "About Us" loading time graph for a specific country like Italy.

You will also see all the requests made by a specific page at the bottom. Basically, you will be able to see the DNS, latency, SSL, server, and transfer time for every resource loaded for a specific page and see if any of them is the culprit.

Once you find out which resources are taking too long to load, you can start optimizing your pages.

Final Thoughts

As you saw in this tutorial, Raygun can be of great help to organizations looking to improve their page load times. It is super easy to integrate, and after successful integration, the data will simply start showing up in the dashboard without any intervention from your side.

Raygun also has different tabs to organize the collected data so that you can analyze it more easily and efficiently. For example, it can show you load times for different countries, browsers, and platforms. It also has filters that you can use to isolate a particular set of data from the rest and analyze it closely.

If you or your company are looking for an easy-to-integrate tool which can provide great insights about how your real users are interacting with your website, you should definitely give Raygun a try. You don't have anything to lose since it is free for the first 14 days!

And while you're here, check out some of our other tutorials on Raygun!

Categories: Web Design

Notifications in Laravel

Mon, 04/23/2018 - 05:00

In this article, we're going to explore the notification system in the Laravel web framework. The notification system in Laravel allows you to send notifications to users over different channels. Today, we'll discuss how you can send notifications over the mail channel.

Basics of Notifications

During application development, you often need to notify users about different state changes. It could be either sending email notifications when the order status is changed or sending an SMS about their login activity for security purposes. In particular, we're talking about messages that are short and just provide insight into the state changes.

Laravel already provides a built-in feature that helps us achieve something similar—notifications. In fact, it makes sending notification messages to users a breeze and a fun experience!

The beauty of that approach is that it allows you to choose from different channels notifications will be sent on. Let's quickly go through the different notification channels supported by Laravel.

  • Mail: The notifications will be sent in the form of email to users.
  • SMS: As the name suggests, users will receive SMS notifications on their phone.
  • Slack: In this case, the notifications will be sent on Slack channels.
  • Database: This option allows you to store notifications in a database should you wish to build a custom UI to display it.

Among different notification channels, we'll use the mail channel in our example use-case that we're going to develop over the course of this tutorial.

In fact, it'll be a pretty simple use-case that allows users of our application to send messages to each user. When users receive a new message in their inbox, we'll notify them about this event by sending an email to them. Of course, we'll do that by using the notification feature of Laravel!

Create a Custom Notification Class

As we discussed earlier, we are going to set up an application that allows users of our application to send messages to each other. On the other hand, we'll notify users when they receive a new message from other users via email.

In this section, we'll create necessary files that are required in order to implement the use-case that we're looking for.

To start with, let's create the Message model that holds messages sent by users to each other.

$php artisan make:model Message --migration

We also need to add a few fields like to, from and message to the messages table. So let's change the migration file before running the migrate command.

<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMessagesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('messages', function (Blueprint $table) { $table->increments('id'); $table->integer('from', FALSE, TRUE); $table->integer('to', FALSE, TRUE); $table->text('message'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('messages'); } }

Now, let's run the migrate command that creates the messages table in the database.

$php artisan migrate

That should create the messages table in the database.

Also, make sure that you have enabled the default Laravel authentication system in the first place so that features like registration and login work out of the box. If you're not sure how to do that, the Laravel documentation provides a quick insight into that.

Since each notification in Laravel is represented by a separate class, we need to create a custom notification class that will be used to notify users. Let's use the following artisan command to create a custom notification class—NewMessage.

$php artisan make:notification NewMessage

That should create the app/Notifications/NewMessage.php class, so let's replace the contents of that file with the following contents.

<?php // app/Notifications/NewMessage.php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use App\User; class NewMessage extends Notification { use Queueable; public $fromUser; /** * Create a new notification instance. * * @return void */ public function __construct(User $user) { $this->fromUser = $user; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['mail']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { $subject = sprintf('%s: You\'ve got a new message from %s!', config('app.name'), $this->fromUser->name); $greeting = sprintf('Hello %s!', $notifiable->name); return (new MailMessage) ->subject($subject) ->greeting($greeting) ->salutation('Yours Faithfully') ->line('The introduction to the notification.') ->action('Notification Action', url('/')) ->line('Thank you for using our application!'); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return [ // ]; } }

As we're going to use the mail channel to send notifications to users, the via method is configured accordingly. So this is the method that allows you to configure the channel type of a notification.

Next, there's the toMail method that allows you to configure various email parameters. In fact, the toMail method should return the instance of \Illuminate\Notifications\Messages\MailMessage, and that class provides useful methods that allow you to configure email parameters.

Among various methods, the line method allows you to add a single line in a message. On the other hand, there's the action method that allows you to add a call-to-action button in a message.

In this way, you could format a message that will be sent to users. So that's how you're supposed to configure the notification class while you're using the mail channel to send notifications.

At the end, you need to make sure that you implement the necessary methods according to the channel type configured in the via method. For example, if you're using the database channel that stores notifications in a database, you don't need to configure the toMail method; instead, you should implement the toArray method, which formats the data that needs to be stored in a database.

How to Send Notifications

In the previous section, we created a notification class that's ready to send notifications. In this section, we'll create files that demonstrate how you could actually send notifications using the NewMessage notification class.

Let's create a controller file at app/Http/Controllers/NotificationController.php with the following contents.

<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Message; use App\User; use App\Notifications\NewMessage; use Illuminate\Support\Facades\Notification; class NotificationController extends Controller { public function __construct() { $this->middleware('auth'); } public function index() { // user 2 sends a message to user 1 $message = new Message; $message->setAttribute('from', 2); $message->setAttribute('to', 1); $message->setAttribute('message', 'Demo message from user 2 to user 1.'); $message->save(); $fromUser = User::find(2); $toUser = User::find(1); // send notification using the "user" model, when the user receives new message $toUser->notify(new NewMessage($fromUser)); // send notification using the "Notification" facade Notification::send($toUser, new NewMessage($fromUser)); } }

Of course, you need to add an associated route in the routes/web.php file.

Route::get('notify/index', 'NotificationController@index');

There are two ways Laravel allows you to send notifications: by using either the notifiable entity or the Notification facade.

If the entity model class utilizes the Illuminate\Notifications\Notifiable trait, then you could call the notify method on that model. The App\User class implements the Notifiable trait and thus it becomes the notifiable entity. On the other hand, you could also use the Illuminate\Support\Facades\Notification Facade to send notifications to users.

Let's go through the index method of the controller.

In our case, we're going to notify users when they receive a new message. So we've tried to mimic that behavior in the index method in the first place.

Next, we've notified the recipient user about a new message using the notify method on the $toUser object, as it's the notifiable entity.

$toUser->notify(new NewMessage($fromUser));

You may have noticed that we also pass the $fromUser object in the first argument of the __construct method, since we want to include the from username in a message.

On the other hand, if you want to mimic it using the Notification facade, it's pretty easy to do so using the following snippet.

Notification::send($toUser, new NewMessage($fromUser));

As you can see, we've used the send method of the Notification facade to send a notification to a user.

Go ahead and open the URL http://your-laravel-site-domain/notify/index in your browser. If you're not logged in yet, you'll be redirected to the login screen. Once you're logged in, you should receive a notification email at the email address that's attached with the user 1.

You may be wondering how the notification system detects the to address when we haven't configured it anywhere yet. In that case, the notification system tries to find the email property in the notifiable object. And the App\User object class already has that property as we're using the default Laravel authentication system.

However, if you would like to override this behavior and you want to use a different property other than email, you just need to define the following method in your notification class.

public function routeNotificationForMail() { return $this->email_address; }

Now, the notification system should look for the email_address property instead of the email property to fetch the to address.

And that's how to use the notification system in Laravel. That brings us to the end of this article as well!

Conclusion

What we've gone through today is one of the useful, yet least discussed, features in Laravel—notifications. It allows you to send notifications to users over different channels.

After a quick introduction, we implemented a real-world example that demonstrated how to send notifications over the mail channel. In fact, it's really handy in the case of sending short messages about state changes in your application.

For those of you who are either just getting started with Laravel or looking to expand your knowledge, site, or application with extensions, we have a variety of things you can study in Envato Market.

Should you have any queries or suggestions, don't hesitate to post them using the feed below!

Categories: Web Design

Pages