Angular JS blog tutorial

43
ANGULARJS INTRODUCTION by @ Daniel Perez Claude Tech

description

Tutorial to create a blog using AngularJS. The slides were originally used for a study meetup at our office.

Transcript of Angular JS blog tutorial

Page 1: Angular JS blog tutorial

ANGULARJSINTRODUCTION

by @Daniel Perez Claude Tech

Page 2: Angular JS blog tutorial

LEAVESSTATIC WEBSITE BUILD TOOL

Depends only on NodeJS.

Uses:

Yeoman

Grunt

Bower

Jade (or EJS)

Stylus (or less or plain CSS)

Coffee (or plain JS)Install with:

$ npm install -g leaves$ leaves setup

Checkout for more info.the docs

Page 3: Angular JS blog tutorial

PROJECT CREATION$ leaves new angular-blog$ cd angular-blog$ leaves install jquery angular angular-resource bootstrap angular-ui-router markdown

Rename assets/js/app.coffee to assets/js/app.js,

erase assets/css/main.styl content and edit views/layout.jade.

// views/layout.jadehtml head ....

link(rel="stylesheet" href="components/bootstrap/dist/css/bootstrap.min.css") link(rel="stylesheet" href="components/bootstrap/dist/css/bootstrap-theme.min.css")

script(src="components/jquery/dist/jquery.min.js") script(src="components/angular/angular.min.js") script(src="components/angular-resource/angular-resource.min.js") script(src="components/angular-ui-router/release/angular-ui-router.min.js") script(src="components/bootstrap/dist/js/bootstrap.min.js")

script(src="js/app.js") body block content

Page 4: Angular JS blog tutorial

START HACKINGWhen you are done with basic setup, run

$ leaves

and start hacking.

FOR SCEPTICAL PEOPLEIf you do not want to use leaves, check about basic Angular setup.my blog post

Page 5: Angular JS blog tutorial

TRY ANGULARJSInitialize application

// views/layout.jadehtml head ... body(ng-app="") block content

Try two-way data binding:

Output variable value with: {{variable}}.

Change variable value with: ng-model="variable"// views/index.jadeextends ./layout.jade

block content input(ng-model="variable") span {{variable}}

Page 6: Angular JS blog tutorial

TRY ANGULARJSInitialize variable:

// views/index.jadeextends ./layout.jade

block content div(ng-init="variable = 'plain text'") span {{variable}}

You can use any element, not just div and span.

Page 7: Angular JS blog tutorial

TRY ANGULARJSUse controller to initialize variable.

// views/index.jadediv(ng-controller="TestCtrl") span {{variable}}

Define controller in JS file.

$scope is injected by Angular on instanciation.

// assets/js/app.jsfunction TestCtrl($scope) { $scope.variable = "my variable text";}

Angular uses to instanciate controllers, services, etc.DI

Page 8: Angular JS blog tutorial

TRY ANGULARJSReact to events:

// views/index.jadediv(ng-controller="TestCtrl") span {{variable}} button(ng-click="action()")

// assets/js/app.jsfunction TestCtrl($scope) { $scope.variable = "my variable text"; $scope.action = function () { $scope.variable = "I just clicked!"; };}

Page 9: Angular JS blog tutorial

CREATE BLOGCreate blog with following functionalities:

List posts

List posts by category

Show post

Create new postWe will use prepared API to work with.

Authentication/authorization will be for next time.

Sample is available at .angular-blog-sample.herokuapp.com

Full source code is available at .

github.com/claudetech-meetups/angular-blog-sample

Page 10: Angular JS blog tutorial

AVAILABLE APIThe available API calls are

GET /posts

GET /posts/:id

POST /posts

GET /categories

POST /categoriesAPI is available at: http://angular-tutorial-api.herokuapp.com/

Page 11: Angular JS blog tutorial

CREATE BASIC LAYOUTAdd header to views/layout.jade

html head .... body(ng-app="") .container nav.navbar.navbar-default .container-fluid .navbar-header a.navbar-brand(href="#") Blog .row .col-xs-8 block content .col-xs-4 .block.categories h3 Categories ul.list-unstyled li: a.small(href="#") Plenty of categories .block.admin h3 Admin ul.list-unstyled li: a.small(href="#") Add post

Page 12: Angular JS blog tutorial

CREATE BASIC LAYOUTCreate empty views/posts/index.jade and edit views/index.jade.

// views/index.jadeextends ./layout.jade

block content include ./posts/index

Page 13: Angular JS blog tutorial

BUILD POST LISTCreate controller in JS file:

function PostIndexCtrl($scope) { $scope.post = { id: 1, title: "Post title", content: "Post content", createdAt: new Date() };}

Wrap post list in a controller and use dummy data

.posts(ng-controller="PostIndexCtrl") .post.row .row .col-xs-12 h2 {{post.title}} small.date {{post.createdAt}} .row.content .col-xs-12 {{post.content}} .row .col-xs-12 a.small(href="#") See more

and create the controller.

Page 14: Angular JS blog tutorial

SOME ISSUES{{variable}} appears on page load

Date format is strange

No linebreak in content

Content may be very long

Page 15: Angular JS blog tutorial

SOME SOLUTIONSUse ng-bind instead of {{}}Use date filter

We will see how to render markdown in content later on

Use limitTo filter

.posts(ng-controller="PostIndexCtrl") .post.row .row .col-xs-12 h2(ng-bind="post.title") small.date(ng-bind="post.createAt|date:'y/M/d'") .row.content .col-xs-12(ng-bind="post.content|limitTo:100") .row .col-xs-12 a.small(href="#") See more

Page 16: Angular JS blog tutorial

MAKE IT A LISTAdd dummy data in the controller.

function PostIndexCtrl($scope) { $scope.posts = [{ id: 1, title: "Post title", content: "Post content", createdAt: new Date() }, { id: 2, title: "Post title 2", content: "Post content 2", createdAt: new Date() }];}

Use ng-repeat.posts(ng-controller="PostIndexCtrl") .post.row(ng-repeat="post in posts") ...

Page 17: Angular JS blog tutorial

SHOW SINGLE POSTCreate views/posts_show.jade, we will extend layout.jade for now.

extends ./layout.jade

block content .post.row(ng-controller="PostShowCtrl") .row .col-xs-12 h2 {{post.title}} small.date {{post.createdAt|date:'y/M/d'}} .row.content .col-xs-12 {{post.content}}

create PostShowCtrl function.

// assets/js/app.js....function PostShowCtrl($scope) { $scope.post = { id: 1, title: "Post title", content: "Post content", createdAt: new Date() };}

You can try to access your page: localhost:9000/posts_show.html

Page 18: Angular JS blog tutorial

SOME ISSUESBetter modularize controllers

We don't want another page

We want content in markdown

Page 19: Angular JS blog tutorial

MODULARIZATIONLet's start by splitting files.

// assets/js/controllers/posts/index.jsfunction PostIndexCtrl($scope) { .....}

// assets/js/controllers/posts/show.jsfunction PostShowCtrl($scope) { .....}

// views/layout.jadehtml head .... script(src="js/app.js") script(src="js/controllers/posts/index.js") script(src="js/controllers/posts/show.js") body(ng-app="") ...

Page 20: Angular JS blog tutorial

ANGULAR MODULESAngular has native modules. Declare BlogApp module with no depencies.

// assets/js/app.jsangular.module('BlogApp', []);

Declare controllers as part of the module.

// assets/js/controllers/posts/index.jsangular.module('BlogApp').controller('PostIndexCtrl', [ '$scope', function ($scope) { ... }]);

Same goes for PostShowCtrl.

Page 21: Angular JS blog tutorial

ROUTINGRoute in order to be able to have

/#/ -> all posts

/#/posts/:id -> post with id = :id/#/posts/new -> new post

First, add depency to the module.ui.router// assets/js/app.jsangular.module('BlogApp', [ 'ui.router']);

Page 22: Angular JS blog tutorial

SETUP VIEWFirst, add the view where to display the content of the route.

// views/index.jadeextends ./layout.jade

block content div(ui-view)

Page 23: Angular JS blog tutorial

SETUP ROUTERConfigure to redirect to / when no route found, and configure the routes.

// assets/js/app.js...

angular.module('BlogApp').config([ '$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/');

$stateProvider .state('index', { url: '/', templateUrl: 'posts/index.html' }) .state('show', { url: '/posts/:id', templateUrl: 'posts/show.html' }); }]);

Then, remove the extends and block content fromviews/posts_show.jade: we don't need the whole layout. For consistency,move views/post_show.jade to views/posts/show.jade.

You can now access your route: localhost:9000/#/posts/1

Page 24: Angular JS blog tutorial

SETUP ROUTERControllers should be defined in routes.

// assets/js/app.js .state('index', { url: '/', templateUrl: 'posts/index.html', controller: 'PostIndexCtrl' }) .state('show', { url: '/posts/:id', templateUrl: 'posts/show.html', controller: 'PostShowCtrl' });

and removed from views/posts/index.jade andviews/posts/show.jade.

// views/posts/show.jade.post.row // no ng-controller anymore ...

Page 25: Angular JS blog tutorial

ADD LINKS uses ui-sref to make link instead of normal href or ng-href

attributes.ui-router

// views/posts/index.jade... a.small(ui-sref="show({ id: post.id })") See more

Page 26: Angular JS blog tutorial

USE APIAdd ngResource module as a dependency.

// assets/js/app.jsangular.module('BlogApp', [ 'ui.router', 'ngResource']);...

Try it

// assets/js/controllers/posts/index.jsangular.module('BlogApp').controller('PostIndexCtrl', [ '$scope', '$resource', function ($scope, $resource) { // no need for dummy data anymore var Post = $resource('http://angular-tutorial-api.herokuapp.com/posts/:id'); Post.query(function (posts) { console.log(posts[0]); $scope.posts = posts; }); }]);

Page 27: Angular JS blog tutorial

RESOURCE FACTORYWe want to be able to use Post resource anywhere.

Let's make it a factory.

// assets/js/resources/post.jsangular.module('BlogApp').factory('Post', [ '$resource', function ($resource) { return $resource('http://angular-tutorial-api.herokuapp.com/posts/:id'); }]);

and include it in views/layout.jadehtml head ... script(src="js/resources/post.js")

Page 28: Angular JS blog tutorial

USE RESOURCE FACTORY// assets/js/controllers/index.jsangular.module('BlogApp').controller('PostIndexCtrl', [ '$scope', 'Post', function ($scope, Post) { Post.query(function (posts) { $scope.posts = posts; }); }]);

Page 29: Angular JS blog tutorial

FETCH SINGLE POSTUse $stateParams to get id, and get post from server.

// assets/js/controllers/posts/show.jsangular.module('BlogApp').controller('PostShowCtrl', [ '$scope', '$stateParams', 'Post', function ($scope, $stateParams, Post) { Post.get({id: $stateParams.id}, function (post) { $scope.post = post; }); }]);

Page 30: Angular JS blog tutorial

CREATE CATEGORY FACTORY// assets/js/resources/category.jsangular.module('BlogApp').factory('Category', [ '$resource', function ($resource) { return $resource('http://angular-tutorial-api.herokuapp.com/categories/:id'); }]);

and add it to layout.jade

Page 31: Angular JS blog tutorial

NEW POST CONTROLLERCreate new JS file, and a fresh post to bind.

// assets/js/controllers/posts/new.jsangular.module('BlogApp').controller('PostNewCtrl', [ '$scope', 'Post', function ($scope, Post) { $scope.post = new Post(); }]);

and include it in views/layout.jadescript(src="js/controllers/posts/new.js")

Page 32: Angular JS blog tutorial

CREATE NEW POST TEMPLATEBind the model created in the controller.

// views/posts/new.jadeh2 Create new post

form .form-group label(for="title") Title input#title.form-control(type="text" placeholder="Title" ng-model="post.title") .form-group label(for="content") Content textarea#content.form-control(rows="10" ng-model="post.content") .form-group select.form-control(ng-model="post.category_id") input.btn.btn-default(type="submit" value="Create")

Page 33: Angular JS blog tutorial

ADD A NEW ROUTECareful, order matters!

$stateProvider .state('index', { ..... }) .state('new', { url: '/posts/new', templateUrl: 'posts/new.html', controller: 'PostNewCtrl' }) .state('show', { ..... });

And set link

// views/layout.jade... .block.admin h3 Admin ul.list-unstyled li: a.small(ui-sref="new") Add post

Page 34: Angular JS blog tutorial

RESOLVE CATEGORIESBetter have categories before loading template. Use custom made promise andresolve API result.

// assets/js/app.js... .state('new', { url: '/posts/new', templateUrl: 'posts/new.html', controller: 'PostNewCtrl', resolve: { categories: ['$q', 'Category', function ($q, Category) { var deferred = $q.defer(); Category.query(function (categories) { deferred.resolve(categories); }); return deferred.promise; }] } }) .state('show', { ....

Page 35: Angular JS blog tutorial

RESOLVE CATEGORIESUse the categories resolved in the controller.

// assets/js/controllers/posts/new.js

angular.module('BlogApp').controller('PostNewCtrl', [ '$scope', 'Post', 'categories', function ($scope, Post, categories) { $scope.post = new Post(); $scope.categories = categories; }]);

Page 36: Angular JS blog tutorial

USE NG-OPTIONSDynamically show categories in select using ng-options.

form ... select.form-control( ng-model="post.category_id" ng-options="c.id as c.name for c in categories" )

Page 37: Angular JS blog tutorial

CREATE POSTReact to submit event

// views/posts/new.jade

form(ng-submit="createPost()")

Add handler for submit event.

// js/controllers/posts/new.jsangular.module('BlogApp').controller('PostNewCtrl', [ '$scope', 'Post', '$state', 'categories', function ($scope, Post, $state, categories) { ... $scope.createPost = function () { $scope.post.$save(function (post) { $state.go('index', {id: post.id}); }); }; ...

Page 38: Angular JS blog tutorial

FORMAT MARKDOWNWe are going to create a filter to format markdown.

// assets/js/filters/markdown.js

angular.module('BlogApp').filter('markdown', [ '$sce', function ($sce) { return function (input) { if (!input) { return ''; } return $sce.trustAsHtml(markdown.toHTML(input)); }; }]);

input is the value to convert to markdown. We return empty string if

undefined.

The markdown is formatted in HTML, we need to trust the input to tell Angular

it is safe.

Page 39: Angular JS blog tutorial

DISPLAY FORMATTEDMARKDOWN

We use ng-bind-html to display HTML and not escaped values.

// views/posts/show.jade.post.row .... .row.content .col-xs-12(ng-bind-html="post.content|markdown")

Page 40: Angular JS blog tutorial

CREATE CATEGORIESCONTROLLER

As for posts, query to get all the categories.

// assets/js/controllers/categories/index.jsangular.module('BlogApp').controller('CategoryIndexCtrl', [ '$scope', 'Category', function ($scope, Category) { Category.query(function (categories) { $scope.categories = categories; }); }]);

Page 41: Angular JS blog tutorial

SHOW CATEGORIESCreate partial

// views/categories/index.jade

.block.categories(ng-controller="CategoryIndexCtrl") h3 Categories ul.list-unstyled li(ng-repeat="category in categories") a.small(ui-sref="index({category: category.id})" ng-bind="category.name")

Change layout to use partial.

// views/layout.jade.... .col-xs-4 include ./categories/index .block.admin h3 Admin ul.list-unstyled li: a.small(ui-sref="new") Add post

Page 42: Angular JS blog tutorial

UPDATE ROUTESUpdate index routes to take category query parameter.

// assets/js/app.js...

$stateProvider .state('index', { url: '/?category', templateUrl: 'posts/index.html', controller: 'PostIndexCtrl' })

This is needed to pass category to ui-sref and to read it from$stateParams.

Page 43: Angular JS blog tutorial

FILTER QUERYCheck if there is a category defined and adapt query.

// assets/js/controllers/posts/index.js

angular.module('BlogApp').controller('PostIndexCtrl', [ '$scope', '$stateParams', 'Post', function ($scope, $stateParams, Post) { var search = $stateParams.category ? {category_id: $stateParams.category} : {}; Post.query(search, function (posts) { $scope.posts = posts; }); }]);