Monthly Archives: February 2017

React Native App with Authentication and User Management in 15 Minutes

By stormpath

While React Native brings many efficiencies, it can also be complicated to work with when it comes to developing authentication and user management functions. (React Native is a Javascript framework designed to improve users’ ability to create cross-platform applications with native UI performance.) With React Native, everything needs to be built from scratch—from screen navigation and a responsive interface to creating and wiring up a user management backend. Enter Stormpath!

This tutorial will walk you through how to use Stormpath to add authentication and user management to your React Native application with ease. With Stormpath you’ll add library-free navigation, and create a responsive, networked user interface, all without needing to touch your own backend!

To complete this tutorial, sign up for a forever-free developer account with Stormpath. Stormpath is free to use, and can help your team write a secure, scalable application without worrying about the nitty-gritty details of authentication, authorization, and user security – or managing a backend user database. Sign up for an account today!

If you want to jump ahead, you can grab the source code from GitHub. Let’s get started!

Create Your Base Project

Start by installing the React Native command line interface (CLI) using npm and set up a skeleton project.

$ npm install -g react-native-cli
$ react-native init ReactNativeStormpath

After entering the created directory, install packages (with either npm install or yarn, which is faster) and run the project. This tutorial deploys to Android but iOS will work as well.

$ cd ReactNativeStormpath
$ yarn
$ react-native run-android

To ensure execution works set up Android on your system and have either a device attached or an emulator running. When successful you should see the stock welcome screen.

Add Easy Navigation with React Native

Navigation in React Native is quite an involved affair. Using either built-in or external libraries you define your aplication’s screens and the permissable routes between them using a Navigation Component, with each library sporting various design requirements and caveats.

It needn’t be so complicated. If you just need to switch between views you can simply use a single state variable and some callbacks (and no esoteric libraries or components).

Create two empty components, Login and Secured. Inside the main script (index.android.js) use a state variable to decide which of these components is returned by the main render, and inside each pass functions needed to switch the variable appropriately.

import React, { Component } from 'react';
import {
  AppRegistry
} from 'react-native';

import Login from './src/screens/Login';
import Secured from './src/screens/Secured';

class ReactNativeStormpath extends Component {

  state = {
    isLoggedIn: false
  }

  render() {

    if (this.state.isLoggedIn) 
      return <Secured 
          onLogoutPress={() => this.setState({isLoggedIn: false})}
        />;
    else 
      return <Login 
          onLoginPress={() => this.setState({isLoggedIn: true})}
        />;
  }

}

AppRegistry.registerComponent(ReactNativeStormpath , () => ReactNativeStormpath );

The secured page is passed a function to logout which switches the view back to the login page. Similarly the login page is passed a function which switches the view to the secured page.

To finish, all that’s needed is to fill out these pages and connect buttons inside them to the passed in functions.

Set Up Your Login Page

The login component just needs username and password text inputs and a button (and perhaps a title to make everything look professional).

import React, { Component } from 'react';
import {
    ScrollView,
    Text,
    TextInput,
    View,
    Button
} from 'react-native';

export default class Login extends Component {

    render() {
        return (
            <ScrollView style={{padding: 20}}>
                <Text 
                    style={{fontSize: 27}}>
                    Login
                </Text>
                <TextInput placeholder='Username' />
                <TextInput placeholder='Password' />
                <View style={{margin:7}} />
                <Button 
                        onPress={this.props.onLoginPress}
                        title="Submit"
                    />
                </ScrollView>
            )
    }
}

Note how when the button is pressed the switching function is called.

Set Up Your Secured Page

In the secured component put a welcome message and a logout button (which calls it’s own switch function).

import React, { Component } from 'react';
import {
    ScrollView,
    Text,
    View,
    Button
} from 'react-native';

export default class Secured extends Component {
    render() {
        return (
            <ScrollView style={{padding: 20}}>
                <Text 
                    style={{fontSize: 27}}>
                    Welcome
                </Text>
                <View style={{margin:20}} />
                <Button
                            onPress={this.props.onLogoutPress}
                            title="Logout"
                     />
                </ScrollView>
                )
    }
}

Add Navigation Logic

As simple as that you have a login and secured page with navigation between the two!

Set Up Stormpath for Secure User Management

Stormpath is an API that helps developers manage users in their applications (without needing to create a backend). Follow these steps to set up Stormpath for the first time: Browse to: https://api.stormpath.com/register to register for an account.

You’ll see a confirmation screen and you’ll receive an email at the address you put in to verify your account. Click the friendly green button in your email to verify your account.

Now, you can login to your newly created Stormpath account.

Next, Create an account in the default My Application Stormpath Application. You’ll use this account to login from the React Native app. Browse to: Applications -> My Application ->Accounts -> Create Account. Fill out the form and click the Create button.

The last step is to make note of your Client API DNS label. You can find this by clicking on Applications -> My Application -> Policies -> Client API. You’ll see DNS Label field. It will be something like: {DNS-LABEL}.apps.stormpath.io. You’ll need this address later when configuring your React Native app to connect to Stormpath.

Just like that you can now make authentication requests (defined in the Client API documentation) to https://{DNS-LABEL}.apps.stormpath.io/{endpoint}.

Modify Your Application UI to Make Requests

React Native uses the Fetch API for networking. To use it you need to modify the login component to respond to each request.

Put the following at the top of the component:

state = {
username: '',
    password: '',
    isLoggingIn: false,
    message: ''
}

username and password store the contents of the text inputs. To fill these variables you need to use the OnChangeText property. For example in the username component:

 this.setState({username})}
/>

isLoggingIn tracks whether logging in is in progress. When true an activity spinner can be shown by embedding a JSX expression. Put the following above the button component:

{this.state.isLoggingIn && <ActivityIndicator />}

The sign in button itself should be disabled both whilst logging in and if either the username or password fields aren’t set.

<Button 
disabled={this.state.isLoggingIn||!this.state.username||!this.state.password}
onPress={this._userLogin}
title="Submit"
/>

Note that on press is set to call this._userLogin. message stores any error message returned from logging in. It can be displayed by putting the following above the button as well:

{!!this.state.message && (
<Text
        style={{fontSize: 14, color: 'red', padding: 5}}>
        {this.state.message}
    </Text>
)}

Now for _userLogin. First reset the relevant states:

    _userLogin = () => { 

        this.setState({isLoggingIn: true, message:''});

Then create the data object to be sent to the API:

        var params = {
            username: this.state.username,
            password: this.state.password,
            grant_type: 'password'
        };

Next URL encode the parameters so they can be sent as application/x-www-form-urlencoded ([as required when getting an OAuth token from the Client API).

        var formBody = [];
        for (var property in params) {
          var encodedKey = encodeURIComponent(property);
          var encodedValue = encodeURIComponent(params[property]);
          formBody.push(encodedKey + "=" + encodedValue);
        }
        formBody = formBody.join("&");

Finally, call the Stormpath application URL using the Fetch API.

        var proceed = false;
        fetch("https://”+Environment.CLIENT_API+”/oauth/token", {
            method: "POST", 
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: formBody
          })
          .then((response) => response.json())
          .then((response) => {
            if (response.error) this.setState({message: response.message});
            else proceed = true;
          })
          .then(() => {
            this.setState({isLoggingIn: false})
            if (proceed) this.props.onLoginPress();
          })
          .done();

Here Environment.CLIENT_API pulls in the DNS entry name you saved, which you need to put into Environment.js in the root folder. Javascript Promises handles the network call. Each then happens in sequence after the initial call:

  1. Get the initial JSON response
  2. Convert it to an object
  3. Set message if there was an error
  4. Run this.props.onLoginPress() if authentication successful, to switch the page

With this complete, your application should authenticate correctly and display any error messages the Stormpath servers send back.

Your New React Native Application with Auth and User Management

After some cosmetic changes such as clearing the text inputs when selected and securing the password field (see the source code for these) you should have the following login process:

Features like user registration and token-secured access control are easy to add on, once you have this initial application built. Check out these resources to learn more about how Stormpath supports these, and much more:

Source:: scotch.io

Three things you didn't know about the AsyncPipe

You sure heard about Angular’s AsyncPipe haven’t you? It’s this handy little pipe that we can use from within our templates so that we don’t have to deal with unwrapping data from Observables or Promises imperatively. Turns out the AsyncPipe is full of little wonders that may not be obvious at first sight. In this article we like to shed some light on the inner workings of this useful little tool.

Subscribing to long-lived Observables

Often when we think about the AsyncPipe, we only think about values that resolve from some http call. We issue an http call, get an Observable back, apply some transformations (e.g. map(...).filter(...)) and finally expose an Observable to the template of our component. Here is what that typically looks like.

...
@Component({
  ...
  template: `
    <md-list>
      <a md-list-item
        *ngFor="let contact of contacts | async"
        title="View {{contact.name}} details">
        <img md-list-avatar [src]="contact.image" alt="Picture of {{contact.name}}">
        <h3 md-line>{{contact.name}}</h3>
      </a>
    </md-list>`,
})
export class ContactsListComponent implements OnInit {

  contacts: Observable<Array<Contact>>;

  constructor(private contactsService: ContactsService) {}

  ngOnInit () {
    this.contacts = this.contactsService.getContacts();
  }
}

In the described scenario our Observable is what we like to refer to as short-lived. The Observable emits exactly one value – an array of contacts in this case – and completes right after that. That’s the typical scenario when working with http and it’s basically the only scenario when working with Promises.

However, we can totally have Observables that emit multiple values. Think about working with websockets for instance. We may have an array that builds up over time! Let’s simulate an Observable that emits an array of numbers. But instead of emitting just a single array once, it will emit an array every time a new item was added. To not let the array grow infinitely we will limit it to the last five items.

...
@Component({
  selector: 'my-app',
  template: `
    <ul>
      <li *ngFor="let item of items | async">{{item}}</li>
    </ul>`
})
export class AppComponent {
  items = Observable.interval(100)
                    .scan((acc, cur)=>[cur, ...acc].slice(0, 5), []);             
}

Notice how our list is kept nicely in sync without further ado thanks to the AsyncPipe!

Keeping track of references

Let’s back of and refactor above code to what it would look like without the help of the AsyncPipe. But while we’re at it, let’s introduce a button to restart generating numbers and pick a random background color for the elements each time we regenerate the sequence.

...
@Component({
  selector: 'my-app',
  template: `
    <button (click)="newSeq()">New random sequence</button>
    <ul>
      <li [style.background-color]="item.color"
          *ngFor="let item of items">{{item.num}}</li>
    </ul>`
})
export class AppComponent {
  items = [];

  constructor () {
    this.newSeq();
  }

  newSeq() {

    // generate a random color
    let color = '#' + Math.random().toString(16).slice(-6);

    Observable.interval(1000)
          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])
          .subscribe(items => this.items = items);
  }
}

Click on the button and play with the demo. Notice something? The color flips back and forth between more and more different colors with every click of the button. That’s because in this case the Observable is what we like to refer to as long-lived. And furthermore, every time we click the button we are creating one more of these long-lived Observables without cleaning up the previous.

Let’s refactor the code to track subscriptions and tear down our long-lived Observable every time that we create a new one.

...
export class AppComponent {
  ...
  subscription: Subscription;

  newSeq() {

    if (this.subscription) {
      this.subscription.unsubscribe();
    }

    // generate a random color
    let color = '#' + Math.random().toString(16).slice(-6);

    this.subscription = Observable.interval(1000)
          .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), [])
          .subscribe(items => this.items = items);
  }
}

Every time we subscribe to our Observable we save the subscription in an instance member of our component. Then, when we run newSeq again we check if there’s a subscription that we need to call unsubscribe on. That’s why we don’t see our list flipping between different colors anymore no matter how often we click the button.

Now meet the AsyncPipe again. Let’s change the ngFor again to apply the AsyncPipe and get rid of all the manual bookkeeping.

@Component({
  selector: 'my-app',
  template: `
    <button (click)="newSeq()">New random sequence</button>
    <ul>
      <li [style.background-color]="item.color"
          *ngFor="let item of items | async">{{item.num}}</li>
    </ul>`
})
export class AppComponent {
  items: Observable<any>;

  constructor () {
    this.newSeq();
  }

  newSeq() {

    // generate a random color
    let color = '#' + Math.random().toString(16).slice(-6);

    this.items = Observable.interval(1000)
                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []);
  }
}

I’m sure you’ve heard that the AsyncPipe unsubscribes from Observables as soon as the component gets destroyed. But did you also know it unsubscribes as soon as the reference of the expression changes? That’s right, as soon as we assign a new Observable to this.items the AsyncPipe will automatically unsubscribe from the previous bound Observable! Not only does this make our code nice and clean, it’s protecting us from very subtle memory leaks.

Marking things for check

Alright. We have one last nifty AsyncPipe feature for you! If you’ve read our article about Angular’s change detection you sure know that you can speed up Angular’s blazingly fast change detection even further using the OnPush strategy. Let’s refactor our example and introduce a SeqComponent to display the sequences while our root component will manage the data and pass it on via an input binding.

Let’s start creating the SeqComponent which is pretty straight forward.

@Component({
  selector: 'my-seq',
  template: `
    <ul>
      <li [style.background-color]="item.color" 
          *ngFor="let item of items">{{item.num}}</li>
    </ul>`
})
export class SeqComponent {
  @Input()
  items: Array<any>;
}

Notice the @Input()decorator for items which means the component will receive those from the outside via a property binding.
Our root component maintains an array seqs and pushes new long-lived Observables into it with the click of a button. It uses an *ngFor to pass each of these Observables on to a new SeqComponent instance. Also notice that we are using the AsyncPipe in our property binding expression ([items]="seq | async") to pass on the plain array instead of the Observable since that’s what the SeqComponent expects.

@Component({
  selector: 'my-app',
  template: `
    <button (click)="newSeq()">New random sequence</button>
    <ul>
      <my-seq *ngFor="let seq of seqs" [items]="seq | async"></my-seq>
    </ul>`
})
export class AppComponent {
  seqs = [];
  
  constructor () {
    this.newSeq();
  }
  
  newSeq() {
    
    // generate a random color
    let color = '#' + Math.random().toString(16).slice(-6);
    
    this.seqs.push(Observable.interval(1000)
                           .scan((acc, num)=>[{num, color }, ...acc].slice(0, 5), []));
  }
}

So far, we haven’t made any changes to the underlying change detection strategy. If you click the button a couple of times, notice how we get multiple lists that update independently at a different timing.

However, in terms of change detection, it means that all components are checked each time any of the Observables fire. That’s a waste of resources. We can do better by setting the change detection for our SeqComponent to OnPush which means it will only check it’s bindings if the inputs – the array in our case – changes.

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'my-seq',
  ...
})

That works and seems to be an easy quick win. But here comes the thing: It only works because our Observable creates an entirely new array each time it emits a new value. And even though that’s actually not too bad and in fact beneficial in most scenarios, let’s consider we use a different implementation which mutates the existing array rather than recreating it every time.

Observable.interval(1000)
          .scan((acc, num)=>{
            acc.splice(0, 0, {num, color});
            if (acc.length > 5) {
              acc.pop()
            }
            return acc;
          }, [])

If we try that out OnPush doesn’t seem to work anymore because the reference of items simply won’t change anymore. In fact, when we try that out we see each list doesn’t grow beyond its first element.

Meet the AsyncPipe again! Let’s change our SeqComponent so that it takes an Observable instead of an array as its input.

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'my-seq',
  template: `
    <ul>
      <li [style.background-color]="item.color" 
          *ngFor="let item of items | async">{{item.num}}</li>
    </ul>`
})
export class SeqComponent {
  @Input()
  items: Observable<Array<any>>;
}

Also note that it now applies the AsyncPipe in its template since it’s not dealing with a plain array anymore. Our AppComponent needs to be changed as well to not apply the AsyncPipe in the property binding anymore.

<ul>
  <my-seq *ngFor="let seq of seqs" [items]="seq"></my-seq>
</ul>

Voila! That seems to work!

Let’s recap, our array instance doesn’t change nor does the instance of our Observable ever change. So why would OnPush work in this case? The reason can be found in the source code of the AsyncPipe itself

private _updateLatestValue(async: any, value: Object): void {
  if (async === this._obj) {
    this._latestValue = value;
    this._ref.markForCheck();
  }
}

The AsyncPipe marks the ChangeDetectorRef of our component to be checked, effectively telling the change detection that there may be a change in this component. If you like to get a more detailed understanding of how that works we recommend reading our in-depth change detection article.

Conclusion

We use to look at the AsyncPipe as a nifty little tool to save a couple of lines of code in our components. In practice though, it hides a lot of complexity from us that comes with managing async tasks. It’s pure gold.

Source:: Thoughtram

Get Started Running Laravel in a Docker Container

By Neo Ighodaro

Laravel and Docker are two very popular tools of choice when considering building for the web. Although both of them do very different things, they can both be combined to create amazing products.

For our use case, we will be running Laravel in a Docker container. This is going to be a simple demonstration on how to use both products to create real-life applications. Nothing heavy.

While this article is made to cater for everyone who will be reading it, the article assumes you already have a working basic knowledge of both Docker and Laravel.

Prerequisites

Before you start, you need to have some certain prerequisites in place:

  • Local machine running the latest version of Docker, v1.13.1 at the time of writing this article.
  • Local machine running the latest version of Docker compose.
  • Server running the latest version of Docker, v1.13.1 at the time of writing this article.
  • Laravel Installer installed on your machine (optional but recommended).

Getting Started

To get started, let us create an instance of Laravel running inside a Docker container. We will be starting with a base image and customizing it to suit our applications needs.

# Create the project directory and some subdirectories we will need later
$ mkdir -p acme/storage/mysql 
$ mkdir -p acme/storage/logs
$ mkdir -p acme/storage/app

# Go into the application directory and create a new laravel application
$ cd acme
$ laravel new src

Now that we have our Laravel application inside the appropriate directory, let us create a Dockerfile to instruct the Laravel application on how to build. Create a new Docker file at ./src/Dockerfile.

For your convenience, I have created a base Docker image that works right out of the box for Laravel. creativitykills/nginx-php-server. You can see the source code for the Docker image right here.

FROM creativitykills/nginx-php-server:latest
MAINTAINER Neo Ighodaro <neo@creativitykills.co>

The Dockerfile is where we instruct Docker on what to build and how to build it. In this file, we are saying: ‘Hey Docker, build a container from the creativitykills/nginx-php-server image, the maintainer is Neo Ighodaro…’. For more information on dockerfiles and the options available to you, read the documentation, but this will do for now.

Finally, lets create a ./docker-compose.yml file. Make sure it is at the root of your project. We will add our first service which is the web service to the docker compose file.

version: '2'
services:
    web:
        build: ./src
        container_name: web
        ports:
            - 8888:80
        volumes:
            - ./src:/var/www
            - ./storage/app:/var/www/storage/app
            - ./storage/logs:/var/www/storage/logs

Once we have defined the web service file, we can move on to the next thing which is creating a MySQL service. Open the docker-compose.yml and make sure you have the following.

version: '2'
services:
    mysql:
        image: mysql:5.7
        env_file:
            - ./mysql.env
        volumes:
            - ./storage/mysql:/var/lib/mysql
    web:
        build: ./web
    container_name: web
        ports:
            - 8888:80
        volumes:
            - ./web:/var/www
            - ./storage/app:/var/www/storage/app
            - ./storage/logs:/var/www/storage/logss

Docker allows you to bring up a container in many ways, you can manually bring up a running instance using docker run, but sometimes, you will want to bring up multiple containers with one single command and that’s where docker-compose comes in.

The version is telling Docker compose what version of the docker-compose syntax tthe current yaml uses. services lists the services we would like to control with docker compose. In the mysql service, we want to build the mysql:5.7 image, load an environment file that contains the MySQL image configuration, and finally mount a volume to the MySQL image for persistent storage of the database data.

Protip: Mounting a volume to the container keeps it alive even after the container is killed. When you mount a folder on the host to the container, the folder is basically mapped to the docker container and can be accessed from either inside or outside the container.

In the web service, one different thing we did is we exposed and mapped a port 8888 (on the host) to 80 (inside the container). We also specified a container_name so we can easily reference the container when we want to.

Now create a new file called ./mysql.env in the root directory, and in there customize these environment values. These will be set as your database credentials when the MySQL container is built.

MYSQL_DATABASE=homestead
MYSQL_USER=homestead
MYSQL_PASSWORD=secret
MYSQL_ROOT_PASSWORD=SuperSecretRootPassword

Protip If you are using a git repository, you might want to gitignore the mysql.env file for security reasons.

Once you are done, you need to edit your Laravel applications .env file. Update the database configuration to match whatever you set as your MySQL details above. Note that the DB_HOST value in your Laravel .env file is equal to the name of the mysql service in the docker-compose.yml; in this case its web.

Now, we can bring up our containers mysql and web using a single command. cd to the root directory of your application and run the command docker-compose up -d. This should bring up all your services. You can visit the docker URL for your web container http://localhost:8888 and you should see the awesome Laravel welcome page.

You can ssh into your web container and run the command php artisan migrate to run migrations on your database. To ssh into your container, run the command:

$ docker exec -it web bash

Conclusion

Docker makes it easy to run consistent disposable environments, and with a little push, it is easy to get up and running with Docker and Laravel. In future articles, I will cover how to create a simple Todo app using our new Laravel + Docker application and also how to scale your Laravel application using Docker compose.

Source:: scotch.io

File Upload in Rails with PaperClip

By kinsomicrote

Introduction

This is the first part of my series – covering file uploading gems available for Rails developers. For this edition, we will cover the popular Paperclip gem.

Paperclip is an easy to use file uploading tool made available by the awesome folks at thoughtbot.

In this tutorial, we will build a Rails application from scratch with file uploading functionality added. We will make use of Paperclip, seeing the awesome features it provides to us.

Here is a snapshot of what we want to build.

The code for this tutorial is available on Github.

Setting Up

For PaperClip to work, we need to have ImageMagick installed on our machine.

For Mac users you can do so by running the command.

brew install imagemagick

Ubuntu users should run the command.

sudo apt-get install imagemagick -y

Let us start by creating our Rails application. For the purpose of this tutorial I’ll call mine ClipUploader.


rails new ClipUploader -T 

The -T flags tells Rails to generate the new application without the default test suite.

Once that is done, you will want to add Twitter Bootstrap for the purpose of styling your application.

Open your Gemfile and add the Bootstrap gem

#Gemfile

...
gem 'bootstrap-sass'

Run the command to install the gem.


bundle install

Rename app/assets/stylesheets/application.css to app/assets/stylesheets/application.scss and import the necessary files needed for Bootstrap to work.

#app/assets/stylesheets/application.scss

...
@import 'bootstrap-sprockets';
@import 'bootstrap';

Using your text editor, create a new file to hold our navigation code.

#app/views/layouts/_navigation.html.erb

<nav class="navbar navbar-default">
    <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">ClipUploader</a>
    </div>
    <div class="collapse navbar-collapse" id="navbar-collapse">
        <ul class="nav navbar-nav navbar-right">
          <li><%= link_to 'Home', root_path %></li>
          <li><%= link_to 'Upload New Image', new_photo_path %></li>
        </ul>
    </div>
  </div>
</nav>

The code above creates a naviation bar to hold your main navigation links.

Tweak your application layout to look like what I have below:

#app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>ClipUploader</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <!-- Renders navigation file located at app/views/layouts/_navigation.html.erb -->
    <%= render "layouts/navigation" %>
    <div id="flash">
      <% flash.each do |key, value| %>
        <div class="flash <%= key %>"><%= value %></div>
      <% end %>
    </div>
    <div class="container-fluid">
      <%= yield %>
    </div>
  </body>
</html>

To make the navigation bar visible in your application, you have to render it in your application layout. That is what we did above.

Integrate PaperClip

Open up your Gemfile and add the PaperClip gem.

#Gemfile

...
gem "paperclip", "~> 5.0.0"

Install the gem.

 bundle install 

We will be making use of one controller, let us generate that.

 rails generate controller Photos

Using your text editor, edit your PhotosController to look like what I have below:

 #app/controllers/photos_controller.rb

 class PhotosController < ApplicationController

 #Index action, photos gets listed in the order at which they were created
 def index
  @photos = Photo.order('created_at')
 end

 #New action for creating a new photo
 def new
  @photo = Photo.new
 end

 #Create action ensures that submitted photo gets created if it meets the requirements
 def create
  @photo = Photo.new(photo_params)
  if @photo.save
   flash[:notice] = "Successfully added new photo!"
   redirect_to root_path
  else
   flash[:alert] = "Error adding new photo!"
   render :new
  end
 end

 private

 #Permitted parameters when creating a photo. This is used for security reasons.
 def photo_params
  params.require(:photo).permit(:title, :image)
 end

end

In the above controller, we created three actions. The new and create actions are used when a photo is to be uploaded. If the photo gets saved the user is redirected to the root_path, else the new page is rendered.

Let us generate our Photo model.

rails generate model Photo title:string

Migrate your database:

rake db:migrate 

We need to add a few attributes to the photos table. To do that we will make use of the generator provided by Paperclip. Run the command below from your terminal.

rails generate paperclip photo image

This will generate a new migration file that looks like this:

#xxxxxxxx_add_attachment_image_to_photos.rb

class AddAttachmentImageToPhotos < ActiveRecord::Migration
  def self.up
    change_table :photos do |t|
      t.attachment :image
    end
  end

  def self.down
    remove_attachment :photos, :image
  end
end

Now run the migration.

rake db:migrate

Open up your Photo model to add PaperClip functionality.

#app/models/photo.rb

...
  #Mounts paperclip image
  has_attached_file :image

PaperClip cannot work if your Photo model is not equipped with it. You do so by adding the line of code used above.

You need to create the files below and paste in the code.

First, your index page should look like this.

#app/views/photos/index.html.erb

<div class="page-header"><h1>Upload Photo</h1></div>

<div class="media">
  <% @photos.each do |photo| %>
    <div class="media-left">
      <!-- Renders image -->
      <%= link_to image_tag(photo.image.url, class: 'media-object'), photo.image.url, target: '_blank' %>
    </div>
    <div class="media-body">
      <h4 class="media-heading"><%= photo.title %></h4>
    </div>
  <% end %>
</div>

Your new.html.erb file should look like this.

#app/views/photos/new.html.erb

<div class="page-header"><h1>Upload Photo</h1></div>

<%= form_for @photo, html: { multipart: true } do |f| %>
  <% if @photo.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= "#{pluralize(@photo.errors.count, "error")} prohibited this photo from being saved:" %>
      </h2>
      <ul>
        <% @photo.errors.full_messages.each do |msg| %>
          <li>
            <%= msg %>
          </li>
          <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :title %>
    <%= text_field :title, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :image %>
    <%= f.file_field :image, class: 'form-control' %>
  </div>

  <div class="form-group">
        <%= f.submit 'Upload Photo', class: 'btn btn-default' %>
    </div>
<% end %>

Time to configure your routes.

Open your route configuration file and paste in the following.

#config/routes.rb

...
  root to: "photos#index"
  resources :photos

We are listing the available photos on the home page of our application. Fire up your rails server and point your browser to http://localhost:3000/photos/new and you should get this.

Validations

Try uploading an image and you will get an error that looks like this.

You get this because PaperClip is concerned about the security of your application against malicious files. To fix this open up your Photo model and add this line of code.

#app/models/photo.rb

...
  #This validates the type of file uploaded. According to this, only images are allowed.
  validates_attachment_content_type :image, content_type: /Aimage/.*z/

PaperClip provides you with validators to ensure that malicious files are not uploaded to your application – this is what you just did above. The method validates the file type. In the above code, only images are permitted to be uploaded.

Try uploading an image again and it will work this time.

#app/models/photo.rb

...
  #Use this if you do not want to validate the uploaded file type.
  do_not_validate_attachment_file_type :image

If you prefer to take care of the validation of files uploaded yourself. You can tell PaperClip explicitly not to validate your file type using the line of code above.

PaperClip provides more validation options for developers to protect their application against malicious file uploads. You have to add it to your model.

#app/models/photo.rb

...
  #Validates file, file type and file size
  validates_attachment :image, presence: true,
  content_type: { content_type: "image/jpeg" },
  size: { in: 0..10.kilobytes }

In the above code we want to validate that an image is uploaded, and also ensure that non-image files are not uploaded. We’ve also gone ahead to set the maximum size allowed to 10kilobytes.

Deleting a Photo

In case you are wondering if it is possible to delete a uploaded file, PaperClip has got you covered. Add the code below to your PhotosController.

#app/controllers/photos_controller.rb

...

  #Destroy action for deleting an already uploaded image
  def destroy
  @photo = Photo.find(params[:id])
    if @photo.destroy
      flash[:notice] = "Successfully deleted photo!"
      redirect_to root_path
    else
      flash[:alert] = "Error deleting photo!"
    end
  end

The destroy action is called on whenever a user wants to delete a photo. You’ll need to add a button on your views. So open up app/views/photos/index.html.erb and make it look like this:

#app/views/photos/index.html.erb

<div class="page-header"><h1>Photos</h1></div>

<div class="media">
  <% @photos.each do |photo| %>
    <div class="media-left">
      <!-- Renders image -->
      <%= link_to image_tag(photo.image.url, class: 'media-object'), photo.image.url, target: '_blank' %>
      <!-- Button to delete image -->
      <%= link_to 'Remove', photo_path(photo), class: 'btn btn-danger', method: :delete, data: {confirm: 'Are you sure?'} %>
    </div>
    <div class="media-body">
      <h4 class="media-heading"><%= photo.title %></h4>
    </div>
  <% end %>
</div>

Now on your index page when you click the Remove button you will see a pop-up asking you to confirm if you want to delete the photo.

PaperClip VS Other Gems

You might be wondering, when is the best time for me to use PaperClip in my Rails application when compared to other gems. Here are the options I have for you.

  • Easy Attachment: PaperClip provides you with the option of enabling file upload in your Rails application easily. The process is involved is simple.
  • PaperClip is a great option when building simple application. It is advisable to avoid PaperClip is your application will be complex. For complex applications you may want to consider CarrierWave.
  • PaperClip is also not your perfect choice if your application will require multiple file upload. PaperClip handles single file upload pretty well.
  • If you need a gem that is actively maintained by an experienced team, PaperClip falls into such category. PaperClip is maintained and funded by thoughtbot.

Conclusion

We have been able to cover the basics and important aspects of file upload with PaperClip. To dig deep check out the official Github repo.
In the next part of this series, we will take a look at another file uploading gem.

Source:: scotch.io

Drawing and Animating Jelly Shapes with Canvas Part 2

By lmgonzalves

In the last tutorial we saw how to draw and animate jelly shapes. This tutorial will be a continuation, and we will see how to create an original slider full of jelly effects. Here is a gif:

We recommend to read the previous tutorial, for understanding the basics to get jelly shapes directly in the browser.

As we announced in the last post, while we build the slider, we will learn how to:

  • Draw more jelly shapes, and text!
  • Use images inside the shapes, not only solid colors.
  • Animate the shapes to show or hide them smoothly.
  • Morph from a jelly shape to another.
  • Make the entire slider responsive.

Let’s begin!

Basic HTML and CSS

The HTML and CSS code for our slider is pretty simple. The markup will be just a container element, and a canvas to draw all the jelly things:

<div class="jelly-container">
    <canvas class="jelly-canvas"></canvas>
</div>

And the CSS (SCSS) will be the following:

/* General styles */

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
}

body {
  background-color: #106CD9;
  overflow: hidden;
}

/* Jelly slider */

$width: 1300px;
$height: 600px;

// Setting dimensions
.jelly-container, .jelly-canvas {
  width: $width;
  height: $height;
}

// Centering
.jelly-container {
  position: relative;
  left: 50%;
  top: 50%;
  margin-left: - $width / 2;
  margin-top: - $height / 2;
}

As you can see, the markup and styles required are very straightforward. All the hard work will be in the JavaScript. But first, we need to draw our paths in a vector editor, let’s see how to do it!

Drawing SVG paths

We have used a vector editor to draw the SVG paths we need. We have exported the final result to a SVG file, getting something like this:

As you can see, our paths are overlapping, and there’s nothing wrong with that, because only one text and one arrow will be displayed at a time, while the others remain hidden. In addition, the circles around the big shape have been drawn to guide us when positioning the elements on the canvas.

Note also that each letter (and any other shape) must consist of a single closed path. We can use whatever font we want (turn to paths), but we need to make sure we are respecting this condition.

Finally, we have setted id attributes to every shape in the vector editor, so we can select them later from JavaScript.

Making the shapes jelly with Canvas

As we can see in the first part of this tutorial, to initialize the jelly shapes we only need this line of code:

var jelly = new Jelly('.jelly-canvas', options);

The hard work here is to populate the options to draw the paths we want with the effects we want.

As we will be working with many objects (the options for each shape), we will use a function to extend and object as we need. You can find it in the JavaScript file, and we will use it a lot. So, let’s see how we are setting the basic options for each type of shape:

/* Setup options */

// Base options for every shape
var baseOptions = {
    svg: 'jelly.svg',
    pointsNumber: 16
};

// Options for each circle
var optionsCircle = extend({}, baseOptions, {
    paths: '#jelly-circle',
    maxDistance: 40,
    mouseIncidence: 25,
    maxIncidence: 25
});

// Options for each image (big shapes)
var optionsImage = extend({}, baseOptions, {
    paths: '#jelly-image',
    maxDistance: 150,
    mouseIncidence: 50,
    maxIncidence: 50
});

// Options for each text
var optionsText = extend({}, baseOptions, {
    color: 'rgba(0, 0, 0, 0.6)',
    maxDistance: 15,
    mouseIncidence: 20,
    maxIncidence: 20
});

// Options for each arrow
var optionsArrow = extend({}, baseOptions, {
    color: 'rgba(255, 255, 255, 0.5)',
    pointsNumber: 30,
    maxDistance: 40,
    mouseIncidence: 20,
    maxIncidence: 20
});

Then we are going to populate an array with all the options for each shape. We have described the entire process for better understanding:

var items = 5;      // Number of items in the slider
var current = 2;    // Index of current item
var busy = false;   // To check if there is an animation in progress
var options = [];   // Array to populate the options

// Positions for each circle, obtained with the help of the vector editor
var circlePositions = [
    {x: -530, y: 5},
    {x: -330, y: -205},
    {x: 0, y: -285},
    {x: 330, y: -205},
    {x: 530, y: 5}
];

// Function to build the options for an specific item
function buildOptions(i) {
    var index = (i + 1);
    var isCurrent = i === current;

    // Options for each text, arrow and image, using the base options and the index
    var text = extend({}, optionsText, {paths: '#jelly-text-' + index + ' path'});
    var arrow = extend({}, optionsArrow, {paths: '#jelly-arrow-' + index});
    var image = extend({}, isCurrent ? optionsImage : optionsCircle, {image: 'img/image-' + index + '.jpg'});

    // If not the current item, set circle in the position defined, hide text, and hide arrow
    if (!isCurrent) {
        extend(image, circlePositions[i]);
        extend(text, {hidden: true});
        extend(arrow, {hidden: true});
    }

    // Push all of these to the options array
    options.push(text);
    options.push(arrow);
    options.push(image);
}

// Build the options for each item
for (var i = 0; i < items; i++) {
    buildOptions(i);
}

And with this we should have all our jelly shapes working in the browser 🙂

Handling events and adding interactivity

In the previous tutorial we have seen how we can handle the hover event to get the hovered item. We will not explain again how to do this, just keep in mind that we are saving the index of the hovered item in the variable hoverItem.

Let’s see the code below to understand how to handle the click events and perform animations accordingly.

canvas.addEventListener('click', function () {
    // Checking if an item is hovered and it is not busy
    if (hoverItem >= 0 && !busy) {
        busy = true;

        // Hide current text and arrow, and morph the big shape to a circle with the right position
        if (current !== undefined) {
            jelly.hide({i: current * 3, maxDelay: 400});
            jelly.hide({i: current * 3 + 1, maxDelay: 400});
            jelly.morph(extend({i: current * 3 + 2}, optionsCircle, circlePositions[current]));
        }

        // For the clicked item, show the hovered text and arrow, and morph the circle into the big shape
        jelly.show({i: hoverItem * 3, maxDelay: 400});
        jelly.show({i: hoverItem * 3 + 1, maxDelay: 400});
        jelly.morph(extend({i: hoverItem * 3 + 2}, optionsImage, {x: 0, y: 0}));

        // Update current item and release busy after some time to prevent malfunction
        current = hoverItem;
        setTimeout(function () { busy = false; }, 500);
    }
});

Surely you’ve noticed that we are using the functions show, hide and morph to perform the animations. All of these functions receive a set of options, and they have been properly documented in the github repository.

Making it responsive

So far, our slider should work very well. But if we see it in a small screen, we will notice a clear lack of responsiveness. Fortunately, we can solve this issue with the following JavaScript code:

/* Scale the container accordingly to windows/device size, making it responsive */

var jellyContainer = document.querySelector('.jelly-container');

function resize() {
    var scale = Math.min(
        1,
        window.innerWidth / jellyContainer.clientWidth,
        window.innerHeight / jellyContainer.clientHeight
    );
    jellyContainer.style.transform = 'scale(' + scale + ')';
}

window.addEventListener('resize', resize);

resize();

Conclusion

And with all these we should have an original jelly slider in the browser 🙂

See the DEMO, and get the full code on Github.

Please note that this demo is very experimental, with a high consumption of resources. So, use jelly shapes moderately as they can kill the performance of the web page.

We hope you find it fun and inspiring!

Source:: scotch.io

Feature watch: ECMAScript 2018

By Axel Rauschmayer

This blog post tracks what features may be included in ES2018. I will continually update it.

Proposed ECMAScript 2018 features

Recall that every ECMAScript feature proposal goes through stages:

  • Stage 4 means that a feature will be in the next release (or the one after that).
  • Stage 3 means that a feature still has a chance of being included in the next release.

Stage 4 and part of ECMAScript spec draft

(No proposals at this time.)

Candidate features (stage 3)

The following features are currently at stage 3:

FAQ

What do the stages mean?

They refer to maturity stages of the so-called “TC39 process”. Check chapter “The TC39 process for ECMAScript features” in “Exploring ES2016 and ES2017” for more information.

How is [my favorite proposed feature] doing?

If you are wondering what stages various proposed features are in, consult the readme of the ECMA-262 GitHub repository.

Further reading

The following books by me are free to read online:

Source:: 2ality

Implement a Favoriting Feature Using Laravel and Vue.js

By mezie

These days, various web applications are in one way or the other implementing a kind of favorite/like/recommend feature on the websites. These can be seen on sites like Medium, Facebook, Laracasts, and even here on scotch.io and school.scotch.io.

In this tutorial, I’ll be showing you how to implement a favorites feature using Vue.js in your Laravel application. Though we’ll be using the term favorites in this tutorial, this can be replaced with likes, recommends depending on your application.

What We’ll Be Building

We’ll be building a simple Posts app. This app will comprise of users and posts. Users will be able to create posts and as well mark posts as favorites. Finally, users will be able to see all the posts they marked as favorites.

The app will have a User model and a Post model, there will be an authentication system which will allow only authenticated users mark/unmark posts as favorites. We’ll make use of VueJs and Axios to make marking/un-marking posts as favorites dynamic, that is without reloading the page.

Before we start building, let’s take a quick look at what the Posts app will look like when we are done.

Let’s Get started

We’ll start by creating a new Laravel project, the name of the project will be laravel-vue-favorite.

laravel new laravel-vue-favorite

This will create a new Laravel 5.4 (which is the current version as at the time of this tutorial) project.

Installing NPM Dependencies

In a fresh installation of Laravel, Laravel provides some frontend frameworks and libraries with some basic setup to integrate these packages together. Among the frameworks and libraries are Bootstrap, VueJs and Axios, which we will be using in this tutorial. But we still need to install these dependencies through NPM:

npm install

Also, we’ll make use of Laravel Mix to compile and build our CSS and JavaScript. The command above will also install all Laravel Mix dependencies.

Models And Migrations

For our Posts app, we’ll need a User model (which comes with Laravel), a Post model and a Favorite model and their respective migration files.

php artisan make:model Post -m
php artisan make:model Favorite -m

These will create a Post model and a Favorite model along with their migration files respectively. Open the posts table migration file and update the up() with:

/**
 * Define posts table schema
 */
public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id')->unsigned();
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}

The posts table will contain an id, user_id (ID of the user that created the post), title, body, and some timestamps columns.

Next, open the favorites table migration file and update the up() with:

/**
 * Define favorites table schema
 */
public function up()
{
    Schema::create('favorites', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id')->unsigned();
        $table->integer('post_id')->unsigned();
        $table->timestamps();
    });
}

The favorites table will be a pivot table. It will contain two columns: user_id which will hold the ID of the user that favorited a post and post_id which will the ID of the post that was favorited.

For the users table migration, we’ll be using the default Laravel provided.

Before we run our migrations, let’s setup our database. Add your database details to the .env file:

DB_DATABASE=laravue
DB_USERNAME=root
DB_PASSWORD=root

Remember to update with your own database details. Now we can go on and run our migrations:

php artisan migrate

Database Seeder

We’ll also generate some seed data which we can test our app with. To generate dummy data, we’ll make use of Laravel Model Factories. Laravel model factories make use of the Faker PHP library.

We’ll generate dummy data of Users and Posts. When you open the database/factories/ModelFactory.php file, you will see that Laravel provides a User model factory, which means we only need to create a Post model factory. Add the snippets below to database/factories/ModelFactory.php just after the User model factory:

// database/factories/ModelFactory.php

$factory->define(AppPost::class, function (FakerGenerator $faker) {
    // Get a random user
    $user = AppUser::inRandomOrder()->first();

    // generate fake data for post
    return [
        'user_id' => $user->id,
        'title' => $faker->sentence,
        'body' => $faker->text,
    ];
});

Let’s quickly run through the code. Remember from our posts table migration, we defined that a post must have a user ID. So, we get a random user and assign the user_id of a post to the ID of the random user, then we use Faker to generate the title and body of the post.

With our model factories done, let’s move on to create our database seeder classes by running these commands:

php artisan make:seeder UsersTableSeeder
php artisan make:seeder PostsTableSeeder

Open database/seeds/UsersTableSeeder.php and update the run() with:

// database/seeds/UsersTableSeeder.php

/**
 * Run the database seeds to create users.
 *
 * @return void
 */
public function run()
{
    factory(AppUser::class, 5)->create();
}

This will create 5 different users with dummy data when the seeder is run. We’ll do the same for Posts. Open database/seeds/PostsTableSeeder.php and update the run() with:

// database/seeds/PostsTableSeeder.php

/**
 * Run the database seeds to create posts.
 *
 * @return void
 */
public function run()
{
    factory(AppPost::class, 10)->create();
}

This will create 10 different posts with dummy data when the seeder is run.

Before we run the database seeders, let’s update the database/seeds/DatabaseSeeder.php which is provided by default:

// database/seeds/DatabaseSeeder.php

/**
 * Run the database seeds.
 *
 * @return void
 */
public function run()
{
    $this->call(UsersTableSeeder::class);
    $this->call(PostsTableSeeder::class);
}

Now we can run the database seeders:

php artisan db:seed

You should now see some dummy data in your database.

Authenticating Users

Before a user can mark a post has favorite, the user must be logged in. So we need a kind of authentication system. Luckily for us, Laravel got our back on this. We’ll use the artisan make:auth command to scaffold an authentication system.

php artisan make:auth

This will create all of the necessary routes and views for authentication. We can go and register as a user, which we will use to test the functionality of the application we are building.

Defining Our Routes

Let’s define the routes of our application. Open routes/web.php and update with below:

// routes/web.php

Auth::routes();

Route::get('/', 'PostsController@index');

Route::post('favorite/{post}', 'PostsController@favoritePost');
Route::post('unfavorite/{post}', 'PostsController@unFavoritePost');

Route::get('my_favorites', 'UsersController@myFavorites')->middleware('auth');

The routes are pretty straightforward. Auth routes that Laravel created when we ran the make:auth command. A route to the homepage that will list all posts, two other routes for favoriting and unfavoriting posts. Lastly, a route that displays all posts that have been marked as favorites by a user. This route will be accessible to only authenticated users.

When a user registers or login, Laravel will redirect them to the /home route by default. Since we have removed the /home route that Laravel created when we ran make:auth. We need to update the redirectTo property of both app/Http/Controllers/Auth/LoginController.php and app/Http/Controllers/Auth/RegisterController.php to:

protected $redirectTo = '/';

Defining Users To Favorite Posts Relationship

Since a user can mark many posts as favorites and a post can be marked as favorites by many users, the relationship between users and favorite posts will be a many to many relationships. To define this relationship, open the User model and add a favorites():


// app/User.php

/**
 * Get all of favorite posts for the user.
 */
public function favorites()
{
    return $this->belongsToMany(Post::class, 'favorites', 'user_id', 'post_id')->withTimeStamps();
}

Laravel will assume the pivot table is post_user but since we gave the pivot table a different name (favorites), we have to pass in some additional arguments. The second argument is the name of the pivot table (favorites). The third argument is the foreign key name (user_id) of the model on which you are defining the relationship (User), while the fourth argument is the foreign key name (post_id) of the model that you are joining to (Post).

Noticed we chained withTimeStamps() to the belongsToMany(). This will allow the timestamps (create_at and updated_at) columns on the pivot table be affected whenever a row is inserted or updated.

Posts Controller

Let’s create a new controller that will handle displaying posts, marking a post as favorite and unfavorite a post.

php artisan make:controller PostsController

Open the newly created app/Http/Controllers/PostsController.php and add the snippet below to it:

// app/Http/Controllers/PostsController.php

// remember to use the Post model
use AppPost;

/**
 * Display a paginated list of posts.
 *
 * @return Response
 */
public function index()
{
    $posts = Post::paginate(5);

    return view('posts.index', compact('posts'));
}

The index() will get all posts and paginate them into 5 per page. Then render a view file (that we are yet to create) along with the posts, which will do the actual displaying of the posts.

Remember when we ran make:auth command that Laravel created some views. We’ll be using the resources/views/layouts/app.blade.php that was created and make some few additions to it. Add the code below just after the </code>:</p> <pre><code>// resources/views/layouts/app.blade.php <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" /></code></pre> <p>Then add this just before the <code>Logout</code> list item:</p> <pre><code>// resources/views/layouts/app.blade.php <li> <a href="{{ url('my_favorites') }}">My Favorites</a> </li></code></pre> <p>Now let’s create the <code>index</code> view. Create a new <code>posts</code> folder within <code>views</code> directory and create a new <code>index.blade.php</code> file within the newly created folder. Paste the code below into <code>resources/views/posts/index.blade.php</code>:</p> <pre><code>// resources/views/posts/index.blade.php @extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="page-header"> <h3>All Posts</h3> </div> @forelse ($posts as $post) <div class="panel panel-default"> <div class="panel-heading"> {{ $post->title }} </div> <div class="panel-body"> {{ $post->body }} </div> </div> @empty <p>No post created.</p> @endforelse {{ $posts->links() }} </div> </div> </div> @endsection</code></pre> <p>Pretty simple markup that displays a paginated list of posts. Open the homepage in your browser, you should see page like the image below:</p> <p><img src="https://cdn.scotch.io/1965/gLHUX3KFRcq3ex4nTndR_screencapture-127-0-0-1-8000-1486468340684.png" alt=""></p> <p>Next, let’s go back to <code>PostsController</code> and add the methods that will handle marking a post as favorite and unfavorite a post. Add the code below to <code>PostsController</code>:</p> <pre><code>// app/Http/Controllers/PostsController.php // remember to use use IlluminateSupportFacadesAuth; /** * Favorite a particular post * * @param Post $post * @return Response */ public function favoritePost(Post $post) { Auth::user()->favorites()->attach($post->id); return back(); } /** * Unfavorite a particular post * * @param Post $post * @return Response */ public function unFavoritePost(Post $post) { Auth::user()->favorites()->detach($post->id); return back(); }</code></pre> <p>The <code>favoritePost()</code> takes a post as an argument. Using the <code>favorites</code> relationship we defined above, we attach the post ID to the ID of the authenticated user then insert into the <code>favorites</code> table. Finally, return back to the previous page.</p> <p>The <code>unFavoritePost()</code> is the reverse of <code>favoritePost()</code> which simply remove the ID of the authenticated user along with the post ID from the <code>favorites</code> table.</p> <h2>Integrating With VueJs</h2> <p>It’s time to integrate <code>Vue</code> into our application. We’ll make the favorite button/icon a Vue component. Making it a Vue component will allow for reuse in multiple places with our application.</p> <p>Once the favorite button/icon is clicked, we’ll mark the post as favorite or unfavorite without reloading the page, that is through <code>AJAX</code>. For this, we’ll make use of <code>Axios</code> which is a Promise based HTTP client for the browser and node.js.</p> <h2>Creating The Favorite Component</h2> <p>Create a new <code>Favorite.vue</code> file within the <code>resources/assets/js/components</code> folder and paste the code below into it:</p> <pre><code>// resources/assets/js/components/Favorite.vue <template> <span> <a href="#" v-if="isFavorited" @click.prevent="unFavorite(post)"> <i class="fa fa-heart"></i> </a> <a href="#" v-else @click.prevent="favorite(post)"> <i class="fa fa-heart-o"></i> </a> </span> </template> <script> export default { props: ['post', 'favorited'], data: function() { return { isFavorited: '', } }, mounted() { this.isFavorited = this.isFavorite ? true : false; }, computed: { isFavorite() { return this.favorited; }, }, methods: { favorite(post) { axios.post('/favorite/'+post) .then(response => this.isFavorited = true) .catch(response => console.log(response.data)); }, unFavorite(post) { axios.post('/unfavorite/'+post) .then(response => this.isFavorited = false) .catch(response => console.log(response.data)); } } } </script></code></pre> <p>The <code>Favorite</code> component has two sections: <code>template</code> and <code>script</code>. In the <code>template</code> section, we defined the markup that will be rendered when the component is used. We are using conditional rendering to show the appropriate button/icon. That is, if <code>isFavorited</code> is <code>true</code>, the button/icon should be marked as favorite and on click of the button/icon should trigger <code>unFavorite()</code>. Else the button/icon should be marked as unfavorite and on click of the button/icon should trigger <code>favorite()</code>.</p> <p>Moving on to the <code>script</code> section, we defined some properties for the component; <code>post</code> (will be the ID of the post) and <code>favorited</code> (will either be <code>true</code> or <code>false</code> depending on if the post has been favorited by the authenticated user). We also defined an <code>isFavorited</code> data which will be used for the conditional rendering from above.</p> <p>When the component is mounted, we set the value of <code>isFavorited</code> to the value of <code>isFavorite</code> computed property. That is, the <code>isFavorite</code> computed property will return the value of <code>favorited</code> prop which will either be <code>true</code> or <code>false</code>. We use a computed property so as to reactively get the value of the <code>favorited</code> prop instead using the value of <code>favorited</code> prop that was passed directly.</p> <p>Lastly, we defined two methods: <code>favorite()</code> and <code>unFavorite()</code> which both accepts the <code>post</code> prop as arguments. Using <code>Axios</code>, we make a <code>POST</code> request to the routes we defined earlier. For the <code>favorite()</code>, once the <code>POST</code> request is successful, we set <code>isFavorited</code> to <code>true</code> and otherwise console log the errors. Same is applicable to the <code>unFavorite()</code> just that we set <code>isFavorited</code> to <code>false</code>.</p> <h2>Registering The Favorite Component</h2> <p>Before we can start to use the <code>Favorite</code> component, we need to first register it on our <code>Vue</code> root instance. Open <code>resources/assets/js/app.js</code>, you will see that Laravel register an <code>Example</code> component. We are going to replace that with the <code>Favorite</code> component:</p> <pre><code>// resources/assets/js/app.js Vue.component('favorite', require('./components/Favorite.vue'));</code></pre> <p>Now we can compile and build our styles and scripts:</p> <pre><code>npm run dev</code></pre> <h2>Using The Favorite Component</h2> <p>We can now use the <code>Favorite</code> component. Open <code>resources/views/posts/index.blade.php</code> and add the snippets below to it after the closing <code>div</code> of the <code>panel-body</code>:</p> <pre><code>// resources/views/posts/index.blade.php @if (Auth::check()) <div class="panel-footer"> <favorite :post={{ $post->id }} :favorited={{ $post->favorited() ? 'true' : 'false' }} ></favorite> </div> @endif</code></pre> <p>The favorite button/icon will only be displayed to authenticated users. As you can see, we passed to the <code>Favorite</code> component the props that we defined when we created the component. To know if a post is has been favorited by the authenticated user, we call a <code>favorited()</code> (which we are yet to create) on the post.</p> <p>To create <code>favorited()</code>, open <code>app/Post.php</code> and add the code below to it:</p> <pre><code>// app/Post.php // remember to use use AppFavorite; use IlluminateSupportFacadesAuth; /** * Determine whether a post has been marked as favorite by a user. * * @return boolean */ public function favorited() { return (bool) Favorite::where('user_id', Auth::id()) ->where('post_id', $this->id) ->first(); }</code></pre> <p>This gets and casts to <code>boolean</code> the first result where the <code>user_id</code> is equal to that of the authenticated user and where the <code>post_id</code> is equal to the ID of the post the method is called on.</p> <p>If you visit the homepage of the application in the browser and login, you should get something similar to:</p> <p><img src="https://cdn.scotch.io/1965/GVQN0gvQBeEs5iTBPAzg_screencapture-127-0-0-1-8000-1486238585430.png" alt=""></p> <p>As you can see I have marked some posts as favorites.</p> <h2>Displaying User Favorite Posts</h2> <p>Won’t it be nice for users to be able to see all the posts they have marked as favorites? Sure it will be. Remember we defined a <code>my_favorites</code> route that will be accessible to only authenticated users, this is where users will be able to see the posts they’ve marked as favorites.</p> <p>Let’s create a <code>UsersController</code> that will handle this route.</p> <pre><code>php artisan make:controller UsersController</code></pre> <p>Open <code>app/Http/Controllers/UsersController.php</code> and add the code below to it:</p> <pre><code>// app/Http/Controllers/UsersController.php // remember to use use IlluminateSupportFacadesAuth; /** * Get all favorite posts by user * * @return Response */ public function myFavorites() { $myFavorites = Auth::user()->favorites; return view('users.my_favorites', compact('myFavorites')); }</code></pre> <p>The <code>myFavorites()</code> uses the <code>favorites</code> relationship we defined earlier, get all the posts that the authenticated user has marked as favorites. Then return a view along with favorites posts.</p> <p>Now let’s create the view. Create a new <code>users</code> folder within the <code>resources/views</code> directory and within the <code>users</code> folder, create a new file <code>my_favorites.blade.php</code> and paste the code below to it:</p> <pre><code>// resources/views/users/my_favorites.blade.php @extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="page-header"> <h3>My Favorites</h3> </div> @forelse ($myFavorites as $myFavorite) <div class="panel panel-default"> <div class="panel-heading"> {{ $myFavorite->title }} </div> <div class="panel-body"> {{ $myFavorite->body }} </div> @if (Auth::check()) <div class="panel-footer"> <favorite :post={{ $myFavorite->id }} :favorited={{ $myFavorite->favorited() ? 'true' : 'false' }} ></favorite> </div> @endif </div> @empty <p>You have no favorite posts.</p> @endforelse </div> </div> </div> @endsection</code></pre> <p>The markup is similar to that of <code>index.blade.php</code>. As you can see, we also used the <code>Favorite</code> component. When viewed in the browser, you should see something similar to:</p> <p><img src="https://cdn.scotch.io/1965/SHh0dhO2QtKcOJkqHcjE_screencapture-127-0-0-1-8000-my_favorites-1486238634118.png" alt=""></p> <h2>Conclusion</h2> <p>That’s it, we are done building the Post app and seen how to allow only authenticated users mark/unmark posts as favorites without reloading the page using VueJs and Axios. I hope you find this tutorial useful. If you encounter any problems following this tutorial or have questions/suggestions, kindly drop them in the comment section below. Also, I have made a <a target="_blank" href="https://github.com/ammezie/vue-favorite">vue-favorite</a> component based on this tutorial which can be installed through <code>NPM</code>.</p> <p>Source:: <a href="https://scotch.io/tutorials/implement-a-favoriting-feature-using-laravel-and-vue-js" target="_blank" title="Implement a Favoriting Feature Using Laravel and Vue.js">scotch.io</a></p> <div style="min-height:33px;" class="really_simple_share really_simple_share_button robots-nocontent snap_nopreview"><div class="really_simple_share_twitter" style="width:100px;"><a href="https://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-text="Implement a Favoriting Feature Using Laravel and Vue.js" data-url="http://webammer.com/?p=1022761" data-via="" ></a></div><div class="really_simple_share_facebook_share_new" style="width:110px;"><div class="fb-share-button" data-href="http://webammer.com/?p=1022761" data-type="button_count" data-width="110"></div></div><div class="really_simple_share_google_share" style="width:110px;"><div class="g-plus" data-action="share" data-href="http://webammer.com/?p=1022761" data-annotation="bubble" ></div></div><div class="really_simple_share_linkedin" style="width:100px;"><script type="IN/Share" data-counter="right" data-url="http://webammer.com/?p=1022761"></script></div></div> <div class="really_simple_share_clearfix"></div> </div><!-- .entry-content --> <footer class="entry-meta"> </footer><!-- .entry-meta --> </article><!-- #post --> <article id="post-1022760" class="post-1022760 post type-post status-publish format-standard hentry category-rss-feed"> <header class="entry-header"> <h1 class="entry-title"> <a href="http://webammer.com/?p=1022760" rel="bookmark"><a href=https://scotch.io/tutorials/making-our-flexbox-grid-responsive target=_blank >Making our Flexbox Grid Responsive with Less</a></a> </h1> <div class="entry-meta"> <span class="date"><a href="http://webammer.com/?p=1022760" title="Permalink to Making our Flexbox Grid Responsive with Less" rel="bookmark"><time class="entry-date" datetime="2017-02-28T18:48:21+00:00">February 28, 2017</time></a></span><span class="categories-links"><a href="http://webammer.com/?cat=14" rel="category">RSS Feed</a></span><span class="author vcard"><a class="url fn n" href="http://webammer.com/?author=1" title="View all posts by webammer_anand" rel="author">webammer_anand</a></span> </div><!-- .entry-meta --> </header><!-- .entry-header --> <div class="entry-content"> <p><span style="font-style:italic;font-size:16px">By <a target="_blank" href="https://scotch.io/tutorials/making-our-flexbox-grid-responsive">SaraVieira</a></span> </p> <div class="ftpimagefix" style="float:left"><a target="_blank" href="https://scotch.io/tutorials/making-our-flexbox-grid-responsive"><img src="https://scotch.io/wp-content/uploads/2017/02/K4SRsKCZQOO3EPB7TxO1_flex2.png.jpg" alt=""></a></div> <p>If you have been following along with this Flexbox grid series you know that in the <a target="_blank" href="https://scotch.io/tutorials/build-a-flexbox-grid-with-less">previous article</a> we created a simple flexbox grid that had columns and offsets, but it wasn’t responsive yet and we can’t have that right?</p> <p>Let’s start making this grid responsible so that you can use it in any project you may have.</p> <h2>The Breakpoints</h2> <p>In this grid I decided to have three breakpoints, <code>s</code>, <code>m</code> and <code>l</code> , anything above <code>l</code> uses the primary columns and if you remember those were the column classes that had no prefix, like: <code>col-12</code>.<br /> First thing we need to do is set this in our <code>_vars.less</code> so that anyone can change this to fit their project:</p> <pre><code>// _vars.less @s:~"only screen and (max-width: 480px)"; // Small Phones @m:~"only screen and (max-width: 768px)"; // Normal Phones @l:~"only screen and (max-width: 1024px)"; // Tablets</code></pre> <p>Basically what you see is all the breakpoints I think you could ever need for mobile but if you believe more are needed, please add your own and name it something you like. You will see how to integrate these breakpoints in a little while.</p> <h2>Creating the loops</h2> <p>We already created a loop for our non-responsive breakpoints, and it looked like this:</p> <pre><code>//_loops.less // Loop for normal cols .generateCols(@counter) when (@counter > 0) { @width: (@counter / @cols ) * 100; .col-@{counter} { flex-basis: ~"@{width}%"; max-width: ~"@{width}%"; } .col-offset-@{counter} { margin-left: ~"@{width}%"; } .generateCols(@counter - 1)); }</code></pre> <p>If you look at the code, you see that in this case the loop takes one argument and that is the column number. That’s great for grids that are not responsive, but when you want to create breakpoints you also need to need it to generate the classes with the current breakpoint name, so we will need to create a new loop in our <code>_loops.less</code> and this one will create our responsive columns:</p> <pre><code>_loops.less // Create the responsive loops that also takes the media as an argument // this media is our breakpoints .generateResponsiveCols(@counter, @media) when (@counter > 0) { // In here we define the width // and this will be the number of the column we are in divided by the number of columns we have. // Imagine this is column 6 and this will give us 0.5 and if multiply it by 100 we will get 50% @width: (@counter / @cols ) * 100; // we used to only have .col-@{counter} but now that we added the media param // we insert it here so that it creates classses like .col-s-10 and we can use this in our grid .col-@{media}-@{counter} { flex-basis: ~"@{width}%"; max-width: ~"@{width}%"; } // Create our resposnive offset classes .col-offset-@{media}-@{counter} { margin-left: ~"@{width}%"; } // Decrease the counter by one so we don't have an infinite loop // and also pass it the media so that it generates the next loop .generateResponsiveCols((@counter - 1), @media); }</code></pre> <p>So we have our loops, and they will generate all our desired classes, but we still need to call these loops for them to create the classes we need so now we move to our <code>_grid.less</code> and create all the media queries we need, and inside each of them we call our <code>generateResponsiveCols</code> mixin and pass it the number of columns and our current breakpoint.</p> <pre><code> _grid.less .row, .column { ... @media @l { // Call our large columns .generateResponsiveCols(@cols, l); } @media @m { // Call our medium columns .generateResponsiveCols(@cols, m); } @media @s { // Call our small columns .generateResponsiveCols(@cols, s); } }</code></pre> <p>Now if you run <code>gulp less</code> in your console and open up our HTML you should be able to see our responsive columns at work.<br /> If you resize, you will find that they behave according to the breakpoint you are currently viewing on the browser.</p> <h3>Phone View</h3> <p><img src="https://cdn.scotch.io/43/vK6MI55TnSRJB9XHBwtw_Screen%20Shot%202017-02-12%20at%2018.01.43.png" alt="Phone View"></p> <h3>iPad portrait view</h3> <p><img src="https://cdn.scotch.io/43/ixUABWsCTx6fcl5eDffm_Screen%20Shot%202017-02-12%20at%2017.56.48.png" alt="iPad portrait view"></p> <h3>iPad landscape view</h3> <p><img src="https://cdn.scotch.io/43/xpu1DF56RIwOhDUmfXZq_Screen%20Shot%202017-02-12%20at%2017.57.19.png" alt="iPad landscape view"></p> <p>So we got our responsive classes working great, but we are still missing one thing, a crucial part in any responsive grid is the ability to hide columns by the size of the screen, and as you can see by our phone view this is not happening yet, and it’s time to add it.<br /> For simplicity I will call these classes <code>col-s-hidden</code>, <code>.col-m-hidden</code>, <code>col-l-hidden</code>, <code>.col-hidden</code> to cover all our column names and breakpoints.<br /> Adding these classes should be relatively easy since all we need to do is hide the desired column in the correct breakpoint. But something like this is better explained through code:</p> <pre><code> // grid.less @media @l { .generateResponsiveCols(@cols, l); // hide any element that has the class col-l-hidden .col-l-hidden { display: none; } // show all the other hidden elements that were meant for other breakpoints .col-s-hidden, .col-m-hidden, .col-hidden { display: block; } }</code></pre> <p>All we did here was hide the column that matched the breakpoint and show all the other columns since those that did not match the breakpoint. Now that we saw how to add these classes let’s do the same for all the other breakpoints:</p> <pre><code>// grid.less .row, .column { .... // Aply hidden classes to the "primary" columns .col-s-hidden, .col-m-hidden, .col-l-hidden { display: block; } .col-hidden { display: none; } // add hidden classes to our l breakpoint @media @l { .generateResponsiveCols(@cols, l); .col-s-hidden, .col-m-hidden, .col-hidden { display: block; } .col-l-hidden { display: none; } } // add hidden classes to our m breakpoint @media @m { .generateResponsiveCols(@cols, m); .col-s-hidden, .col-l-hidden, .col-hidden { display: block; } .col-m-hidden { display: none; } } // add hidden classes to our s breakpoint @media @s { .generateResponsiveCols(@cols, s); .col-m-hidden, .col-l-hidden, .col-hidden { display: block; } .col-s-hidden { display: none; } } }</code></pre> <p>Now if you reload our <code>index.html</code> and use a small screen size you will see something like:</p> <p><img src="https://cdn.scotch.io/43/p9Qk3CgAQTqoivGi2V0L_Screen%20Shot%202017-02-12%20at%2017.56.21.png" alt=""></p> <p>That’s it for the responsive part of our grid, as you could see since we already had the foundation from our previous loops doing this part was a breeze.</p> <h2>Adding Helper Classes</h2> <p>Something that I appreciate to see in any grid or CSS framework is helper classes. What I mean by helper classes is anything that helps me align the elements according to how I want to see them in that container.<br /> With flex, we can do this easily using <code>align-items</code> and <code>justify-content</code> so let’s add some horizontal alignment to our grid:</p> <pre><code>// grid.less .container { .... .justify-center { justify-content: center; } .justify-end { justify-content: flex-end; } .justify-start { justify-content: flex-start; } .justify-around { justify-content: space-around; } .justify-between { justify-content: space-between; } }</code></pre> <p>if you try to add one of these classes to our columns, you will see that it does indeed work, but the problem is that if you try to add this to the <code>ul</code> for example, it won’t do anything because the <code>ul</code> is not a flex container.<br /> We need to make sure that we always set elements that have a class that includes <code>justify-</code> to <code>display: flex</code> and to do something like that we add this before defining our justify classes:</p> <pre><code>// grid.less [class*="justify-"] { display: flex; }</code></pre> <p>And now we have fully working justify classes for all our horizontal alignment needs.<br /> You may be wondering where our vertical alignment classes are seeing that those are the tricky ones to do without flex, well to have our vertical we need to add this:</p> <pre><code>// grid.less .container { .... [class*="items-"] { display: flex; } .items-center { align-items: center; } .items-start { align-items: flex-start; } .items-end { align-items: flex-end; } .items-stretch { align-items: stretch; } }</code></pre> <p>And voilá we got some pretty good helper classes to add to our grid and help us align all our elements.<br /> If there are some other classes, you feel like you need to improve your grid like margin and padding classes feel free to add them in this file, for me, these are the crucial ones to have on any grid.</p> <h2>Conclusion</h2> <p>As you can see creating a fully fledged grid using Less and flexbox is not as daunting of a task as you may have thought and that’s because like in any programming task all you need to do is divide a large project into smaller less daunting tasks and start coding away.</p> <p>I hope these tutorials helped you get a better grasp of Less loops and also made you have some love for it if you didn’t already.</p> <p>I will be releasing this as an open source grid for everyone to use shortly but in the meantime, you can see it on <a target="_blank" href="https://github.com/SaraVieira/Flexy">Github</a> and let me know your feedback and of course hack my repository away.</p> <p>Source:: <a href="https://scotch.io/tutorials/making-our-flexbox-grid-responsive" target="_blank" title="Making our Flexbox Grid Responsive with Less">scotch.io</a></p> <div style="min-height:33px;" class="really_simple_share really_simple_share_button robots-nocontent snap_nopreview"><div class="really_simple_share_twitter" style="width:100px;"><a href="https://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-text="Making our Flexbox Grid Responsive with Less" data-url="http://webammer.com/?p=1022760" data-via="" ></a></div><div class="really_simple_share_facebook_share_new" style="width:110px;"><div class="fb-share-button" data-href="http://webammer.com/?p=1022760" data-type="button_count" data-width="110"></div></div><div class="really_simple_share_google_share" style="width:110px;"><div class="g-plus" data-action="share" data-href="http://webammer.com/?p=1022760" data-annotation="bubble" ></div></div><div class="really_simple_share_linkedin" style="width:100px;"><script type="IN/Share" data-counter="right" data-url="http://webammer.com/?p=1022760"></script></div></div> <div class="really_simple_share_clearfix"></div> </div><!-- .entry-content --> <footer class="entry-meta"> </footer><!-- .entry-meta --> </article><!-- #post --> <nav class="navigation paging-navigation" role="navigation"> <h1 class="screen-reader-text">Posts navigation</h1> <div class="nav-links"> <div class="nav-previous"><a href="http://webammer.com/?m=201702&paged=2" ><span class="meta-nav">←</span> Older posts</a></div> </div><!-- .nav-links --> </nav><!-- .navigation --> </div><!-- #content --> </div><!-- #primary --> <div id="tertiary" class="sidebar-container" role="complementary"> <div class="sidebar-inner"> <div class="widget-area"> <aside id="archives-3" class="widget widget_archive"><h3 class="widget-title">Archives</h3> <ul> <li><a href='http://webammer.com/?m=201712'>December 2017</a></li> <li><a href='http://webammer.com/?m=201711'>November 2017</a></li> <li><a href='http://webammer.com/?m=201710'>October 2017</a></li> <li><a href='http://webammer.com/?m=201709'>September 2017</a></li> <li><a href='http://webammer.com/?m=201708'>August 2017</a></li> <li><a href='http://webammer.com/?m=201707'>July 2017</a></li> <li><a href='http://webammer.com/?m=201706'>June 2017</a></li> <li><a href='http://webammer.com/?m=201705'>May 2017</a></li> <li><a href='http://webammer.com/?m=201704'>April 2017</a></li> <li><a href='http://webammer.com/?m=201703'>March 2017</a></li> <li><a href='http://webammer.com/?m=201702'>February 2017</a></li> <li><a href='http://webammer.com/?m=201701'>January 2017</a></li> <li><a href='http://webammer.com/?m=201612'>December 2016</a></li> <li><a href='http://webammer.com/?m=201611'>November 2016</a></li> <li><a href='http://webammer.com/?m=201610'>October 2016</a></li> <li><a href='http://webammer.com/?m=201609'>September 2016</a></li> <li><a href='http://webammer.com/?m=201608'>August 2016</a></li> <li><a href='http://webammer.com/?m=201607'>July 2016</a></li> <li><a href='http://webammer.com/?m=201606'>June 2016</a></li> <li><a href='http://webammer.com/?m=201605'>May 2016</a></li> <li><a href='http://webammer.com/?m=201604'>April 2016</a></li> <li><a href='http://webammer.com/?m=201603'>March 2016</a></li> <li><a href='http://webammer.com/?m=201602'>February 2016</a></li> <li><a href='http://webammer.com/?m=201601'>January 2016</a></li> <li><a href='http://webammer.com/?m=201512'>December 2015</a></li> <li><a href='http://webammer.com/?m=201511'>November 2015</a></li> <li><a href='http://webammer.com/?m=201510'>October 2015</a></li> <li><a href='http://webammer.com/?m=201509'>September 2015</a></li> <li><a href='http://webammer.com/?m=201508'>August 2015</a></li> <li><a href='http://webammer.com/?m=201507'>July 2015</a></li> <li><a href='http://webammer.com/?m=201506'>June 2015</a></li> <li><a href='http://webammer.com/?m=201505'>May 2015</a></li> <li><a href='http://webammer.com/?m=201504'>April 2015</a></li> <li><a href='http://webammer.com/?m=201503'>March 2015</a></li> <li><a href='http://webammer.com/?m=201502'>February 2015</a></li> <li><a href='http://webammer.com/?m=201501'>January 2015</a></li> <li><a href='http://webammer.com/?m=201412'>December 2014</a></li> <li><a href='http://webammer.com/?m=201407'>July 2014</a></li> <li><a href='http://webammer.com/?m=201406'>June 2014</a></li> <li><a href='http://webammer.com/?m=201403'>March 2014</a></li> <li><a href='http://webammer.com/?m=201402'>February 2014</a></li> <li><a href='http://webammer.com/?m=201310'>October 2013</a></li> <li><a href='http://webammer.com/?m=201309'>September 2013</a></li> <li><a href='http://webammer.com/?m=201308'>August 2013</a></li> <li><a href='http://webammer.com/?m=201307'>July 2013</a></li> <li><a href='http://webammer.com/?m=201306'>June 2013</a></li> <li><a href='http://webammer.com/?m=201305'>May 2013</a></li> </ul> </aside><aside id="categories-3" class="widget widget_categories"><h3 class="widget-title">Categories</h3> <ul> <li class="cat-item cat-item-12"><a href="http://webammer.com/?cat=12" >AngularJs</a> </li> <li class="cat-item cat-item-8"><a href="http://webammer.com/?cat=8" >apache</a> </li> <li class="cat-item cat-item-2"><a href="http://webammer.com/?cat=2" >HTML</a> </li> <li class="cat-item cat-item-5"><a href="http://webammer.com/?cat=5" >JavaScript</a> </li> <li class="cat-item cat-item-4"><a href="http://webammer.com/?cat=4" >links</a> </li> <li class="cat-item cat-item-7"><a href="http://webammer.com/?cat=7" >maven</a> </li> <li class="cat-item cat-item-11"><a href="http://webammer.com/?cat=11" >Oauth</a> </li> <li class="cat-item cat-item-14"><a href="http://webammer.com/?cat=14" >RSS Feed</a> </li> <li class="cat-item cat-item-9"><a href="http://webammer.com/?cat=9" >tomcat</a> </li> <li class="cat-item cat-item-6"><a href="http://webammer.com/?cat=6" >ubuntu</a> </li> <li class="cat-item cat-item-1"><a href="http://webammer.com/?cat=1" >Uncategorized</a> </li> <li class="cat-item cat-item-3"><a href="http://webammer.com/?cat=3" >web</a> </li> </ul> </aside> <aside id="recent-posts-3" class="widget widget_recent_entries"> <h3 class="widget-title">Recent Posts</h3> <ul> <li> <a href="http://webammer.com/?p=1025460">ReasonML: functions</a> </li> <li> <a href="http://webammer.com/?p=1025458">Note To Self: Adding Type Declaration Files To A TypeScript 2.6.2 Project</a> </li> <li> <a href="http://webammer.com/?p=1025456">Node.js Weekly Update – December 15</a> </li> <li> <a href="http://webammer.com/?p=1025454">The Best JavaScript and CSS Libraries for 2017</a> </li> <li> <a href="http://webammer.com/?p=1025452">Considering LocalStorage And The Browser Cookies As Integration Databases</a> </li> <li> <a href="http://webammer.com/?p=1025451">End of Year Giveaway 2017</a> </li> <li> <a href="http://webammer.com/?p=1025448">Building a Slack Bot with Modern Node.js Workflows</a> </li> <li> <a href="http://webammer.com/?p=1025446">Build a Secure To-Do App with Vue, ASP.NET Core, and Okta</a> </li> <li> <a href="http://webammer.com/?p=1025444">Pattern matching in ReasonML: destructuring, <code>switch</code>, <code>if</code> expressions</a> </li> <li> <a href="http://webammer.com/?p=1025442">Providing Services As Both A Multi-Collection And As An Individual Injectable In Angular 5.1.0</a> </li> <li> <a href="http://webammer.com/?p=1025440">Build a GraphQL Server With Apollo Server and AdonisJS</a> </li> <li> <a href="http://webammer.com/?p=1025438">ReasonML: <code>let</code> bindings and scopes</a> </li> <li> <a href="http://webammer.com/?p=1025436">Interactive Git Rebase: Moving White-Space Changes To An Isolated Commit</a> </li> <li> <a href="http://webammer.com/?p=1025434">Node.js Weekly Update – December 8</a> </li> <li> <a href="http://webammer.com/?p=1025432">ReasonML: basic values and types</a> </li> </ul> </aside> </div><!-- .widget-area --> </div><!-- .sidebar-inner --> </div><!-- #tertiary --> </div><!-- #main --> <footer id="colophon" class="site-footer" role="contentinfo"> <div class="site-info"> <a href="https://wordpress.org/">Proudly powered by WordPress</a> </div><!-- .site-info --> </footer><!-- #colophon --> </div><!-- #page --> <script type="text/javascript"> //<![CDATA[ !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"); window.___gcfg = {lang: "en"}; (function() { var po = document.createElement("script"); po.type = "text/javascript"; po.async = true; po.src = "https://apis.google.com/js/plusone.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(po, s); })(); //]]> </script> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script><script type='text/javascript' src='https://platform.linkedin.com/in.js?ver=4.8.4'></script> <script type='text/javascript' src='http://webammer.com/wp-content/themes/twentythirteen/js/functions.js?ver=20160717'></script> <script type='text/javascript' src='http://webammer.com/wp-includes/js/wp-embed.min.js?ver=4.8.4'></script> </body> </html>