emGee Software Solutions Custom Database Applications

Share this

Web Design

Single-Page Applications With ngRoute and ngAnimate in AngularJS

Tuts+ Code - Web Development - Tue, 11/14/2017 - 04:00

Single-page applications allow you to refresh a certain portion of a web-page by routing any content stored in a separate .html file. By doing so, you do not reload your main page.

AngularJS provides a module named ngRoute exactly for this purpose.

Another useful module of AngularJS is ngAnimate, which makes it easy to animate with certain CSS classes.

In this tutorial I will try to explain each step thoroughly, although you still need a basic knowledge of AngularJS in order to be able to follow.

Starting With a Main PageBasic Structure

This index.html file is going to be our main page where we have both fixed and routed content.

I will start with a basic HTML document and include all the necessary libraries along with our custom stylesheet named style.css and a JavaScript file angularApp.js.

<html> <head> <link href="style.css" rel="stylesheet"> </head> <body> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script> <script src="angularApp.js"></script> </body> </html>

 Now I add two DIVs with id names of fixedContent and routedContent inside a mainWrapper DIV.

routedContent is also wrapped inside another DIV named wrapper. That is because the routedContent should be absolute positioned relative to a parent DIV due to the fact that during routing animation, two different pieces of content clash with each other.

<html> <head> <link href="style.css" rel="stylesheet"> </head> <body> <div id="mainWrapper"> <div id="fixedContent"></div> <div id="wrapper"> <div id="routedContent" ng-view></div> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script> <script src="angularApp.js"></script> </body> </html>

As the id names imply, fixedContent will be the static content of our main page, and routedContent will be dynamically changing upon user interaction.

In order to define an Angular app in our HTML file, we need to use the ng-app directive. Since the whole page will be an Angular app, we need to assign this directive to the mainWrapper DIV.

We also need the ng-view directive, which tells the DIV it is assigned to display the routed page content.

Now our index.html file looks like this:

<html> <head> <link href="style.css" rel="stylesheet"> </head> <body> <div id="mainWrapper" ng-app="mainApp"> <div id="fixedContent"></div> <div id="wrapper"> <div id="routedContent" ng-view></div> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script> <script src="angularApp.js"></script> </body> </html>Navigation Menu

We need a navigation menu in order to route different pieces of content to ng-view.

We are going to use the ul and a elements to create a simple horizontal menu. Below you can see the HTML snippet for the menu structure.

<div id="fixedContent"> <ul> <a href="#page1">Page1</a> <a href="#page2">Page2</a> <a href="#page3">Page3</a> <a href="#page4">Page4</a> </ul> </div>

By default, the ng-route module uses the ! prefix. However, here we only use # in front of our pages to be routed. This is done with the hashPrefix attribute used in the configuration, which I'll explain later in the related section. For now, take it as it is.

Our final HTML file is as follows:

<html> <head> <link href="style.css" rel="stylesheet"> </head> <body> <div id="mainWrapper" ng-app="mainApp"> <div id="fixedContent"> <ul> <a href="#page1">Page1</a> <a href="#page2">Page2</a> <a href="#page3">Page3</a> <a href="#page4">Page4</a> </ul> </div> <div id="wrapper"> <div id="routedContent" ng-view></div> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script> <script src="angularApp.js"></script> </body> </html>Styling the Main Page

Since this tutorial focuses on AngularJS, I am not going to detail the CSS styling. If you have former CSS knowledge, style the page as you wish. Otherwise, you can use the styling I provide below.

html, body{ margin: 0; padding: 0; } #mainWrapper{ display: flex; flex-direction: column; align-items: center; margin-top: 50px } #fixedContent{ margin-bottom: 50px; } #wrapper{ width: 350px; } #routedContent{ width: 350px; position: absolute; } ul{ display: flex; justify-content: space-between; width: 350px; margin: 0; padding: 0; } a{ text-decoration: none; color: #FFFFFF; font-family: Arial; list-style: none; background-color: #cecece; padding: 7px 10px; border-radius: 2px; }Pages to Be Routed

Each page that will be routed to DIV with the ng-view directive inside the main HTML file can have a unique HTML structure and CSS styling.

Let's start with page1.html.

Since we want a specific styling for each page, we need separate CSS files for each page. Therefore, we also create a file named page1.css, which will contain the styling rules of page1.html.

The basic HTML structure for page1 is as follows:

<link href="page1.css" rel="stylesheet"> <div id="page1"> <h1>Page 1</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> </div>

At the top, we linked to the CSS file that will be styling the page, and we declared a DIV with id name of page1, where the whole content will be laid.

I will keep it simple, but it is completely up to you how to structure the HTML file. Just bear in mind that your container will always be the DIV to which the ng-view directive is assigned. So everything in your routed pages will be relative to that DIV.

The styling of page1.html is given below:

#page1{ font-family: Arial; } h1{ color: #ffa42a; }

The other three pages can be totally different, but for the sake of simplicity I am just using the same template for each HTML page and slightly different CSS files (different h1 text-colors).

page2.html & page2.css<link href="page2.css" rel="stylesheet"> <div id="page2"> <h1>Page 2</h1> <p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p> </div>#page2{ font-family: Arial; } h1{ color: cornflowerblue; }page3.html & page3.css<link href="page3.css" rel="stylesheet"> <div id="page3"> <h1>Page 3</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> </div>#page3{ font-family: Arial; } h1{ color: #b2ce6f; }page4.html & page4.css<link href="page4.css" rel="stylesheet"> <div id="page4"> <h1>Page 4</h1> <p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p> </div>


#page4{ font-family: Arial; } h1{ color: #ff4517; }Setting the ngRoute & ngAnimate in JavaScript

So far we have completed all the necessary HTML and CSS files. Now it is time to write the JavaScript code that controls the routing and animation.

Since our ng-app directive is named mainApp, we use this id in the module function. We also need to include the ngRoute and ngAnimate dependencies.

mainAngular = angular.module('mainApp',['ngRoute', 'ngAnimate']);

Now we have access to $routeProvider and $locationProvider.

We are going to use the $routeProvider to manage the routings and $locationProvider to change the hashPrefix, which is set to ! by default.

We use .when('/page1', {templateUrl: 'page1.html'}) to define the page to be routed when <a href="#page1">Page1</a> is clicked in our main HTML file.

We repeat the same line of code for each page to be routed. At the end, we use .otherwise({redirectTo: '/page1'}), which handles unexpected page names. If you try to visit an undefined page name, say page5, you will be redirected to page1.

The complete JavaScript code is below:

var mainAngular = angular.module('mainApp',['ngRoute', 'ngAnimate']); mainAngular.config(function ($routeProvider, $locationProvider) { $routeProvider .when('/page1',{ templateUrl: 'page1.html' }) .when('/page2',{ templateUrl: 'page2.html' }) .when('/page3',{ templateUrl: 'page3.html' }) .when('/page4',{ templateUrl: 'page4.html' }) .otherwise({ redirectTo: '/page1' }); $locationProvider.hashPrefix(''); });

Extra Note: If you wish to add a specific ng-controller directive for any pages to be routed, you can handle this inside the $routeProvider.

An example for page1:

.when('/page1',{ templateUrl: 'page1.html', controller: 'page1Controller' })

In the end, our page should look like this, and you should be able to navigate between pages with no transition animations.

Animating the Page Transitions

Now it is time to animate the route transitions.

For animation purposes, AngularJS has built-in CSS classes thanks to the ngAnimate dependency.

Those classes that we are going to use are:

  • ng-enter: The starting CSS styles for the enter animation.
  • ng-enter-active: The finishing CSS styles for the enter animation.
  • ng-leave: The starting CSS styles for the leave animation.
  • ng-leave-active: The finishing CSS styles for the leave animation.

So the routed content which is coming into the main page has a transition from ng-enter to ng-enter-active. Likewise, the content leaving the main page has a transition from ng-leave to ng-leave-active.

We have to attach the above mentioned classes to our routedContent class.

An example transition is given below. You can either design your own transition animations or use this one inside your style.css file.

#routedContent.ng-enter{ transform: translateX(-500px); opacity: 0; -webkit-transition: all 0.35s cubic-bezier(1,.01,0,.99); -moz-transition: all 0.35s cubic-bezier(1,.01,0,.99); -ms-transition: all 0.35s cubic-bezier(1,.01,0,.99); -o-transition: all 0.35s cubic-bezier(1,.01,0,.99); transition: all 0.35s cubic-bezier(1,.01,0,.99); } #routedContent.ng-enter-active{ transform: translateX(0px); opacity: 1; } #routedContent.ng-leave{ transform: translateX(0); opacity: 1; -webkit-transition: all 0.35s cubic-bezier(1,.01,0,.99); -moz-transition: all 0.35s cubic-bezier(1,.01,0,.99); -ms-transition: all 0.35s cubic-bezier(1,.01,0,.99); -o-transition: all 0.35s cubic-bezier(1,.01,0,.99); transition: all 0.35s cubic-bezier(1,.01,0,.99); } #routedContent.ng-leave-active{ transform: translateX(500px); opacity: 0; }

Below is the final result of the project on Plunker.

Conclusion

In this tutorial, we covered how to create SPA applications with the ng-route module of AngularJS, and then we animated the transitions through the CSS classes of ng-animate.

By using only four CSS classes provided by ng-animate, you can achieve various animations. You can always attach extra classes to have more control over the transition animations. For example, you can make your page transitions direction-aware.

I also mentioned that by attaching the controller directive to each specific page inside the $routeProvider, you can achieve extra control over each page.

JavaScript, with its libraries such as Angular, has become one of the de facto languages of working on the web. It’s not without its learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available on Envato Market.

I hope this tutorial gave you an overall idea of how to utilize the ng-route and ng-animate modules together.

Categories: Web Design

Comprehensive Guide To Designing Comparison Tables & Pricing Pages

Everyone who lands on your page wants information. And they want it fast. A comparison pricing table is the best thing you can add to encourage sales and get...

The post Comprehensive Guide To Designing Comparison Tables & Pricing Pages appeared first on Onextrapixel.

Categories: Web Design

Mobile Interface Myths You Should Throw Out The Window

Smashing Magazine - Mon, 11/13/2017 - 14:30
If anything’s clear in 2017, it’s that lying is back in fashion (if it ever left us at all). From the heated fake news debate to the false data provided by Facebook, lying is all the rage these days. A white lie here and there is no big deal. We’re all guilty of it. The problem arises when lies turn into full-grown myths, then become accepted as truths. In an era of digital chaos, we understandably gravitate to our trusted sources of information.
Categories: Web Design

Get Started With Vue Router

Tuts+ Code - Web Development - Mon, 11/13/2017 - 04:00
Introduction

Routing becomes an important feature to put in place when you have many components. Thanks to the awesome Vue.js team, this is easy using vue-router.

In this tutorial, you will see how to use vue-router while building a simple Vue.js application.

Set Up the Application

You will make use of Vue-CLI to set up your application for this tutorial. If you do not have it installed already on your machine, you can do so by running:

npm install -g vue-cli

With that done, you can set up your application using the command below.

vue init webpack router-app

You are going to be asked some questions. One of these questions is: Install vue-router? For that, enter y.

Navigate to your project folder from your terminal:

cd router-app

Run the command to install all dependencies:

npm install

Start your development server by running:

npm run dev

If you want to install vue-router manually, you can use the command below.

npm install vue-router --saveIntegrate Vue-Router

When using Vue-CLI, the router gets activated by default. You can see that in your src/router/index.js file.

#src/router/index.js import Vue from 'vue' // 1 import Router from 'vue-router' // 2 import HelloWorld from '@/components/HelloWorld' // 3 Vue.use(Router) // 4 export default new Router({ // 5 routes: [ { path: '/', name: 'Hello', component: HelloWorld } ] })
  1. Vue is imported from vue.
  2. Router is imported from the vue-router package.
  3. HelloWorld is imported from the components folder. We'll talk more about this later.
  4. Router is mounted as middleware to your project.
  5. A default route configuration is created. The route is an array of a JavaScript object. It consists of a path, name, and component. The path gets appended to the URL of your web application. The route created above is meant for the home page. You can change the path to /hello, and it will no longer work for the home page. It will only be triggered when you visit the /hello path of your application. The component tells the route the component you want to load when that path is visited. This is the reason you imported the HelloWorld component. This route is exported as Router.

The exported Router has to be imported in your src/main.js file. This is important for the routes to work. This is taken care of when working with Vue-CLI. Your src/main.js should look like this:

#src/main.js // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' // 1 Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, // 2 template: '<App/>', components: { App } })
  1. The router is imported from your router folder.
  2. The imported router is used when creating a new Vue instance.
Standard Routes

The route you saw above is an example of a standard route. A standard route is one having a path, component, and name. Let's create our own route. Try this part out yourself using the flow below.

  1. Create a new component called Pack. You need to have some dummy text.
  2. Go to your src/router/index.js, import the new component you created, and add a new path.
  3. Open your browser and point to the new path.

I bet you were able to do that on your own. Let's try it out together.

Here is what my Pack component looks like.

#src/components/Pack.vue <template> <div class="hello"> <h1>Pack Component</h1> <p>This is a pack component. It is supposed to be a pack of wolves, but the wolves got trap in this tiny component</p> </div> </template> <script> export default { } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>

Open your router file, and it should be looking like this.

#src/router/index.js import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Pack from '@/components/Pack' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: HelloWorld }, { path: '/pack', name: 'Pack', component: Pack } ] })

Now point your browser to http://localhost:8080/#/pack and you should see your new page.

Routes With Parameters

The routes you have made so far are static routes. Let's see how to make dynamic routes. Open your routes file and add another route like this.

#src/router/index.js { path: '/user/:id', component: User }

Here you have your path set to /user/:id; the :id defines a parameter that allows you to pass values to the routes via the URL. From the above, it is possible to have http://localhost:8080/user/23.

The value passed to the route component, in this case, is 23, and this can be accessed in the User component.

To try this out, create a new component called User. In this component, you want to be able to access the route parameter passed through the URL. This parameter will be saved in a variable called id, and the value outputted in the template. Here is what the component should look like.

#src/components/User.vue <template> <div> <h1>User Component</h1> <p>The loaded id is: {{ id }}</p> <router-view></router-view> </div> </template> <script> export default { name: 'user', data () { return { id: this.$route.params.id } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>

Now point your browser to http://localhost:8080/#/user/123 and you will see the parameter displayed. Change from 123 to something else, and the new value will be displayed.

Child Routes

In Vue.js, it is possible to nest routes by defining children routes. This is possible using the children property provided by Vue.js. The children routes are passed as an array of objects. In your test app, you will create a child route for profiles. The child route will be given a path and a component.

First, create a new component called Profile.vue. Here is what it should look like.

#src/components/Profile.vue <template> <div> <h1>User Profile Component</h1> <p>This is a user profile</p> </div> </template> <script> export default { name: 'profile', data () { return { } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>

Open your router file. The first thing you want to do will be to import the Profile component. After doing that, add the children property to the route for the User component so your route file looks like this.

#src/router/index.js import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Pack from '@/components/Pack' import User from '@/components/User' import Profile from '../components/Profile' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: HelloWorld }, { path: '/pack', name: 'Pack', component: Pack }, { path: '/user/13', component: User, children: [ { path: 'profile', component: Profile } ] } ] })

Now you will be able to access this new component when you point your browser to http://localhost:8080/#/user/13/profile.

Link to Routes

At this moment, the only way to navigate in your application is by entering the URL path in the browser. This is not how you will want to build a real-world application. To link to routes in Vue.js, you'll have to make use of the <router-link> element.

Here is how it is used:

<router-link to="/route-path">Link to the route</router-link>

For children routes, here is what it should look like.

<router-link to="/parent-route/child-route">Links to the child route</router-link>

The to attribute contains the path to the route.

To set that up in your application, open your App.vue file and make it look like this.

#src/App.vue <template> <div id="app"> <img src="./assets/logo.png"> <div> <ul> <li><router-link to="/">Home</router-link></li> <li><router-link to="/pack">Pack</router-link></li> <li><router-link to="/user/13">User</router-link></li> <li><router-link to="/user/13/profile">Profile</router-link></li> </ul> </div> <router-view/> </div> </template> <script> export default { name: 'app' } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>Conclusion

Navigating from one view to another is now very easy. You can go ahead and add more views. Try adding a child route to the profile route, so the path becomes http://localhost:8080/#/user/13/profile/child-path.

In this tutorial, you learned how to set up routing in Vue.js. Going forward, you should have no issues with routing in a Vue.js application. While working on this application, you also learned about the route and params object.

I hope you enjoyed yourself.

Categories: Web Design

Creating Secure Password Resets With JSON Web Tokens

Smashing Magazine - Thu, 11/09/2017 - 11:31
When a user of your application has forgotten their password, it can and should be reset securely. To accomplish a secure password reset, I will demonstrate how to use JSON Web Tokens (JWT) to generate a URL-safe token. The JWT contains encoded information about the user and a signature that, when decoded, is validated to ensure that the token has not been tampered with. Once the JWT is validated, your application can securely allow the user to generate a new password, instead of sending them their forgotten one.
Categories: Web Design

How to Create a Custom Authentication Guard in Laravel

Tuts+ Code - Web Development - Thu, 11/09/2017 - 04:00

In this article, we’re going to cover the authentication system in the Laravel framework. The main aim of this article is to create a custom authentication guard by extending the core authentication system.

Laravel provides a very solid authentication system in the core that makes the implementation of basic authentication a breeze. In fact, you just need to run a couple of artisan commands to set up the scaffolding of an authentication system.

Moreover, the system itself is designed in such a way that you could extend it and plug in your custom authentication adapters as well. That’s what we'll discuss in detail throughout this article. Before we go ahead and dive into the implementation of the custom authentication guard, we’ll start with a discussion of the basic elements in the Laravel authentication system—guards and providers.

The Core Elements: Guards and Providers

The Laravel authentication system is made up of two elements at its core—guards and providers.

Guards

You could think of a guard as a way of supplying the logic that’s used to identify the authenticated users. In the core, Laravel provides different guards like session and token. The session guard maintains the state of the user in each request by cookies, and on the other hand the token guard authenticates the user by checking a valid token in every request.

So, as you can see, the guard defines the logic of authentication, and it’s not necessary that it always deals with that by retrieving valid credentials from the back end. You may implement a guard that simply checks the presence of a specific thing in request headers and authenticates users based on that.

Later in this article, we’ll implement a guard that checks certain JSON parameters in request headers and retrieves the valid user from the MongoDB back end.

Providers

If the guard defines the logic of authentication, the authentication provider is responsible for retrieving the user from the back-end storage. If the guard requires that the user must be validated against the back-end storage then the implementation of retrieving the user goes into the authentication provider.

Laravel ships with two default authentication providers—Database and Eloquent. The Database authentication provider deals with the straightforward retrieval of the user credentials from the back-end storage, while Eloquent provides an abstraction layer that does the needful.

In our example, we’ll implement a MongoDB authentication provider that fetches the user credentials from the MongoDB back end.

So that was a basic introduction to guards and providers in the Laravel authentication system. From the next section onwards, we’ll focus on the development of the custom authentication guard and provider!

A Quick Glance at the File Setup

Let's have a quick look at the list of files that we'll implement throughout the course of this article.

  • config/auth.php: It's the authentication configuration file in which we'll add an entry of our custom guard.
  • config/mongo.php: It's the file that holds the MongoDB configuration.
  • app/Services/Contracts/NosqlServiceInterface.php: It's an interface that our custom Mongo database class implements.
  • app/Database/MongoDatabase.php: It's a main database class that interacts with MongoDB.
  • app/Models/Auth/User.php: It's the User model class that implements the Authenticable contract.
  • app/Extensions/MongoUserProvider.php: It's an implementation of the authentication provider.
  • app/Services/Auth/JsonGuard.php: It's an implementation of the authentication guard driver.
  • app/Providers/AuthServiceProvider.php: This is an existing file that we'll use to add our service container bindings.
  • app/Http/Controllers/MongoController.php: It's a demo controller file that we'll implement to test our custom guard.

Don't worry if the list of the files doesn't make much sense yet as we'll discuss everything in detail as we go through it.

Deep Dive Into the Implementation

In this section, we'll go through the implementation of the required files.

The first thing that we need to do is to inform Laravel about our custom guard. Go ahead and enter the custom guard details in the config/auth.php file as shown.

... ... 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', ], 'custom' => [ 'driver' => 'json', 'provider' => 'mongo', ], ], ... ...

As you can see, we've added our custom guard under the custom key.

Next, we need to add an associated provider entry in the providers section.

... ... 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\User::class, ], 'mongo' => [ 'driver' => 'mongo' ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], ... ...

We've added our provider entry under the mongo key.

Finally, let's change the default authentication guard from web to custom.

... ... 'defaults' => [ 'guard' => 'custom', 'passwords' => 'users', ], ... ...

Of course, it won't work yet, as we've not implemented the necessary files yet. And that's what we'll discuss in the next couple of sections.

Set Up the MongoDB Driver

In this section, we'll implement the necessary files that talk to the underlying MongoDB instance.

Let's first create a configuration file config/mongo.php that holds the default MongoDB connection settings.

<?php return [ 'defaults' => [ 'host' => '{HOST_IP}', 'port' => '{HOST_PORT}', 'database' => '{DB_NAME}' ] ];

Of course, you need to change the placeholder values as per your settings.

Instead of directly creating a class that interacts with MongoDB, we'll create an interface in the first place.

The benefit of creating an interface is that it provides a contract that a developer must adhere to while implementing it. Also, our implementation of MongoDB could be easily swapped with another NoSQL implementation if needed.

Go ahead and create an interface file app/Services/Contracts/NosqlServiceInterface.php with the following contents.

<?php // app/Services/Contracts/NosqlServiceInterface.php namespace App\Services\Contracts; Interface NosqlServiceInterface { /** * Create a Document * * @param string $collection Collection/Table Name * @param array $document Document * @return boolean */ public function create($collection, Array $document); /** * Update a Document * * @param string $collection Collection/Table Name * @param mix $id Primary Id * @param array $document Document * @return boolean */ public function update($collection, $id, Array $document); /** * Delete a Document * * @param string $collection Collection/Table Name * @param mix $id Primary Id * @return boolean */ public function delete($collection, $id); /** * Search Document(s) * * @param string $collection Collection/Table Name * @param array $criteria Key-value criteria * @return array */ public function find($collection, Array $criteria); }

It's a pretty simple interface that declares the basic CRUD methods that a class must define that implements this interface.

Now, let's define an actual class at app/Database/MongoDatabase.php.

<?php // app/Database/MongoDatabase.php namespace App\Database; use App\Services\Contracts\NosqlServiceInterface; class MongoDatabase implements NosqlServiceInterface { private $connection; private $database; public function __construct($host, $port, $database) { $this->connection = new MongoClient( "mongodb://{$host}:{$port}" ); $this->database = $this->connection->{$database}; } /** * @see \App\Services\Contracts\NosqlServiceInterface::find() */ public function find($collection, Array $criteria) { return $this->database->{$collection}->findOne($criteria); } public function create($collection, Array $document) {} public function update($collection, $id, Array $document) {} public function delete($collection, $id) {} }

Of course, I assume that you've installed MongoDB and the corresponding MongoDB PHP extension.

The __construct method instantiates the MongoClient class with the necessary parameters. The other important method we're interested in is the find method, which retrieves the record based on the criteria provided as method arguments.

So that was the implementation of the MongoDB driver, and I tried to keep it as simple as possible.

Set Up the User Model

Adhering to the standards of the authentication system, we need to implement the User model that must implement the Illuminate\Contracts\Auth\Authenticatable contract.

Go ahead and create a file app/Models/Auth/User.php with the following contents.

<?php // app/Models/Auth/User.php namespace App\Models\Auth; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use App\Services\Contracts\NosqlServiceInterface; class User implements AuthenticatableContract { private $conn; private $username; private $password; protected $rememberTokenName = 'remember_token'; public function __construct(NosqlServiceInterface $conn) { $this->conn = $conn; } /** * Fetch user by Credentials * * @param array $credentials * @return Illuminate\Contracts\Auth\Authenticatable */ public function fetchUserByCredentials(Array $credentials) { $arr_user = $this->conn->find('users', ['username' => $credentials['username']]); if (! is_null($arr_user)) { $this->username = $arr_user['username']; $this->password = $arr_user['password']; } return $this; } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifierName() */ public function getAuthIdentifierName() { return "username"; } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifier() */ public function getAuthIdentifier() { return $this->{$this->getAuthIdentifierName()}; } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthPassword() */ public function getAuthPassword() { return $this->password; } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::getRememberToken() */ public function getRememberToken() { if (! empty($this->getRememberTokenName())) { return $this->{$this->getRememberTokenName()}; } } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::setRememberToken() */ public function setRememberToken($value) { if (! empty($this->getRememberTokenName())) { $this->{$this->getRememberTokenName()} = $value; } } /** * {@inheritDoc} * @see \Illuminate\Contracts\Auth\Authenticatable::getRememberTokenName() */ public function getRememberTokenName() { return $this->rememberTokenName; } }

You should have already noticed that App\Models\Auth\User implements the Illuminate\Contracts\Auth\Authenticatable contract.

Most of the methods implemented in our class are self-explanatory. Having said that, we've defined the fetchUserByCredentials method, which retrieves the user from the available back end. In our case, it'll be a MongoDatabase class that'll be called to retrieve the necessary information.

So that's the implementation of the User model.

Set Up the Authentication Provider

As we discussed earlier, the Laravel authentication system consists of two elements—guards and providers.

In this section, we'll create an authentication provider that deals with the user retrieval from the back end.

Go ahead and create a file app/Extensions/MongoUserProvider.php as shown below.

<?php // app/Extensions/MongoUserProvider.php namespace App\Extensions; use Illuminate\Support\Str; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\Authenticatable; class MongoUserProvider implements UserProvider { /** * The Mongo User Model */ private $model; /** * Create a new mongo user provider. * * @return \Illuminate\Contracts\Auth\Authenticatable|null * @return void */ public function __construct(\App\Models\Auth\User $userModel) { $this->model = $userModel; } /** * Retrieve a user by the given credentials. * * @param array $credentials * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByCredentials(array $credentials) { if (empty($credentials)) { return; } $user = $this->model->fetchUserByCredentials(['username' => $credentials['username']]); return $user; } /** * Validate a user against the given credentials. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param array $credentials Request credentials * @return bool */ public function validateCredentials(Authenticatable $user, Array $credentials) { return ($credentials['username'] == $user->getAuthIdentifier() && md5($credentials['password']) == $user->getAuthPassword()); } public function retrieveById($identifier) {} public function retrieveByToken($identifier, $token) {} public function updateRememberToken(Authenticatable $user, $token) {} }

Again, you need to make sure that the custom provider must implement the Illuminate\Contracts\Auth\UserProvider contract.

Moving ahead, it defines two important methods—retrieveByCredentials and validateCredentials.

The retrieveByCredentials method is used to retrieve the user credentials using the User model class that was discussed in the earlier section. On the other hand, the validateCredentials method is used to validate a user against the given set of credentials.

And that was the implementation of our custom authentication provider. In the next section, we'll go ahead and create a guard that interacts with the MongoUserProvider authentication provider.

Set Up the Authentication Guard

As we discussed earlier, the guard in the Laravel authentication system provisions how the user is authenticated. In our case, we'll check the presence of the jsondata request parameter that should contain the JSON-encoded string of the credentials.

In this section, we'll create a guard that interacts with the authentication provider that was just created in the last section.

Go ahead and create a file app/Services/Auth/JsonGuard.php with the following contents.

<?php // app/Services/Auth/JsonGuard.php namespace App\Services\Auth; use Illuminate\Http\Request; use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\UserProvider; use GuzzleHttp\json_decode; use phpDocumentor\Reflection\Types\Array_; use Illuminate\Contracts\Auth\Authenticatable; class JsonGuard implements Guard { protected $request; protected $provider; protected $user; /** * Create a new authentication guard. * * @param \Illuminate\Contracts\Auth\UserProvider $provider * @param \Illuminate\Http\Request $request * @return void */ public function __construct(UserProvider $provider, Request $request) { $this->request = $request; $this->provider = $provider; $this->user = NULL; } /** * Determine if the current user is authenticated. * * @return bool */ public function check() { return ! is_null($this->user()); } /** * Determine if the current user is a guest. * * @return bool */ public function guest() { return ! $this->check(); } /** * Get the currently authenticated user. * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function user() { if (! is_null($this->user)) { return $this->user; } } /** * Get the JSON params from the current request * * @return string */ public function getJsonParams() { $jsondata = $this->request->query('jsondata'); return (!empty($jsondata) ? json_decode($jsondata, TRUE) : NULL); } /** * Get the ID for the currently authenticated user. * * @return string|null */ public function id() { if ($user = $this->user()) { return $this->user()->getAuthIdentifier(); } } /** * Validate a user's credentials. * * @return bool */ public function validate(Array $credentials=[]) { if (empty($credentials['username']) || empty($credentials['password'])) { if (!$credentials=$this->getJsonParams()) { return false; } } $user = $this->provider->retrieveByCredentials($credentials); if (! is_null($user) && $this->provider->validateCredentials($user, $credentials)) { $this->setUser($user); return true; } else { return false; } } /** * Set the current user. * * @param Array $user User info * @return void */ public function setUser(Authenticatable $user) { $this->user = $user; return $this; } }

First of all, our class needs to implement the Illuminate\Contracts\Auth\Guard interface. Thus, we need to define all the methods declared in that interface.

The important thing to note here is that the __construct function requires an implementation of Illuminate\Contracts\Auth\UserProvider. In our case, we'll pass an instance of App\Extensions\MongoUserProvider, as we'll see in the later section.

Next, there's a function getJsonParams that retrieves the user credentials from the request parameter named jsondata. As it's expected that we'll receive a JSON encoded string of the user credentials, we've used the json_decode function to decode the JSON data.

In the validate function, the first thing we check is the existence of the $credentials argument. If it's not present, we'll call the getJsonParams method to retrieve user credentials from the request parameters.

Next, we call the retrieveByCredentials method of the MongoUserProvider provider that retrieves the user from the MongoDB database back end. Finally, it's the validateCredentials method of the MongoUserProvider provider that checks the validity of the User.

So that was the implementation of our custom guard. The next section describes how to stitch these pieces together to form a successful authentication system.

Putting It All Together

So far, we've developed all the elements of the custom authentication guard that should provide us a new authentication system. However, it won't work out of the box as we need to register it in the first place using the Laravel service container bindings.

As you should already know, the Laravel service provider is the right place to implement the necessary bindings.

Go ahead and open the file app/Providers/AuthServiceProvider.php that allows us to add authentication service container bindings. If it doesn't contain any custom changes, you could just replace it with the following contents.

<?php // app/Providers/AuthServiceProvider.php namespace App\Providers; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use App\Services\Auth\JsonGuard; use App\Extensions\MongoUserProvider; use App\Database\MongoDatabase; use App\Models\Auth\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; class AuthServiceProvider extends ServiceProvider { /** * The policy mappings for the application. * * @var array */ protected $policies = [ 'App\Model' => 'App\Policies\ModelPolicy', ]; /** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); $this->app->bind('App\Database\MongoDatabase', function ($app) { return new MongoDatabase(config('mongo.defaults.host'), config('mongo.defaults.port'), config('mongo.defaults.database')); }); $this->app->bind('App\Models\Auth\User', function ($app) { return new User($app->make('App\Database\MongoDatabase')); }); // add custom guard provider Auth::provider('mongo', function ($app, array $config) { return new MongoUserProvider($app->make('App\Models\Auth\User')); }); // add custom guard Auth::extend('json', function ($app, $name, array $config) { return new JsonGuard(Auth::createUserProvider($config['provider']), $app->make('request')); }); } public function register() { $this->app->bind( 'App\Services\Contracts\NosqlServiceInterface', 'App\Database\MongoDatabase' ); } }

Let's go through the boot method that contains most of the provider bindings.

To start with, we'll create bindings for the App\Database\MongoDatabase and App\Models\Auth\User elements.

$this->app->bind('App\Database\MongoDatabase', function ($app) { return new MongoDatabase(config('mongo.defaults.host'), config('mongo.defaults.port'), config('mongo.defaults.database')); }); $this->app->bind('App\Models\Auth\User', function ($app) { return new User($app->make('App\Database\MongoDatabase')); });

It's been a while that we've been talking about provider and guard, and it's time to plug our custom guard into the Laravel authentication system.

We've used the provider method of the Auth Facade to add our custom authentication provider under the key mongo. Recall that the key reflects the settings that were added earlier in the auth.php file.

Auth::provider('mongo', function ($app, array $config) { return new MongoUserProvider($app->make('App\Models\Auth\User')); });

In a similar way, we'll inject our custom guard implementation using the extend method of the Auth facade.

Auth::extend('json', function ($app, $name, array $config) { return new JsonGuard(Auth::createUserProvider($config['provider']), $app->make('request')); });

Next, there's a register method that we've used to bind the App\Services\Contracts\NosqlServiceInterface interface to the App\Database\MongoDatabase implementation.

$this->app->bind( 'App\Services\Contracts\NosqlServiceInterface', 'App\Database\MongoDatabase' );

So whenever there's a need to resolve the App\Services\Contracts\NosqlServiceInterface dependency, Laravel responds with the implementation of the App\Database\MongoDatabase adapter.

The benefit of using this approach is that one could easily swap the given implementation with a custom implementation. For example, let's say someone would like to replace the App\Database\MongoDatabase implementation with the CouchDB adapter in future. In that case, they just need to add the corresponding binding in the register method.

So that was the service provider at your disposal. At this moment, we have everything that is required to test our custom guard implementation, so the next and concluding section is all about that.

Does It Work?

You've done all the hard work setting up your first custom authentication guard, and now it's time to reap the benefits as we'll go ahead and give it a try.

Let's quickly implement a pretty basic controller file app/Http/Controllers/MongoController.php as shown below.

<?php // app/Http/Controllers/MongoController.php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Contracts\Auth\Guard; class MongoController extends Controller { public function login(Guard $auth_guard) { if ($auth_guard->validate()) { // get the current authenticated user $user = $auth_guard->user(); echo 'Success!'; } else { echo 'Not authorized to access this page!'; } } }

Take a close look at the dependency of the login method, which requires the implementation of the Illuminate\Contracts\Auth\Guard guard. Since we've set the custom guard as the default guard in the auth.php file, it's the App\Services\Auth\JsonGuard that'll be injected actually!

Next, we've called the validate method of the App\Services\Auth\JsonGuard class, which in turn initiates a series of method calls:

  • It calls the retrieveByCredentials method of the App\Extensions\MongoUserProvider class.
  • The retrieveByCredentials method calls the fetchUserByCredentials method of the User App\Models\Auth\User class.
  • The fetchUserByCredentials method calls the find method of the App\Database\MongoDatabase to retrieve the user credentials.
  • Finally, the find method of the App\Database\MongoDatabase returns the response!

If everything works as expected, we should get an authenticated user by calling the user method of our guard.

To access the controller, you should add an associated route in the routes/web.php file.

Route::get('/custom/mongo/login', 'MongoController@login');

Try accessing the URL http://your-laravel-site/custom/mongo/login without passing any parameters and you should see a "not authorized" message.

On the other hand, try something like http://your-laravel-site/custom/mongo/login?jsondata={"username":"admin","password":"admin"} and that should return a success message if the user is present in your database.

Please note that this is just for example purposes, to demonstrate how the custom guard works. You should implement a foolproof solution for a feature like login. In fact, I've just provided an insight into the authentication flow; you're responsible for building a robust and secure solution for your application.

That ends our journey today, and hopefully I'll be back with more useful stuff. If you want me to write on any specific topics, don't forget to drop me a line!

Conclusion

The Laravel framework provides a solid authentication system in the core that could be extended if you want to implement a custom one. That was the topic of today's article to implement a custom guard and plug it in to the Laravel authentication workflow.

In the course of that, we went ahead and developed a system that authenticates the user based on the JSON payload in the request and matches it with the MongoDB database. And to achieve that, we ended up creating a custom guard and a custom provider implementation.

I hope the exercise has provided you an insight into the Laravel authentication flow, and you should now feel more confident about its inner workings.

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 on Envato Market.

I would love to hear your feedback and suggestions, so shout out loud using the feed below!

Categories: Web Design

10 Simple Tips To Improve User Testing

Smashing Magazine - Wed, 11/08/2017 - 16:17
(This is a sponsored post). Testing is a fundamental part of the UX designer’s job and a core part of the overall UX design process. Testing provides the inspiration, guidance and validation that product teams need in order to design great products. That’s why the most effective teams make testing a habit. Usability testing involves observing users as they use a product. It helps you find where users struggle and what they like.
Categories: Web Design

Maximizing The Design Sprint

Smashing Magazine - Tue, 11/07/2017 - 14:54
Following a summer of Wonder Woman, Spiderman, and other superhero blockbusters, it’s natural to fantasize about having a superpower of your own. Luckily for designers, innovators, and customer-centric thinkers, a design sprint allows you to see into the future to learn in just five days what customers think about your finished product. As a UX consultant and in-house design strategist, I have facilitated dozens upon dozens of design workshops (ranging from rapid prototyping sessions to, of course, sprints).
Categories: Web Design

Working With Tables in React, Part Two

Tuts+ Code - Web Development - Tue, 11/07/2017 - 04:00
Overview

This is part two of a two-part series about React-Bootstrap-Table. In part one we created a simple React application using react-create-app, added React-Bootstrap-Table, populated a table with data, worked with columns, styled the table, and selected rows. 

In this part we'll continue the journey by expanding rows, adding rows, deleting rows, and covering pagination, cell editing, and advanced customization.

Expanding Rows

This is one of the coolest features of React-bootstrap-table. When displaying tabular data, there is often additional data you may want to see on one or two rows, but you can't display all the data for all the rows. 

One option to address that is to show tooltips, but tooltips require that you hover with the mouse over the target area, and you can only see one tooltip at a time. Expanding rows let you display additional data for each row in a kind of drawer that stays expanded as long as you want, and you can collapse it back when you're done. You can expand as many rows as you want at the same time. Here is how it's done with React-bootstrap-table. 

The love map contains relationships between some Arrested Development characters: Gob loves Marta and Buster loves Lucile 2. The isExpandable() function controls which rows are expandable. In this case, it returns true for rows whose character name is in the love map. The expandRow() function returns a component when a row is expanded. 

The returned component is displayed below the row until the row is collapsed. Configuring row expansion is a little tricky. Some options are just props on the BootstrapTable component. 

The expand column options are one object prop, and then there is a prop called options that contains additional row expansion options like expandRowBgColor and expanding. It would be much simpler if there was just one prop called expandRowProp that contained all the options (like the selectRowProp).

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' const loveMap = { Gob: 'Martha', Buster: 'Lucile 2', } function isExpandableRow(row) { return row['name'] in loveMap; } function expandRow(row) { return ( <p>{row['name']} loves {loveMap[row['name']]}.</p> ); } class Table5 extends Component { render() { const options = { expandRowBgColor: 'pink', expanding: [1] // initially expanded } return ( <div> <BootstrapTable data={this.props.data} expandableRow={isExpandableRow} expandComponent={expandRow} expandColumnOptions={ {expandColumnVisible: true}} options={options} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table5Pagination

So far we displayed just three rows of data. Tables are designed to display a lot of data that doesn't necessarily fit on the screen at the same time. That's where pagination comes in. React-bootstrap-table supports many pagination options. 

Let's populate our table with 100 items, which will be ten pages of ten items each. We will use a getData() function that returns an array of 100 objects with ids, names, and values based on their index. 

Let's also specify which page to display initially (4), customize the text for prev, next, first and last page (using Unicode arrows for extra coolness) and finally provide a custom function called showTotal() to display the total number of items. Note that the attribute for controlling the previous page button is called "prePage" and not "prevPage" (it got me). All the pagination options go into the general "options" attribute of the table. 

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' function getData() { var data = [] for (var i = 0; i < 100; ++i) { data[i] = {id: i, name: 'item_' + i, value: i} } return data } function showTotal() { return <p>There are 100 items total</p> } class Table6 extends Component { render() { const options = { page: 4, prePage: '⟵', nextPage: '⟶', firstPage: '⟸', lastPage: '⟹', paginationShowsTotal: showTotal } return ( <div> <BootstrapTable data={getData()} pagination={true} options={options} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table6Adding and Deleting Rows

So far we used the table to display information in a variety of ways. But tables can be used as a user interface for manipulating data. Let's see how to add and remove rows from a table. 

The key attributes are insertRow and deleteRow. When you specify them, "New" and "Delete" buttons are added. If you click the "New" button, a modal dialog opens up and lets you add new rows. If you click the "Delete" button, all selected rows are deleted. To delete rows, you must enable row selection, of course. You can also attach hook functions that are called after adding or deleting rows.

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' function onInsertRow(row) { let newRowStr = '' for (const prop in row) { newRowStr += prop + ': ' + row[prop] + ' \n' } alert('You inserted:\n ' + newRowStr) } function onDeleteRow(rowKeys) { alert('You deleted: ' + rowKeys) } class Table7 extends Component { render() { const options = { afterInsertRow: onInsertRow, afterDeleteRow: onDeleteRow } // To delete rows you be able to select rows const selectRowProp = { mode: 'checkbox' } return ( <div> <BootstrapTable data={this.props.data} insertRow={true} deleteRow={true} selectRow={selectRowProp} options={options} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table7Cell Editing

Another form of data manipulation is in-place editing (a.k.a. cell editing). Cell editing can be triggered by a click or double-click. Cell editing is controlled by the "cellEdit" attribute. In addition to the mode, you can specify non-editable rows and hook functions. 

In the following code, the nonEditableRows function simply returns the row key 3, but could use a more sophisticated logic.

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' class Table8 extends Component { render() { const cellEditProp = { mode: 'click', // 'dbclick' for trigger by double-click nonEditableRows: function() { return [3]; } } return ( <div> <BootstrapTable data={this.props.data} cellEdit={cellEditProp} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table8Exporting Your Data

Sometimes, viewing your data and playing with it in a web UI is not enough, and you need to take your data and feed it to other tools. The way to do it with React-bootstrap-table is very simple. You just add the exportCSV attribute (and optionally a filename) and an export button is added. When you click the button, it allows you to save your data to a CSV file.

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' class Table9 extends Component { render() { return ( <div> <BootstrapTable data={this.props.data} exportCSV csvFileName='data.csv' > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table9

Here is the exported data from our little table:

"id","name","value" "1","Gob","2" "2","Buster","5" "3","George Michael","4" Customizing All the Things

We covered a lot of material, but React-bootstrap-table has a lot more in store. Practically, every aspect can be customized. Read the full documentation on how to customize a table.

Here is a list of the customizable parts:

  • Cell
  • Toolbar
  • Insert Modal
  • Pagination
  • Column Filter
  • Cell Editing
  • Row Selection Column
Conclusion

React-bootstrap-table packs a powerful punch. It provides out of the box a pleasant user interface for displaying, searching and manipulating tabular data. The API is very consistent—major features can be enabled by specifying a simple attribute and optionally customized with additional attributes that often can be dynamic functions. 

While the default behavior and the basic configuration should satisfy most users, if you need more advanced features and customization, there is ample documentation and examples how to go about it.

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.

Categories: Web Design

Introducing Stockio: Free Stock Photography, Vectors, Videos & Fonts

One of the hardest aspects of any kind of design or content creation is finding good quality free imagery, so Stockio – a new option for finding free photography...

The post Introducing Stockio: Free Stock Photography, Vectors, Videos & Fonts appeared first on Onextrapixel.

Categories: Web Design

Right-To-Left Development In Mobile Design

Smashing Magazine - Mon, 11/06/2017 - 11:04
The Middle Eastern market is growing at a rapid pace, and, as a result, demand for various IT products is also booming in the region. What is peculiar, though, is that Middle Eastern countries require design that is not only compatible with their needs and comfortable for their users, but that is also suitable to their language standards, making a serious adaptation process very important. Given that most languages spoken in the Middle East are written and read from right to left (such as Arabic, Hebrew and Urdu), developers often face a range of problems when creating products in those languages.
Categories: Web Design

Working With Tables in React, Part One

Tuts+ Code - Web Development - Mon, 11/06/2017 - 04:00
Overview

One of the most common user interface elements for presenting your data is a table. It turns out that there are many aspects to control when working with tables, such as:

  • defining columns and headers
  • various cell formats (text, numbers, check boxes)
  • resizing
  • filtering
  • dynamic growing
  • styling

In this two-part series, you'll will learn about the ins and outs of working with tabular data in React using the React Bootstrap Table component. You'll be able to create sophisticated and professional looking tables by default with little effort and yet be able to customize every part you want to.

Create a Vanilla React App

I assume you are familiar with React itself and will focus on working with React Bootstrap Table. Envato Tuts+ has a great series on React you can read for background.

In a nutshell, I used react-create-app to create a vanilla React app and then installed the react-bootstrap-table: npm install react-bootstrap-table --save.

It's important to add the bootstrap CSS to the public/index.html file.

<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="theme-color" content="#000000"> <link rel="manifest" href="%PUBLIC_URL%/manifest.json"> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> <!-- Latest compiled and minified bootstrap CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest /css/bootstrap.min.css"> <!-- bootstrap theme (Optional) --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest /css/bootstrap-theme.min.css"> <title>React App</title> </head> <body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div> </body> </html> WebStorm

If you use JetBrains' WebStorm and you want to run the tests, add --env=jsdom in your run config.

Basic Table

We will start with a basic table. Here is a basic table component. It imports the BoostrapTable and TableHeaderColumn from react-bootstrap-table and also the CSS from the dist directory. 

The render() method renders a table with three columns: "ID", "Name", and "Value". The actual data in the table comes from the "data" property (this.props.data). The data contains the names of some characters from the hilarious show Arrested Development.

import React, { Component } from 'react'; import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; import '../css/Table.css'; import '../../node_modules/react-bootstrap-table/css/ react-bootstrap-table.css' class Table1 extends Component { render() { return ( <div> <BootstrapTable data={this.props.data}> <TableHeaderColumn isKey dataField='id'> ID </TableHeaderColumn> <TableHeaderColumn dataField='name'> Name </TableHeaderColumn> <TableHeaderColumn dataField='value'> Value </TableHeaderColumn> </BootstrapTable> </div> ); } } export default Table1;

I instantiated the BasicTable in the render() method of the standard App.js and passed some hard-coded data:

import React, { Component } from 'react'; import './App.css'; import Table1 from './components/Table1' var data = [ {id: 1, name: 'Gob', value: '2'}, {id: 2, name: 'Buster', value: '5'}, {id: 3, name: 'George Michael', value: '4'} ]; class App extends Component { render() { return ( <div className="App"> <p className="Table-header">Basic Table</p> <Table1 data={data}/> </div> ); } } export default App;

To view the table, type: npm start. The configuration created by create-react-app watches over your code and will recompile whenever you change anything, so you need to run only once and then every change will automatically be reflected.

Compiled successfully! You can now view react-table in the browser. Local: http://localhost:3000/ On Your Network: http://192.168.1.136:3000/ Note that the development build is not optimized. To create a production build, use yarn build.

Here is the result:

Note that each column has exactly the same width.

Working With Columns

You can control many aspects of the columns. In particular, the column widths can be specified in absolute units as percentages or left unspecified. The column width of unspecified columns is the remainder divided equally. For example, for a table width of 100 px, one column specified 15 px, a second column specified 25% (25 px), and a third column specified 30% (15 px). 

Two other columns didn't specify a width. Columns 1, 2 and 3 used 70 px together, which leaves 30 px for columns 4 and 5, which will divide it equally. Column 4 and 5 will each have a width of 15 px. Note that if the table is resized, the numbers will change. Only column 1 will always be 15 px wide. 

The other columns will be computed based on the table width. You can also manage the alignment of text and columns as well as the style of headers and columns. Here is an example of how to specify different column widths, text alignment, and custom styles:

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' class Table2 extends Component { render() { return ( <div> <BootstrapTable data={this.props.data}> <TableHeaderColumn isKey dataField='id' dataAlign='center' headerAlign="left" width="30" tdStyle={ {backgroundColor: 'green'}}> ID </TableHeaderColumn> <TableHeaderColumn dataField='name' dataAlign='center' headerAlign="center" width="20%" thStyle={ {fontWeight: 'lighter', color: 'red'}}> Name </TableHeaderColumn> <TableHeaderColumn dataField='value' dataAlign='center' headerAlign="right"> Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table2Styling Your Table

You saw how to style individual columns and headers, but styling can go much further. React-bootstrap-table provides a lot of options for customization. First you can simply add the striped and hover attributes to the BootstrapTable component to get alternate background colors on each row: <BootstrapTable data={this.props.data} striped hover>

To style all rows, use trClassName: <BootstrapTable data={data} trClassName='tr-style'> 

If you want to get really fancy, the trStyle can a function. Check out the following table component that styles differently rows where the name is "George Michael":

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' function rowClassNameFormat(row, rowIdx) { // row is whole row object // rowIdx is index of row console.log(row) return row['name'] === 'George Michael' ? 'GeorgeMichael-Row' : 'Other-Row'; } class Table3 extends Component { render() { return ( <div> <BootstrapTable data={this.props.data} trClassName={rowClassNameFormat} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table3

The GeorgeMichael-Row and Other-Row CSS classes are defined in Table.css:

.Table-header { background-color: #ccc; color: blue; padding: 10px; text-align: left; } .GeorgeMichael-Row { background-color: plum; } .Other-Row { background-color: greenyellow; }Selecting Rows

Once you have your data in a table, you may want to select some rows in order to perform some operations on them. React-bootstrap-table provides a wide variety of selection options. All the options are organized in a single object you pass to the component as the selectRow attribute. Here are some of the selection options:

  • single selection mode (radio button)
  • multi-selection mode (checkbox)
  • configurable column selection width
  • select on row click: by default you must click the selector (radio button or checkbox)
  • hide selection column (useful if select on row click is true)
  • change background color on selection
  • initial selected rows
  • selection hooks (on single row or when all rows are selected).

The following components demonstrate many of these options:

import React, {Component} from 'react' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import '../css/Table.css' import '../dist/react-bootstrap-table-all.min.css' function onSelectRow(row, isSelected, e) { if (isSelected) { alert(`You just selected '${row['name']}'`) } } const selectRowProp = { mode: 'checkbox', clickToSelect: true, unselectable: [2], selected: [1], onSelect: onSelectRow, bgColor: 'gold' }; class Table4 extends Component { render() { return ( <div> <BootstrapTable data={this.props.data} selectRow={selectRowProp} > <TableHeaderColumn isKey dataField='id' > ID </TableHeaderColumn> <TableHeaderColumn dataField='name' > Name </TableHeaderColumn> <TableHeaderColumn dataField='value' > Value </TableHeaderColumn> </BootstrapTable> </div> ) } } export default Table4Conclusion

In this part, we created a simple React application using react-create-app, added React-Bootstrap-Table, populated a table with data, worked with columns, styled the table, and selected rows. 

In the next part, we'll continue the journey by expanding rows, adding rows, deleting rows, and covering pagination, cell editing, and advanced customization. Stay tuned.

Categories: Web Design

How to Set Up a Scalable, E-Commerce-Ready WordPress Site Using ClusterCS

Tuts+ Code - Web Development - Mon, 11/06/2017 - 03:00

When a website, such as an online store, is at the core of your business, the site's ability to serve customers through traffic spikes and sudden server failures can mean the difference between a big win and a frustrating loss. 

So, while setting up a single virtual server for the job is easier than ever, it's worth taking a few moments to consider a high-availability cluster. In a cluster, instead of running everything on one machine, the different parts of the server architecture are handled by separate server instances. In such a setup, you can scale the server by adding more workers when the load grows and keep the server running even if one or more of the workers break for any reason.

When you are well prepared, you can stay assured that this year's Black Friday sales or a sudden feature won’t bring your online store down.

In this tutorial, you will learn how to use ClusterCS and Amazon Web Services to create a scalable cluster of servers and to use it to host a WordPress site for a WooCommerce-based e-commerce website.

1. Set Up Your ClusterCS Account

ClusterCS is a control panel for servers in the cloud. It brings the power of regular control panels to your virtual and dedicated servers, collecting them all behind a single interface. The tool was originally created for handling the hosting accounts managed by Soft Dreams, the company behind it, so it has been well tested in real-life production environments.

ClusterCS supports multi-server applications such as the e-commerce-optimized WordPress site we'll build in this tutorial, as well as maintaining individual servers.

To get started, visit the ClusterCS website and create an account.

A free account lets you manage a single server and up to five separate domains running on it. A cluster setup split on multiple servers requires a paid account, but many of the steps you'll see in the tutorial are applicable also on a single-server setup.

2. Start Some Virtual Servers

As a cloud-based control panel, ClusterCS works with any virtual or dedicated server: you can use it to control servers on Digital Ocean, Amazon Web Services (AWS), or even on a virtual machine running on your laptop.

In this tutorial, we'll go with AWS. If you don’t have an AWS account yet, sign up for one. Then, sign in to the AWS Console to create the server instances for our website setup.

The setup we'll create in this tutorial is as follows:

  • Load balancer (lb): This instance will be the outward-facing part of the cluster. It'll also hold the files for your website.
  • Two (or more) application servers (app1, app2): These instances will run the web server. Having more than one application server makes the setup more resilient—if one goes down, the others can still serve the application's pages. Also, adding more application servers is a quick way to respond to an increase in traffic.
  • Database server (db): Having the database separate from the app servers adds a level of security while making the database available for all servers using it.

Let's get the servers up and running!

Step 1: Create a Security Group

When you launch servers for a cluster, it's important to make sure they can talk to each other and that ClusterCS can reach them using SSH. 

At Amazon Web Services, firewall configuration is done using a Security Group. We'll create one right at the beginning, but if you have connection troubles at any time, you can always return to verify and modify the settings. 

In the AWS EC2 Admin console's left side menu, click on Security Groups. Then click on Create Security Group

In the popup that opens, give your security group a name and description. Then, click on Add Rule to add new rules one by one.

First, allow SSH access from the ClusterCS IP addresses 85.9.60.46/32 and 193.226.133.91/32. Then, allow HTTP and HTTPS access (ports 80 and 443) from everywhere so that your customers can access your website.

At this point, your security group setup will look like this:

Click Create to save the changes.

Now that you have created the security group, it has an ID, which you can use to configure the access between your cluster's server instances.

In the list of security groups, select the one you just created and open its Inbound tab. You'll see the rules you just created. Copy the security group's ID (a string starting with sg-) and click on the Edit button to modify the rules.

Add a new rule with the security group's ID as the traffic source. You can either choose to allow all TCP traffic from this source or, if you prefer being more specific, use the following list of ports: 80, 443, 25, 110, 143, 465, 587, 993, 995, 2049, 111, 892, 662, 32803, 21, 2049, 111, 892, 662, 32769, 8080, 8081, and 8082.

At this point, your configuration will look like this:

To be able to connect to the AWS instances from your computer, add an SSH rule for your IP. The AWS console has an option for this ("My IP"), so you don't need to look up your IP.

Finally, to allow FTP access for uploading files to the server cluster, add ports 21 and  50500-50900.

Once that's done, you are ready to start some servers.

Step 2: Start Four AWS EC2 Server Instances

With your security group in place, it's time to launch the AWS instances for the cluster. AWS gives you many options for customizing the virtual servers, but most of the time, the default options are a good choice.

Return to the EC2 Dashboard and click the Launch Instance button.

Clicking the button starts a seven-step wizard for launching the server. 

In Step 1, you get to select the base image for your virtual server. Pick the first option, Amazon Linux AMI, by clicking the Select button next to it.

In Step 2, choose an instance type that fits the needs of your website; a smaller instance won't be able to handle as much traffic as a larger one, but a larger one will be more expensive. Consider the roles the machines will play in your cluster. For example, you can quickly add new application servers in response to an increase in server load, but adding database resources is much more difficult. That's why it's a good idea to run the database on a more powerful server with more memory than on the app servers.

In steps 3 to 5 of the Launch Wizard, go through the options to see if there's anything you'd like to modify—but most likely, the default options will be good for your setup.

In Step 6, choose the option Select an existing security group, and choose the security group we defined above. 

Finally, review your settings and click Launch to start the instances.

As the last step, AWS will ask you to specify and download an SSH key pair for accessing the servers.

Select the Create a new key pair option and enter a descriptive name for the key pair. Then click Download Key Pair to download the private key file. 

Save the key in a safe place on your computer. I like to place all my SSH keys in the  ~/.ssh directory, but you can choose any location.

Once you have downloaded the key, the Launch Instances button becomes clickable. Click on it, and wait a few minutes for the instances to start.

If you are starting instances one by one, repeat the process for the next instances until all four servers for the cluster are up and running.

Step 3: Attach Elastic IP Addresses to Your Instances

AWS assigns IP addresses to your instances when you start them. Stopping an instance releases its IP address. As ClusterCS relies on the server's IP addresses to connect to them, this can lead to trouble if for any reason you need to restart your instances.

To get past this issue, you can use AWS's Elastic IP Addresses feature. 

An Elastic IP address is a permanent IP address that you can attach to any of your server instances. By using that address in your ClusterCS configuration, you can keep it pointing to the instance even if the instance is stopped.

Click on Elastic IPs in the AWS Console's left side menu. Then, click on the Allocate new address button.

Select VPC as Scope, and click Allocate. An IP address is immediately allocated for you to use.

Right click on the IP address, and select Associate Address to link the IP to an EC2 server instance. 

Click on the Instance text field. Then, select an instance from a dropdown menu showing all your instances. Finally, click Associate. Repeat the process for the rest of your EC2 instances.

Now, your servers can be accessed using their Elastic IP address, even after they have been restarted.

Step 4: Verify That You Can Access the Servers

Once your EC2 instances are up and running, before moving to ClusterCS, it's good to verify that you can connect to them.

First, change the SSH key's permissions:

chmod 400 ~/.ssh/key_name.pem

Next, look up the instance's public IP address (IPv4 Public IP) from the EC2 Dashboard:

Then, connect to it, for example using the command-line client (or PuTTY if you are using Windows):

ssh -i ~/.ssh/tutorial-clustercs.pem ec2-user@54.162.85.168

If you have trouble connecting, return to the Security Group settings and make sure all of the required ports are accessible. 

When you see that you can connect to the server, you are ready to move over to ClusterCS to set up the software for the servers on your cluster.

3. Set Up the Server Cluster

The server instances are now up and running. It's time to configure them to do their part in running your e-commerce website as parts of a multi-server cluster. We will do this using the ClusterCS control panel.

The ClusterCS admin is split into two parts: Servers and Domains.

The Servers section defines the underlying system: the server configuration and the software running on the one or more machines that form it. The Domains part, which we'll look into in a bit, specifies customer accounts and sites that run on top of the low-level configuration.

Step 1: Add Server Instances to the Cluster

Click on Manage Servers to start configuring the cluster. If you use the free version, you won't be able to create a cluster, but the steps in setting up a single server are very similar to what we'll do in this part of the tutorial.


Click on Add Cluster

Then, on the next page, scroll down to the Add new server section. There, you'll see some instructions, followed by a form.

To add the first server to the cluster, fill the form with the following information: 

  • Server IP: The server's public IP address. You can find it from the EC2 Dashboard.
  • SSH Port: 22
  • User: ec2-user
  • Login type: SSH key
  • Host Name: A name that makes it easy for you to keep track of what the machine does. The field is only used as an identifier, so you can use any name you like, for example, lb for the load balancer and app1 for the first application server, and so on.

When you choose "SSH key" as the login type, a text area for entering the SSH key appears. Copy the contents of the .pem file you downloaded from AWS when starting the machines and paste it in this text area. Leave the SSH Key Password field empty.

Once all the data is in place, click Next.

ClusterCS will now connect to the server and verify that it matches the setup's requirements. Once the check finishes, you'll see the following results.

Click on Add Server to Cluster Pool.

You'll see that the server was added to the list of servers that are going to make up your cluster. 

Scroll down and repeat the process for the remaining three servers. 

Only then, click on Continue to Setup to configure the cluster and the services you wish to run on its different servers.

Step 2: Configure the Cluster

Now that all the servers are available for ClusterCS to use, you can start the fun part of choosing the roles for each of the servers. 

Give your setup a name and choose a recipe, a template configuration, to use as a basis for the configuration.

ClusterCS comes with an optimized starting point for PHP-based applications, called  Smart web server (optimized LAMP). On a single machine setup where the entire configuration runs on a single server, this recipe is ready to be used out of the box.

In a cluster setup, you will need to do a little more configuration.

Click Customize to open a detailed view. You will see the following list of "layers", combinations of software modules that together give the servers their capabilities. All of the functionality within a layer can be configured, and you can also create new layers and add new applications to existing ones.

Start from the first layer, Firewall, by clicking on the Manage link next to its title. You'll see the following view for specifying the details of this layer. 

On the left side, you can choose which modules make up the layer (we'll use the default set of modules, so you can leave that part as is). 

On the right side, you can select the servers that will run this layer. A firewall is something every server in the cluster needs to have, so select all four servers by clicking their names.

Then click Save to return to the cluster configuration view. Now, the Firewall layer looks like this:

Go through the rest of the layers in your cluster and create the following configuration:

Layer Servers Firewall lb, db, app1, app2 Smart Traffic Manager lb Webserver app1, app2 Database db Email Not used, remove from configuration Webmail Not used, remove from configuration Storage lb, app1, app2 Utilities app1, app2

In this tutorial, there's no need for email, so I removed the email-related layers from the configuration. If you want to use email in your setup, I suggest adding a separate server instance for it. That way, issues with your web server won't interfere with your email or vice versa.

I activated the Storage layer on the load balancer machine as well as on the app servers. Later, when configuring the site, we'll select the lb server as the one that holds its files. The two other machines will be mounted to it using NFS, so they too will have access to the data. This way, your website won't be dependent on any single app server, and you can scale them up and down freely.

When everything looks good, click Save to save your configuration and start the installation process on the servers. 

The setup will take about 20 minutes to complete, depending on the size of your cluster.

4. Configure the Domain

You have now created a server cluster with a database server, two application servers, and a load balancer carefully routing traffic to the different instances. It's time to start using the cluster by setting up a website to run on top of it.

Click on Domains in the ClusterCS top menu.

Then, click on Add Domain.

On this screen, you specify how the cluster (or a single server) is used for your domain. 

  • Domain Name: The domain name of your website. You can either configure the domain's DNS settings outside of ClusterCS (point the domain to your load balancer instance's public IP address) or check the Enable DNS Management option at the bottom of the screen and use ClusterCS's name servers.
  • Username / Password: The username and password combination you (or your customer) will use to access the site using FTP.
  • Login Type: The method you want to use (or let your customers use) for accessing the server. You will still be able to connect to the underlying AWS instances using the SSH key method, but that's probably not something you want your customers or other site admins to do.
  • Server: The server or server cluster the domain will run on. Pick the cluster you just created.
  • Storage Location: The server on which the site's files will be stored. Pick the load balancer server (e.g. lb WooCommerce Cluster) as explained earlier.
  • Entry Point: The server instance in the cluster through which traffic comes to your website. This should point to the load balancer instance which can direct the traffic to the correct servers within the cluster.
  • IP: The public IP of the load balancer

When the setup looks good, click Add.

Once the orange "In progress" text changes to "Active," the domain is ready to be used. This step usually only takes a few minutes.

Optional: Configure SSL

On any website that handles sensitive customer information, such as people's orders in an online store, it's recommended to use SSL to encrypt the traffic between the user and the server.

Using ClusterCS, SSL is easy to set up. In the Domains dashboard, click on SSL. You'll see three different ways for configuring SSL.

We'll go with Let's Encrypt, a free, widely trusted certificate authority backed by big organizations such as Mozilla and created to speed up the web's move to secure browsing.

Click on Install Let's Encrypt to start the setup.

Give the certificate a name, enter your email address, and select the domain names you want to create certificates for. Check the Auto renew checkbox so you will always have a valid SSL certificate.

Then click the Start button, accept the confirmation popup that shows up, and wait for the certificate generation to complete.

When the certificate is ready, you'll see a screen with its information. 

The Let's Encrypt SSL certificate is now ready to use. 

Configure the server cluster's firewall to allow access to the load balancer from port 443, and your website is ready to accept HTTPS traffic.

On the Servers dashboard, next to your cluster, click Firewall. Then scroll down to the bottom of the page to add a new rule. 

Set up the new firewall rule using the following information:

  • Server: lb (your load balancer instance)
  • Protocol: any
  • State: ANY
  • Source IP: any
  • (Source) Port Spectrum: All
  • Destination IP: any
  • (Destination) Port Spectrum: Exact
  • Port: 443
  • Action: Allow
  • Active: Checked

Click on the Add button to add the configuration and then on the Apply button to execute the changes in your server cluster.

5. Set Up WordPress

You are now ready to set up WordPress. The beauty of this ClusterCS setup is that, even though you are using multiple servers, the installation isn't different from what you'd do with a single server or shared hosting.

Step 1: Create a Database for Your E-Commerce Site

Start by creating the database. In the Domains dashboard, select Databases. Then, click on Add Database.

On the next page, enter a name for your database (for example, wordpress), and click Create.

On the next page, you'll notice that the database has now been added.

Click on the Add New User button to create a new user. 

When the setup asks you to choose the host the database user can connect the database from, select Anyhost. This will allow the application servers to connect to the database, even though they are running on separate servers.

When you have added the user, click on Associate next to the user's name to give the user access to that database.

On the next page, you will be able to choose the permissions for the user in this database. Click on Check All to give the user full access to the database. Then, click on Associate User to apply these changes.

The database is now ready to be used. 

Step 2: Install WordPress 

Download the latest WordPress version and use FTP (using the username and password you specified when creating the domain) to upload it to your new site. If you have already configured your DNS settings, connect to your domain's URL. You can also use the load balancer instance's public IP address.

When you have uploaded WordPress to your site's public_html directory, open the website's URL in your browser and run the WordPress installation.

Use the following database information:

  • Database Name: The name of the database you just created.
  • Username: The name of the database user you just created.
  • Password: The password you selected for the database user.
  • Database Host: The private IP of your db server instances. You will find this information on the AWS EC2 Dashboard. Private IP addresses are used for server-to-server communication within the Amazon virtual private cloud.

When your WordPress installation is ready, continue by setting up WooCommerce and any other plugins and themes you want to use on your e-commerce site. 

Then, come back for some final optimizations, and to learn how to scale your cluster up and down.

6. Use ClusterCS to Optimize Your WordPress Setup

You have now configured a multi-server WordPress website on ClusterCS. The site is secure, stable, well organized, and easily scalable. But what exactly do you do when your online store or other website gets a lot of traffic and it's time to scale the service up?

Step 1: Add App Servers

This is where you'll see the power of a cluster setup: thanks to the work we did up front, when you need more processing power, you can simply add more servers to the cluster.

First, launch a new server instance on AWS, making sure it uses the same SSH key pair and belongs to the same security group as the other servers in the cluster.

Then, in the ClusterCS Manage Servers dashboard, click Manage to update your cluster's preferences.

Scroll all the way down, and click on Add Servers.

Repeat the steps for adding a server to the cluster pool from above. 

Click Continue to setup and configure the layers for the server. Select all the same software as on the app1 and app2 servers. Then click Save to apply the changes and wait until the configuration finishes. 

Your website is now running on three application servers instead of two. 

Step 2: Caching

As easy as adding new servers to the cluster is, it's not the only thing you can do to respond to growing server requirements. In the Speed section of the ClusterCS Domains dashboard, you can configure rules for how requests coming to your website should be handled.

As you used the "Smart web server" recipe to start the cluster, you already have some optimizations in place: while PHP traffic is handled by Apache, there is a rule, "Static files to Lighttpd", that tells the load balancer to route requests for static files such as images to Lighttpd to take some load off Apache.

Below that rule, you'll notice a section for configuring your own configuration sets. 

There are many things you can do with this tool, but we'll look at one of the most effective ones: caching a single page using Nginx. 

Fill in the following information:

  • Set name: Give your configuration a descriptive name, for example "Cache Shop Front Page".
  • Operator: Select if all of the conditions need to be matched for the rule to apply (and) or if it's enough if just one does (or).
  • Conditions: Add as many conditions as you need for specifying the rule. You can create your conditions based on path, cookies, IP, method, referrer, user agent, or dynamic values. In this case, choose path and ends_with, and type the shop page's path—for example, /shop. Then click Add.
  • Action: Choose the action to do for requests matching these conditions. To cache the shop page, choose cache_with as the action and Smart Traffic Manager / Nginx as its parameter.

Click Add Set. Then, click Execute to apply the changes to your server cluster.

Now, the page will be cached using Nginx, which means that less time is spent on running PHP requests on the Apache server.

Conclusion

In this tutorial, you learned how to use ClusterCS to set up a cluster of AWS servers and a WordPress site on top of it. We also looked at ways of scaling the site up and down as well as configuring caching to handle big traffic spikes.

As you saw, these steps can also be applied to smaller, one-server setups, or alternatively, if you want to dig deeper into the details of your setup, ClusterCS has more options for that too.

Visit the ClusterCS website and Knowledge Base to learn more. The ClusterCS staff is also always ready to answer your questions and help you with your server setup.

Categories: Web Design

How to Write an SEO Friendly Press Release

Webitect - Fri, 11/03/2017 - 16:46

Do you know the history of the press release? Back in 1906, a train belonging to the Pennsylvania Railroad was involved in a terrible accident. They hired marketing expert Ivy Lee to help. Lee promptly sent out a written, prepared statement describing what happened. This might not seem like a big deal today, but the professional, real-time response was actually very innovative. The company could help shape the story in the minds of the public. Lee’s written statement was published practically verbatim by the New York Times. Today, the technology has changed but the basics of a press release remain roughly

The post How to Write an SEO Friendly Press Release appeared first on Webitect.

Categories: Web Design

Level-Up Email Campaigns With Customer Journey Mapping

Smashing Magazine - Fri, 11/03/2017 - 09:41
I became a huge fan of customer journey mapping (CJM) the first time I was introduced to it. And after a few years of mapping, tweaking and presenting maps, my team and I started looking for other more exotic uses of this technique. The law of the instrument at its best, I suppose. Well, seek and ye shall find. Customer journey mapping is a visualization technique that helps marketing specialists, user experience designers, and product and business owners see the journey people take when interacting with products and services.
Categories: Web Design

Build Web Applications Using Node.js

Tuts+ Code - Web Development - Fri, 11/03/2017 - 05:00
Introduction

Aside from building APIs, Node.js is great for building standard web applications. It has powerful tools to meet the taste of web developers. In this tutorial, you will be building a web application that can serve as a local library. 

While building you will learn about some types of middleware, you will see how to handle form submission in Node.js, and you will also be able to reference two models.

Let's get started.

Getting Started

Start by installing the express generator on your machine.

npm install express-generator -g

Run the express generator command to generate your application.

express tutsplus-library --view=pug create : tutsplus-library create : tutsplus-library/package.json create : tutsplus-library/app.js create : tutsplus-library/public create : tutsplus-library/routes create : tutsplus-library/routes/index.js create : tutsplus-library/routes/users.js create : tutsplus-library/views create : tutsplus-library/views/index.pug create : tutsplus-library/views/layout.pug create : tutsplus-library/views/error.pug create : tutsplus-library/bin create : tutsplus-library/bin/www create : tutsplus-library/public/javascripts create : tutsplus-library/public/images create : tutsplus-library/public/stylesheets create : tutsplus-library/public/stylesheets/style.css install dependencies: $ cd tutsplus-library && npm install run the app: $ DEBUG=tutsplus-library:* npm start

Now migrate into your working, open package.json, and make the dependencies similar to what I have below.

#package.json { "name": "tutsplus-library", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.17.1", "connect-flash": "^0.1.1", "cookie-parser": "~1.4.3", "debug": "~2.6.3", "express": "~4.15.2", "express-messages": "^1.0.1", "express-session": "^1.15.5", "express-validator": "^4.2.1", "mongoose": "^4.11.12", "morgan": "~1.8.1", "pug": "~2.0.0-beta11", "serve-favicon": "~2.4.2" } }

Run the command to install the packages.

npm installSet Up the Entry File

app.js was created when you ran the generator command; however, you need to set up extra configuration. Edit the file to look like what I have below.

#app.js var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); const session = require('express-session') const expressValidator = require('express-validator') const flash = require('connect-flash') const mongoose = require('mongoose') // 1 const genres = require('./routes/genres'); const books = require('./routes/books'); var app = express(); // 2 mongoose.Promise = global.Promise const mongoDB = process.env.MONGODB_URI || 'mongodb://127.0.0.1/tutsplus-library' mongoose.connect(mongoDB) // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); // 3 app.use(session({ secret: 'secret', saveUninitialized: true, resave: true })) // 4 app.use(expressValidator({ errorFormatter: function(param, msg, value) { var namespace = param.split('.') , root = namespace.shift() , formParam = root while(namespace.length) { formParam += '[' + namespace.shift() + ']' } return { param : formParam, msg : msg, value : value } } })) // 5 app.use(flash()) app.use(function (req, res, next) { res.locals.messages = require('express-messages') next() }) // 6 app.use('/genres', genres); app.use('/books', books); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
  1. You required the two routes you will be making use of in building this application. You will create the routes file shortly. The required routes are assigned as values to two different variables which are used when setting up the middleware for your routes.
  2. You set Mongoose to use global.Promise. The variable MongoDB is assigned the MONGODB_URI of your environment or the path to your local mongo server. This variable is passed as an argument to connect to the running MongoDB server.
  3. You set up session middleware using express-session. This middleware is important as you will be displaying flash messages in some parts of your application.
  4. You set up middleware for validation. This middleware will be used to validate form input, ensuring that users of the application do not submit an empty form. The validation uses a package installed, express-validator.
  5. You set up middleware which will come in handy when displaying flash messages. This middleware makes use of connect-flash.
  6. The routes for the application are set up to make use of the routes file you required. Requests pointing to /genres and /books will make use of the genres and books routes files respectively. At this moment you have not created the routes files, but you will do that soon.
Book and Genre Model

The Book Model will make use of Mongoose Schema to define how the books will be structured. Create a directory called models, and a new file called Book.js. Here is what it looks like.

#models/Book.js const mongoose = require('mongoose') mongoose.Promise = global.Promise const Schema = mongoose.Schema const bookSchema = Schema({ name: { type: String, trim: true, required: 'Please enter a book name' }, description: { type: String, trim: true }, author: { type: String, trim: true, }, genre: [{ type: Schema.Types.ObjectId, ref: 'Genre' }] }) module.exports = mongoose.model('Book', bookSchema)

Here you have four fields. The last field is used to store the genre each book belongs to. The genre field here references the Genre model, which will be created next. That's why the type is set to Schema.Types.ObjectId, which is where the ids of each referenced genre will be saved. ref specifies the model you are referencing. Note that genre is saved as an array, meaning that a book can have more than one genre.

Let's go ahead a create the Genre model.

#models/genre.js const mongoose = require('mongoose') mongoose.Promise = global.Promise const Schema = mongoose.Schema const genreSchema = Schema({ name: { type: String, trim: true, required: 'Please enter a Genre name' } }) module.exports = mongoose.model('Genre', genreSchema)

For your Genre, you need just one field: name.

Genre Index Route and View

For this tutorial, you will make use of two routes paths for your genre: a path to add new genres, and another that lists the genres you have. Create a file in your routes directory called genres.js.

Start by requiring all the modules you will be making use of.

#routes/genres.js var express = require('express'); var router = express.Router(); const mongoose = require('mongoose') const Genre = require('../models/Genre')

Next, drop in the route that handles the index file for your genres.

router.get('/', (req, res, next) => { const genres = Genre.find({}).exec() .then((genres) => { res.render('genres', { genres: genres }) }, (err) => { throw err }) });

This route gets called whenever requests are made to /genres. Here you call the find method on your Genre model to obtain all the genres that have been created. These genres are then rendered on a template called genres. Let's go ahead and create that, but first, update your layout.pug to look like this:

#views/layout.pug doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') link(rel='stylesheet', href='https://bootswatch.com/paper/bootstrap.css') script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js') script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js') body .container-fluid block header nav.navbar.navbar-inverse .container-fluid .navbar-header button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#bs-example-navbar-collapse-2') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar a.navbar-brand(href='#') Local Library #bs-example-navbar-collapse-2.collapse.navbar-collapse ul.nav.navbar-nav.navbar-right li a(href='/books') View Books li a(href='/books/add') Add New Book li a(href='/genres') View Genres li a(href='/genres/add') Add New Genre block content

This will give your views a nice structure to aid navigation. Now create a view file called genre.pug. In this file, you will loop through the genres created and output each genre in an unordered list.

Here is how the file should look.

#views/genres.pug extends layout block content h1 Genre ul.well.well-lg each genre, i in genres li.well.well-sm p #{genre.name}Add New Genre Routes and View

Go back to your routes/genres.js to add the routes that will handle the creation of new genres.

#routes/genres.js // 1 router.get('/add', (req, res, next) => { res.render('addGenre') }) // 2 router.post('/add', (req, res, next) => { req.checkBody('name', 'Name is required').notEmpty() const errors = req.validationErrors() if (errors) { console.log(errors) res.render('addgenres', { genre, errors }) } const genre = (new Genre(req.body)).save() .then((data) => { res.redirect('/genres') }) .catch((errors) => { console.log('oops...') console.log(errors) }) }) // 3 module.exports = router;
  1. The job of this router is to simply display the page for adding new routes. This router gets called whenever requests are made to /genres/add path.
  2. This router handles the submission of the form. When the form is submitted, we check to ensure that a name is entered by the user. If no name is entered, the page is re-rendered. If the checks are good to go, the genre is saved and the user is redirected to the /genres page.
  3. The module is exported as a router.

Now you can go ahead and create the page for adding a new genre.

#views/addGenre.pug extends layout block content .row .col-md-12 h1 Add Book form(method="POST", action="/genres/add") .form-group label.col-lg-2.control.label Name .col-lg-10 input.form-control(type="text", name='name') .form-group .col-lg-10.col-lg-offset-2 input.button.btn.btn-primary(type='submit', value='Submit') if errors ul for error in errors li!= error.msgBooks Routes and View

Create a new route file for books, and name it books.js. As you did earlier with the genre, start by requiring the necessary modules.

#routes/books.js var express = require('express'); var router = express.Router(); const mongoose = require('mongoose') const Book = require('../models/Book') const Genre = require('../models/Genre')

Next, set up the router for displaying all the books saved in the library. Try that on your own in the way as you set up that of the genre; you can always check back to make corrections.

I guess you tried that out—here is how it should look.

router.get('/', (req, res, next) => { const books = Book.find({}).exec().then((books) => { res.render('books', { books: books }) }, (err) => { throw err }) });

When this router is called, a request is made to find all books saved in the database. If all goes well, the books are shown on the /books page, else an error is thrown.

You need to create a new file for displaying all books, and here is how it should look.

#views/books.pug extends layout block content h1 Books ul.well.well-lg each book, i in books li.well.well-sm a(href=`/books/show/${book.id}`) #{book.name} p= book.description

You simply loop through the books returned and output the name and description of each book using an unordered list. The name of the book points to the individual page of the book.

Add New Book Routes and View

The next router you set up will handle the addition of new books. Two routers will be used here: one will simply render the page, and another will handle the submission of the form.

This is how the routers look.

router.get('/add', (req, res, next) => { const genres = Genre.find({}).exec() .then((genres) => { res.render('addBooks', { genres }) }) .catch((err) => { throw err }) }) router.post('/add', (req, res, next) => { req.checkBody('name', 'Name is required').notEmpty() req.checkBody('description', 'Description is required').notEmpty() req.checkBody('genre', 'Genre is required').notEmpty const errors = req.validationErrors() if (errors) { console.log(errors) res.render('addBooks', { book, errors }) } const book = (new Book(req.body)).save() .then((data) => { res.redirect('/books') }) .catch((errors) => { console.log('oops...') }) })

In the first router, you are displaying the /addBooks page. This router is called when a request is made to /add path. Since books added are supposed to have genres, you want to display the genres that have been saved to the database.

const genres = Genre.find({}).exec() .then((genres) => {

The code above finds all the genres in your database and returns them in the variable genres. With this, you will be able to loop through the genres and display them as checkboxes.

The second router handles the submission of the form. First, you check the body of the request to ensure that some fields are not empty. This is where the express-validator middleware you set in app.js comes handy. If there are errors, the page is re-rendered. If there are none, the new Book instance is saved and the user is redirected to the /books page.

Let's go ahead and create the views for this.

Create a new view file called addBooks.pug. Note that the name of the view matches the first parameter given to res.render. This is because you are rendering a template. During redirection, you simply pass the path you want to redirect to, as you did with res.redirect('/books').

Having established that, here is what the views should look like.

#views/addBooks.pug extends layout block content .row .col-md-12 h1 Add Book form(method="POST", action="/books/add") .form-group label.col-lg-2.control-label Name .col-lg-10 input.form-control(type="text", name='name') .form-group label.col-lg-2.control-label Author .col-lg-10 input.form-control(type="text", name='author') .form-group label.col-lg-2.control-label Book Description .col-lg-10 textarea#textArea.form-control(rows='3', name='description') .form-group label.col-lg-2.control-label Genre .col-lg-10 for genre in genres .checkbox input.checkbox(type='checkbox', name='genre', id=genre._id, value=genre._id, checked=genre.checked) label(for=genre._id) #{genre.name} .form-group .col-lg-10.col-lg-offset-2 input.button.btn.btn-primary(type='submit', value='Submit') if errors ul for error in errors li!= error.msg

The important thing to note here is the form action and method. When the submit button is clicked, you are making a POST request to /books/add. One other thing—once again you loop through the collection of genres returned and display each of them.

Book Show Route and View

Let us drop in the route to handle the requests made to each books page. While you are there, it is important to export your module too.

#routes/books.js router.get('/show/:id', (req, res, next) => { const book = Book.findById({ _id: req.params.id }) .populate({ path: 'genre', model: 'Genre', populate: { path: 'genre', model: 'Book' } }) .exec() .then((book) => { res.render('book', { book }) }) .catch((err) => { throw err }) }) module.exports = router;

No magic is happening here.

First, requests made to this router must have an id: the id of the book. This id is obtained from the params of the request using req.params.id. This is used to identify the specific book that should be obtained from the database, as the ids are unique. When the book is found, the genre value of the book is populated with all the genres that have been saved to this book instance. If all goes well, the book view is rendered, else an error is thrown.

Let's create the view for a book. Here is how it should look.

block content .well.well-lg h1 #[strong Name:] #{book.name} ul li #[strong Description:] #{book.description} li #[strong Author]: #{book.author} li #[strong Genre:] each genre in book.genre #{genre.name} |,

You can start up your node server by running:

DEBUG=tutsplus-library:* npm startConclusion

Now you know how to build a standard web application in Node.js, not just a simple to-do app. You were able to handle form submission, reference two models, and set up some middleware.

You can go further by extending the application—try adding the ability to delete a book. First add a button to the show page, and then go to the routes files and add a router for this. Note that this is going to be a POST request.

You can also think of more features to add to the application. I hope you enjoyed it.

Categories: Web Design

13 Free jQuery Plugins For Custom Web Form UX Features

Looking to increase your default form functionality? Pretty much every feature under the sun has a plugin you can use to simplify the process. But if you aren’t sure...

The post 13 Free jQuery Plugins For Custom Web Form UX Features appeared first on Onextrapixel.

Categories: Web Design

Understanding The Vary Header

Smashing Magazine - Thu, 11/02/2017 - 13:35
The Vary HTTP header is sent in billions of HTTP responses every day. But its use has never fulfilled its original vision, and many developers misunderstand what it does or don’t even realize that their web server is sending it. With the coming of the Client Hints, Variants and Key specifications, varied responses are getting a fresh start. What’s Vary? The story of Vary starts with a beautiful idea of how the web should work.
Categories: Web Design

New Course on WordPress Conditional Tags

Tuts+ Code - Web Development - Wed, 11/01/2017 - 02:08
What You'll Be Creating

In our new Coffee Break Course, WordPress Coding Basics: Conditional Tags, you'll learn all about conditional tags in WordPress: what they are and how to use them. 

Envato Tuts+ instructor Rachel McCollin will show you how conditional tags can help you cut down on the number of different templates in your theme and also make your plugin logic more straightforward. Along the way, you'll get to practice using conditional tags in a simple plugin.

Watch the introduction below to find out more.

 

You can take our new Coffee Break 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 300,000+ creative assets. Create with unique fonts, photos, graphics and templates, and deliver better projects faster.

Categories: Web Design

SmashingConf 2018: Fetch Those Early-Bird Tickets!

Smashing Magazine - Tue, 10/31/2017 - 14:09
Great conferences are all about learning new skills and making new connections. That’s why we’ve set up a couple of new adventures for SmashingConf 2018 — just practical sessions, new formats, new lightning talks, evening sessions and genuine, interesting conversations — with a dash of friendly networking! Taking place in London, San Francisco, Toronto. Tickets? Glad you asked! SmashingConf London / #perfmatters / Feb 7–8 Performance matters. Next year, we’re thrilled to venture to London for our brand new conference fully dedicated to everything front-end performance.
Categories: Web Design

Pages