Angular.js Fundamentals

Post on 08-May-2015

1135 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

As present at FluentConf 2014 on March 11th, 2014. AngularJS is one of the most popular, and powerful, JavaScript frameworks for building rich client-side applications. AngularJS is both simultaneously both simple to use and extremely full featured. With AngularJS a little goes a long way, but to make the most of it, you need to know what you’re doing. In this workshop we will build a complex application to help exercise all of the salient points of the AngularJS framework. Topics covered include, ngResource, directives, fitlers, routing, templates, controllers, testing, and more. Code can be found at: https://github.com/markbates/fluent-2014

Transcript

@markbates

@markbates

FLUENT2014www.metacasts.tv

www.angularmasterclass.com

Angular Fundamentals

Enough Angular to be Dangerous!

What Will Cover?

What Will Cover?**hopefully

• Controllers!

• ngRoute!

• Templates!

• ngResource!

• Directives!

• Filters!

• Scope!

• Testing!

• Code Organization!

• Best Practices

Part 1• Features/Why

Angular?!

• Getting Started/Setting Up!

• Directives, Filters, and Data Binding!

• Controllers, Templates, and Scope!

• Modules!

• Routing!

• Custom Directives and Event Handling!

• Testing

Part 2

We Code!

Part 1

Features

Features• Plain JavaScript

• Data Binding

• Routing/PushState

• Testing

• Templates/Directives/Controllers

• Modular

• Dependency Injection

• jqLite

• Lightweight

Why Angular?

Philosophies

Backbone.js“minimal set of data-structure and view primitives

for building web application with JavaScript”

Ember“framework for creating ambitious web applications”

AngularJS“Toolset for building the framework

most suited to your application development”

Weight

“production” versions (minified) w/ required dependencies

AngularJS Ember Backbone.js

base 109kb 264kb 6.5kb

templating language built-in

90kb (handlebars) ??

data adapter built-in75kb

(ember-data)84kb

(jQuery)

support N/A 84kb (jQuery)

17kb (json2.js)

5.0kb (underscore.js

)

109kb 513kb 112.5kb

Mindshare

Google

AngularJS Backbone.js Ember

Watchers 2,155 1,442 824

Stars 21,408 17,291 9,570

Forks 6,670 3,783 2,044

Github

“Basic” Models

Backbone.jsclass  App.Beer  extends  Backbone.Model      class  App.Beers  extends  Backbone.Collection  !  model:  Beer

EmberApp.Beer  =  DS.Model.extend      title:  DS.attr("string")      abv:  DS.attr("number")      country_id:  DS.attr("number")      brewery_id:  DS.attr("number")      brewery:  DS.belongsTo("App.Brewery")      country:  DS.belongsTo("App.Country")

AngularJSApp.Beer  =  {}

“Remote” Models

Backbone.jsclass  App.Beer  extends  Backbone.Model      urlRoot:  "/api/v1/beers"        class  App.Beers  extends  Backbone.Collection          url:  -­‐>          if  @brewery_id?              return  "/api/v1/breweries/#{@brewery_id}/beers"          else              return  "/api/v1/beers"          model:  Beer

EmberApp.Beer  =  DS.Model.extend      title:  DS.attr("string")      abv:  DS.attr("number")      country_id:  DS.attr("number")      brewery_id:  DS.attr("number")      brewery:  DS.belongsTo("App.Brewery")      country:  DS.belongsTo("App.Country")

EmberDS.RESTAdapter.reopen      namespace:  'api/v1'      App.Store  =  DS.Store.extend      revision:  14      adapter:  DS.RESTAdapter.create()

AngularJSApp.factory  "Beer",  ($resource)  -­‐>      return  $resource  "/api/v1/beers/:id",                                        {id:  "@id"},                                        {update:  {method:  "PUT"}}

Routers

Backbone.js@Router  =  Backbone.Router.extend          initialize:  -­‐>          @countries  =  new  Countries()          routes:          "breweries/:brewery_id":  "brewery"          "breweries/:brewery_id/edit":  "breweryEdit"          brewery:  (brewery_id)  -­‐>          @changeView(new  BreweryView(collection:  @countries,  model:  new  Brewery(id:  brewery_id)))          breweryEdit:  (brewery_id)  -­‐>          @changeView(new  BreweryEditView(collection:  @countries,  model:  new  Brewery(id:  brewery_id)))          changeView:  (view)  =>          @currentView?.remove()          @currentView  =  view          $("#outlet").html(@currentView.el)

Ember

App.Router.map  -­‐>      @resource  "brewery",  {path:  "brewery/:brewery_id"}

EmberApp.BreweryRoute  =  Ember.Route.extend      model:  (params)-­‐>          App.Brewery.find(params.brewery_id)

AngularJSApp.config  ($routeProvider)  -­‐>          $routeProvider          .when("/breweries/:id",  {              templateUrl:  "/assets/brewery.html",              controller:  "BreweryController"          })          .when("/breweries/:id/edit",  {              templateUrl:  "/assets/edit_brewery.html",              controller:  "EditBreweryController"          })

Controllers/Views

Backbone.jsclass  @BreweryEditView  extends  Backbone.View          template:  "brewery_edit"          events:          "click  #save-­‐button":  "saveClicked"          "keypress  #brewery-­‐title":  "titleEdited"          initialize:  -­‐>          super          @countriesView  =  new  CountriesView(collection:  @collection)          @$el.html(@countriesView.el)          @model.on  "change",  @render          @model.fetch()          render:  =>          @$("#country-­‐outlet").html(@renderTemplate())          return  @

   saveClicked:  (e)  =>          e?.preventDefault()          attrs  =              title:  @$("#brewery-­‐title").val()              synonyms:  @$("#brewery-­‐synonyms").val()              address:  @$("#brewery-­‐address").val()          @model.save  attrs,              success:  (model,  response,  options)  =>                  App.navigate("/breweries/#{@model.id}",  trigger:  true)              error:  (model,  xhr,  options)  -­‐>                  errors  =  []                  for  key,  value  of  xhr.responseJSON.errors                      errors.push  "#{key}:  #{value.join(",  ")}"                  alert  errors.join("\n")          titleEdited:  (e)  =>          title  =  @$("#brewery-­‐title").val()          @$("h2").text(title)  !    #  further  code  omitted

EmberApp.BreweryController  =  Ember.ObjectController.extend        save:  -­‐>          @store.commit()          #  further  code  omitted

AngularJS@EditBreweryController  =  ($scope,  $routeParams,  $location,  Brewery)  -­‐>          $scope.brewery  =  Brewery.get(id:  $routeParams.id)          $scope.save  =  -­‐>          success  =  -­‐>              $location.path("/breweries/#{$routeParams.id}")              $scope.errors  =  null          failure  =  (object)-­‐>              $scope.errors  =  object.data.errors          $scope.brewery.$update  {},  success,  failure

Templates

Backbone.js<h2><%=  @model.displayName()  %></h2>      <form>          <div  class="control-­‐group">          <label  class="control-­‐label"  for="title">Title</label>          <div  class="controls">              <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("title")  %>'id='brewery-­‐title'>          </div>      </div>          <div  class="control-­‐group">          <label  class="control-­‐label"  for="synonyms">Synonyms</label>          <div  class="controls">              <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("synonyms")  %>'id='brewery-­‐synonyms'>          </div>      </div>          <div  class="control-­‐group">          <label  class="control-­‐label"  for="address">Address</label>          <div  class="controls">              <textarea  class='input-­‐xxlarge'  id='brewery-­‐address'><%=  @model.get("address")  %></textarea>          </div>      </div>          <button  class='btn  btn-­‐primary'  id='save-­‐button'>Save</button>      <a  href="/breweries/<%=  @model.id  %>"  class='btn'>Cancel</a>      </form>

Backbone.js<h2><%=  @model.displayName()  %></h2>  

   

<form>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="title">Title</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("title")  %>'id='brewery-­‐title'>          </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("synonyms")  %>'id='brewery-­‐synonyms'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="address">Address</label>  

       <div  class="controls">  

           <textarea  class='input-­‐xxlarge'  id='brewery-­‐address'><%=  @model.get("address")  %></textarea>  

       </div>  

   </div>  

   

   <button  class='btn  btn-­‐primary'  id='save-­‐button'>Save</button>  

   <a  href="/breweries/<%=  @model.id  %>"  class='btn'>Cancel</a>  

   

</form>

Backbone.js<h2><%=  @model.displayName()  %></h2>  

   

<form>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="title">Title</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("title")  %>'id='brewery-­‐title'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  value='<%=  @model.get("synonyms")  %>'id='brewery-­‐synonyms'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="address">Address</label>  

       <div  class="controls">  

           <textarea  class='input-­‐xxlarge'  id='brewery-­‐address'><%=  @model.get("address")  %></textarea>          </div>  

   </div>  

   

   <button  class='btn  btn-­‐primary'  id='save-­‐button'>Save</button>  

   <a  href="/breweries/<%=  @model.id  %>"  class='btn'>Cancel</a>  

   

</form>

<div  class='span12'>  

   <h2>{{displayName}}</h2>  

   <h3>  

       {{cityState}}  

       {{#linkTo  "country"  country}}  

           {{country.title}}  

       {{/linkTo}}  

   </h3>  

   {{#if  isEditing}}  

       <form>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="title">Title</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="title"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="synonyms"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextArea  valueBinding="address"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <button  class='btn  btn-­‐primary'  {{action  "save"}}>Save</button>  

   

       </form>  

   {{  else  }}  

       {{  partial  "brewery/show"  }}  

   {{/if}}  

</div>

Ember

<div  class='span12'>  

   <h2>{{displayName}}</h2>  

   <h3>  

       {{cityState}}  

       {{#linkTo  "country"  country}}  

           {{country.title}}  

       {{/linkTo}}  

   </h3>  

   {{#if  isEditing}}  

       <form>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="title">Title</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="title"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="synonyms"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextArea  valueBinding="address"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <button  class='btn  btn-­‐primary'  {{action  "save"}}>Save</button>  

   

       </form>  

   {{  else  }}  

       {{  partial  "brewery/show"  }}  

   {{/if}}  

</div>

Ember

<div  class='span12'>  

   <h2>{{displayName}}</h2>  

   <h3>  

       {{cityState}}  

       {{#linkTo  "country"  country}}  

           {{country.title}}  

       {{/linkTo}}  

   </h3>  

   {{#if  isEditing}}  

       <form>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="title">Title</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="title"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextField  valueBinding="synonyms"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

   

           <div  class="control-­‐group">  

               <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

               <div  class="controls">  

                   {{view  Ember.TextArea  valueBinding="address"  class='input-­‐xxlarge'}}  

               </div>  

           </div>  

           <button  class='btn  btn-­‐primary'  {{action  "save"}}>Save</button>  

       </form>  

   {{  else  }}  

       {{  partial  "brewery/show"  }}  

   {{/if}}  

</div>

Ember

<form>  

   <h3>{{brewery.title}}</h3>  

   <div  ng-­‐include='"/assets/_errors.html"'></div>  

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="title">Title</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐

model='brewery.title'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐

model='brewery.synonyms'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="address">Address</label>  

       <div  class="controls">  

           <textarea  class='input-­‐xxlarge'  ng-­‐model='brewery.address'></

textarea>  

       </div>  

   </div>  

   

   <button  class='btn  btn-­‐primary'  ng-­‐click='save()'>Save</button>  

   

</form>

AngularJS

AngularJS<form>  

   <h3>{{brewery.title}}</h3>  

   <div  ng-­‐include='"/assets/_errors.html"'></div>  

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="title">Title</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐model='brewery.title'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐model='brewery.synonyms'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="address">Address</label>  

       <div  class="controls">  

           <textarea  class='input-­‐xxlarge'  ng-­‐model='brewery.address'></textarea>  

       </div>  

   </div>  

   

   <button  class='btn  btn-­‐primary'  ng-­‐click='save()'>Save</button>  

   

</form>

<form>  

   <h3>{{brewery.title}}</h3>  

   <div  ng-­‐include='"/assets/_errors.html"'></div>  

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="title">Title</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐model='brewery.title'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="synonyms">Synonyms</label>  

       <div  class="controls">  

           <input  type='text'  class='input-­‐xxlarge'  ng-­‐model='brewery.synonyms'>  

       </div>  

   </div>  

   

   <div  class="control-­‐group">  

       <label  class="control-­‐label"  for="address">Address</label>  

       <div  class="controls">  

           <textarea  class='input-­‐xxlarge'  ng-­‐model='brewery.address'></textarea>  

       </div>  

   </div>  

   <button  class='btn  btn-­‐primary'  ng-­‐click='save()'>Save</button>  

 </form>

AngularJS

Pros/Cons

Backbone.js• Too simple

• Not opinionated enough

• “Memory” management

• Unstructured

• Spaghetti code

• Lightweight

• Not opinionated

• Simple

• Easy to read source

• “widget” development

Pros Cons

Ember• Too complex

• Overly opinionated

• Heavyweight

• ember-data - not production ready (very buggy)

• Little to no mind-share outside of Rails

• Difficult to read source code

• Structured

• Highly opinionated

• “less” code

• “large” apps

Pros Cons

AngularJS• Difficult to read source

code

• jQuery plugins require custom directives

• Large apps requiring self-imposed structure

• Lightly structured

• Lightly opinionated

• “less” code

• Plain JavaScript

• Simple/Powerful

• Easy to test

• Lightweight

• small, medium, or large apps

Pros Cons

Getting Started

5 Minute Break

Directives, Filters, and Data Binding

Directives<body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>!</body>

Directives<body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>!</body>

Directives<body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>!</body>

Directives<body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>!</body>

Directives<body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>!</body>

Demo

Filters<ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>!</ul>

Filters<ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>!</ul>

Filters<ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>!</ul>

<ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>!</ul>

Filters

Demo

Controllers, Templates, and Scope

Controllers, Templates and Scope

Template

Controllers, Templates and Scope

Template Controller

Controllers, Templates and Scope

Template Controller

Controllers, Templates and Scope

Template Controller

Demo

Modules

Module

angular.module('app', []);

Module

Config

angular.module('app', []);

Module

Config Controller

angular.module('app', []);

Module

Config Controller Factories

angular.module('app', []);

Module

Config Controller Factories Directives

angular.module('app', []);

Module

Config Controller Factories Directives Filters

angular.module('app', []);

Module

Config Controller Factories Directives Filters

Routes

angular.module('app', []);

Demo

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

App.config(function($routeProvider, $locationProvider) {! $locationProvider.html5Mode(true);!! $routeProvider.when('/posts', {! controller: 'PostsIndexController',! templateUrl: 'posts/index.html'! })! .when('/posts/:id',{! controller: 'PostsShowController',! templateUrl: 'posts/show.html'! })! .otherwise({! redirectTo: '/posts'! });!});

Routing

Demo

Custom Directives and Event Handling

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

DirectivesApp.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

App.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

<div up-caser>! <p>some text</p>! <p>some other text</p>!</div>

Directives

Directives<up-caser>! <p>some text</p>! <p>some other text</p>!</up-caser>

App.directive("upCaser", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

Directives<up-caser>! <p>some text</p>! <p>some other text</p>!</up-caser>

App.directive("upCaser", function() {! return {! restrict: 'AEC',! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };!});

Directives

Demo

Events

Scope

Events

$broadcast

$emit

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! setTimeout(function() {! scope.$emit("up", "Hello Up!");! scope.$broadcast("down", "Hello Down!");! scope.$apply();! }, 3000);! }! };!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });!});

Events

Demo

App.controller('Main', function($scope) {! $scope.clicker = function() {! $scope.pressed = true;! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! scope.$watch('pressed', function() {! if (scope.pressed) {! $(el).text('Oi!');! }! });! }! };!});

Events

App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! scope.$watch('pressed', function() {! if (scope.pressed) {! $(el).text('Oi!');! }! });! }! };!});

Events

Demo

Testing

App.controller('FooController', function($scope) {!! $scope.setFoo = function(val) {! $scope.foo = val;! };!!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

describe('FooController', function() {!! beforeEach(function() {module('app')});!! beforeEach(inject(function($rootScope, $controller) {! this.scope = $rootScope.$new();! $controller('FooController', {$scope: this.scope})! }));!! describe('setFoo()', function() {!! it('sets the foo value', function() {! expect(this.scope.foo).not.toBeDefined();! this.scope.setFoo('Bar');! expect(this.scope.foo).toEqual('Bar');! });! });!});

Testing

Demo

Part 2

Setup!

Base Project!github.com/markbates/

fluent-2014

Node.js!http://nodejs.org

Lineman.js!npm install -g

lineman@0.27.2

Install Modules!npm install

Code Time!!

Thanks! @markbates

www.angularmasterclass.com

top related