Tag Archives: es6

Angular 2 Tutorial – movies App

Last time I wrote a blog created the MovieAPI and now I am using that API as back end services to create an angular 2 app.

the final source code could find at my github repository.  here is the link download

For the MovieAPI open it, build it will install the packages automatically, then run the service first.

After the service is done, go to the angular2 folder, open command window

–run npm install,

— npm start

It will look like this:

home

create.PNG

added

Step 1 : Setting up your angular 2 application, I already have a blog  click here to found it. Or also could download from the source file from https://github.com/angular/quickstart/blob/master/README.md but it add a lot of unnecessary setting, probably confuse for beginning learner.

It was better do few times manually setting it up to get understand all of the necessary for start anguar 2 application.

Step 2:  In angualr 2 everything is component, for me normally like to create the service first. I just create a file named movies.service.ts under folder movies


import {Injectable} from 'angular2/core';
import {IMovie} from './movie';
import {Http, RequestOptions , Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';

@Injectable() 
export class MovieService {   
    private _movieUrl = 'http://localhost:53871/api/movies';

    constructor(private _http: Http) { }

    getMovies(): Observable<IMovie[]> {
        return this._http.get(this._movieUrl)
            .map((response: Response) => <IMovie[]>response.json()) //arrow function, tranceform response to json object and is array of IMovie[]
            .do(data => console.log("All: " + JSON.stringify(data))) //for debug
            .catch(this.handleError);
    }


    createMovie(movie: IMovie): Observable {
        let body = JSON.stringify(movie); 
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this._http.post(this._movieUrl, body, options)               
                .map(this.extractData)
                .catch(this.handleError);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body.data || {};
    }

    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

In there MovieService I have a IMovie here is my movie.ts I put it in the same folder with MovieService .


//interface won't compile to js file, it just for typescript do type checking
 export interface IMovie { 
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];

}


export class Movie implements IMovie {
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];
}

Step 3: Inject the services.
Go to the app.component.ts this is our container component, for anguar 2 only need inject once, then all of the nest container could using it ( by import it ).

Inject a service has two steps:

1) import the file into the app.component,

2) Inject it as providers

here is my app.component.ts

angular2_injection

Step 4: Create the movies-list.component.ts


//for using component, simply import it into this file
import {Component, OnInit} from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import {IMovie} from './movie';
import {MovieService} from './movies.service';
import {MovieFilterPipe} from './movie-filter.pipe';
import { StarComponent } from '../shared/star.component';

@Component({
    selector: 'sz-movies',
    templateUrl: 'app/movies/movies-list.component.html',
    styleUrls: ['app/movies/movies-list.component.css'],  
    pipes: [MovieFilterPipe],
    directives: [StarComponent, ROUTER_DIRECTIVES]
   
})
export class MovieListComponent implements OnInit {
    pageTitle: string = 'Movie List';   
   
    listFilter: string;
    errorMessage: string;
    movies: IMovie[];


    constructor(private _movieService: MovieService) {

    }
    
    ngOnInit(): void {
        this._movieService.getMovies()
            .subscribe(
            movies => this.movies = movies,
            error => this.errorMessage = error);
    }

}

here is the movies-list.component.htmlmovies_html

 

for this component I have a nested StarComponent.

Setp 5: create nested StarComponent, here is the star.component.ts under the share folder


import { Component, OnInit, Input} from 'angular2/core';
@Component({
    selector: 'sz-star',
    templateUrl: 'app/shared/star.component.html',
    styleUrls: ['app/shared/star.component.css']
})
export class StarComponent implements OnInit {
    @Input() rating: number;
    starWidth: number;
    ngOnInit(): void {
        this.starWidth = this.rating * 86 / 5;
    }
}

star.component.htmlstar.PNG

css:

star_css

In angular 2: data flow is unidirectional, for passing data from view to the class

first: you passing data into the directive could bind it to the property

passing_data_down.PNG

Second: need import the Input from angular2/core, and bind it to the property

inData

Now, one nested movies-list.component and nested star.componet down.

Now using the same steps create the MovieCreateComponent


import {Component, OnInit} from 'angular2/core';

import {IMovie, Movie} from '../movie';
import {Router} from 'angular2/router';
import {MovieService} from '../movies.service';

@Component({
    templateUrl: 'app/movies/create/movie-create.component.html'
})

export class MovieCreateComponent implements OnInit {
    title: string = 'Create New Movie';
    newMovie: IMovie;

    constructor(
        private _moviesService: MovieService,
        private _router: Router
    ) { }

    ngOnInit() {
        this.newMovie = new Movie;
    }

    createMovie(event) {
        let _this = this;

        this._moviesService.createMovie(this.newMovie)
            .subscribe(function () {
                _this._router.navigate(['Movies']);
            });
    }

    cancelCreate(event) {
        this._router.navigate(['Movies']);
    }

}angular2_form.PNG
Router:
route.PNG

In the index.html page, put the baseurl:
baseUrl.png

Done, have fun!

React & ES6 project dev enviroment setup

In this day, the JavaScript project set up it was a big complex, and this tutorial will show you how to set up the environment for build react SPA working with ES6.

First, create package.json file, here is the common using packages I was included, and I will use npm start to run the project instead of gulp. so the packages has included the scripts part with in it.


{
  "name": "react-Demo",
  "version": "1.0.0",
  "description": "react kit for React and es6 demo project",
  "scripts": {
    "prestart":"babel-node tools/startMessage.js",
    "start":"npm-run-all --parallel test:watch open:src lint:watch",
    "open:src": "babel-node tools/srcServer.js",
    "lint": "node_modules/.bin/esw webpack.config.* src tools",
    "lint:watch": "npm run lint -- --watch",
    "test": "mocha --reporter progress tools/testSetup.js \"src/**/*.spec.js\"",
    "test:watch": "npm run test -- --watch"
  },
  "author": "Sandy Zhang",
  "license": "MIT",
  "dependencies": {
    "babel-polyfill": "6.8.0",
    "bootstrap": "3.3.6",
    "jquery": "2.2.3",
    "react": "15.0.2",
    "react-dom": "15.0.2",
    "react-redux": "4.4.5",
    "react-router": "2.4.0",
    "react-router-redux": "4.0.4",
    "redux": "3.5.2",
    "redux-thunk": "2.0.1",
    "toastr": "2.1.2"
  },
  "devDependencies": {
    "babel-cli": "6.8.0",
    "babel-core": "6.8.0",
    "babel-loader": "6.2.4",
    "babel-plugin-react-display-name": "2.0.0",
    "babel-preset-es2015": "6.6.0",
    "babel-preset-react": "6.5.0",
    "babel-preset-react-hmre": "1.1.1",
    "babel-register": "6.8.0",
    "colors": "1.1.2",
    "compression": "1.6.1",
    "cross-env": "1.0.7",
    "css-loader": "0.23.1",
    "enzyme": "2.2.0",
    "eslint": "2.9.0",
    "eslint-plugin-import": "1.6.1",
    "eslint-plugin-react": "5.0.1",
    "eslint-watch": "2.1.11",
    "eventsource-polyfill": "0.9.6",
    "expect": "1.19.0",
    "express": "4.13.4",
    "extract-text-webpack-plugin": "1.0.1",
    "file-loader": "0.8.5",
    "jsdom": "8.5.0",
    "mocha": "2.4.5",
    "nock": "8.0.0",
    "npm-run-all": "1.8.0",
    "open": "0.0.5",
    "react-addons-test-utils": "15.0.2",
    "redux-immutable-state-invariant": "1.2.3",
    "redux-mock-store": "1.0.2",
    "rimraf": "2.5.2",
    "style-loader": "0.13.1",
    "url-loader": "0.5.7",
    "webpack": "1.13.0",
    "webpack-dev-middleware": "1.6.1",
    "webpack-hot-middleware": "2.10.0"
  } 
}

step 2: setup the dev server with webpack, and for the developing tools all put it inside the tools folder.

  • in the root project create the webpack.config.dev.js, there are the standards

import webpack from 'webpack';
import path from 'path';

export default {
  debug: true,
  devtool: 'cheap-module-eval-source-map',
  noInfo: false,
  entry: [
    'eventsource-polyfill', // necessary for hot reloading with IE
    'webpack-hot-middleware/client?reload=true', //note that it reloads the page if hot module reloading fails.
    './src/index'
  ],
  target: 'web',
  output: {
    path: __dirname + '/dist', // Note: Physical files are only output by the production build task `npm run build`.
    publicPath: '/',
    filename: 'bundle.js' 
  },
  devServer: {
    contentBase: './src' //the folder put the source files
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),//for the hot load
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [
      {test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel']},
      {test: /(\.css)$/, loaders: ['style', 'css']},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file"},
      {test: /\.(woff|woff2)$/, loader: "url?prefix=font/&limit=5000"},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream"},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml"}
    ]
  }
};

        

step 3: create a file named srcServer.js inside the tools, npm scripts already has the command will run to open the dev server.

 
import express from 'express';
import webpack from 'webpack';
import path from 'path';
import config from '../webpack.config.dev';
import open from 'open';

/* eslint-disable no-console */

const port = 3000;
const app = express();
const compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));

app.get('*', function(req, res) {
  res.sendFile(path.join( __dirname, '../src/index.html'));
});

app.listen(port, function(err) {
  if (err) {
    console.log(err);
  } else {
    open(`http://localhost:${port}`);
  }
});

step 4: for people like to set up testing driver developer, create a testSetp.js inside the tools folder:


process.env.NODE_ENV = 'test';

require('babel-register')();

require.extensions['.css'] = function () {return null;};
require.extensions['.png'] = function () {return null;};
require.extensions['.jpg'] = function () {return null;};

var jsdom = require('jsdom').jsdom;

var exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

global.navigator = {
  userAgent: 'node.js'
};

documentRef = document; 

in the script I have

  "test": "mocha --reporter progress tools/testSetup.js \"src/**/*.spec.js\"",

will run with it.

step 5: setup the .babelrc in the root project

{
  "presets": ["react", "es2015"],
  "env": {
    "development": {
      "presets": ["react-hmre"]
    }
  }
}

step 6 : setup .eslintrc inside the root project


{
  "extends": [
    "eslint:recommended",
    "plugin:import/errors",
    "plugin:import/warnings"
  ],
  "plugins": [
    "react"
  ],
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "env": {
    "es6": true,
    "browser": true,
    "node": true,
    "jquery": true,
    "mocha": true
  },
  "rules": {
    "quotes": 0,
    "no-console": 1,
    "no-debugger": 1,
    "no-var": 1,
    "semi": [1, "always"],
    "no-trailing-spaces": 0,
    "eol-last": 0,
    "no-unused-vars": 0,
    "no-underscore-dangle": 0,
    "no-alert": 0,
    "no-lone-blocks": 0,
    "jsx-quotes": 1,
    "react/display-name": [ 1, {"ignoreTranspilerName": false }],
    "react/forbid-prop-types": [1, {"forbid": ["any"]}],
    "react/jsx-boolean-value": 1,
    "react/jsx-closing-bracket-location": 0,
    "react/jsx-curly-spacing": 1,
    "react/jsx-indent-props": 0,
    "react/jsx-key": 1,
    "react/jsx-max-props-per-line": 0,
    "react/jsx-no-bind": 1,
    "react/jsx-no-duplicate-props": 1,
    "react/jsx-no-literals": 0,
    "react/jsx-no-undef": 1,
    "react/jsx-pascal-case": 1,
    "react/jsx-sort-prop-types": 0,
    "react/jsx-sort-props": 0,
    "react/jsx-uses-react": 1,
    "react/jsx-uses-vars": 1,
    "react/no-danger": 1,
    "react/no-did-mount-set-state": 1,
    "react/no-did-update-set-state": 1,
    "react/no-direct-mutation-state": 1,
    "react/no-multi-comp": 1,
    "react/no-set-state": 0,
    "react/no-unknown-property": 1,
    "react/prefer-es6-class": 1,
    "react/prop-types": 1,
    "react/react-in-jsx-scope": 1,
    "react/require-extension": 1,
    "react/self-closing-comp": 1,
    "react/sort-comp": 1,
    "react/wrap-multilines": 1
  }
}

there are already have script command could run check the lint


 "lint": "node_modules/.bin/esw webpack.config.* src tools",

Now could start create react components
> create index.html inside the src folder

<!DOCTYPE html>
<html lang=”en”>
<head>
<title>Pluralsight Admin</title>
</head>
<body>

/bundle.js
</body>
</html>

create index.js file


/*eslint-disable import/default */
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import './styles/styles.css'; //Webpack can import CSS files too!
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';


render( 
    ,
  document.getElementById('app')
);

create route.js inside the src folder


import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
import HomePage from './components/home/HomePage';
import AboutPage from './components/about/AboutPage';


export default (
  
    
    
  
);

create components folder inside the src, then create App.js inside it


// This component handles the App template used on every page.
import React, {PropTypes} from 'react';


class App extends React.Component {
  render() {
    return (

heading

{this.props.children}

); } } App.propTypes = { children: PropTypes.object.isRequired }; export default App;

separate pages by folder,create home, about folder

for HomePage.js


import React from 'react';
import {Link} from 'react-router';

class HomePage extends React.Component {
  render() {
    return (

React Demo

React, Redux and React Router in ES6 for ultra-responsive web apps.

Learn more

); } } export default HomePage;

for AboutPage.js


import React from 'react';

class AboutPage extends React.Component {
  render() {
    return (

About

This application uses React, Redux, React Router and a variety of other helpful libraries.

 

); } } export default AboutPage;

source code here:

https://github.com/sandywebdesigner/react_es6_starter_demo

now run npm start it will open the page in the brower, and you could start code now. have fun!

the default page will look like this:

react_starter