SlideShare una empresa de Scribd logo
1 de 167
Descargar para leer sin conexión
@markbates
@markbates
FLUENT2014
www.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-in
75kb	

(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")
AngularJS
App.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")
Ember
DS.RESTAdapter.reopen	
  
	
  	
  namespace:	
  'api/v1'	
  
	
  	
  
App.Store	
  =	
  DS.Store.extend	
  
	
  	
  revision:	
  14	
  
	
  	
  adapter:	
  DS.RESTAdapter.create()
AngularJS
App.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_i
d)
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
Ember
App.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

Más contenido relacionado

La actualidad más candente

Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuerydeimos
 
Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012Rafael Felix da Silva
 
Prototype & jQuery
Prototype & jQueryPrototype & jQuery
Prototype & jQueryRemy Sharp
 
AngularJS Compile Process
AngularJS Compile ProcessAngularJS Compile Process
AngularJS Compile ProcessEyal Vardi
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integraçãoVinícius Pretto da Silva
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceIvan Chepurnyi
 
Testing Web Applications with GEB
Testing Web Applications with GEBTesting Web Applications with GEB
Testing Web Applications with GEBHoward Lewis Ship
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On RailsJohn Wilker
 
jQuery from the very beginning
jQuery from the very beginningjQuery from the very beginning
jQuery from the very beginningAnis Ahmad
 
06 jQuery #burningkeyboards
06 jQuery  #burningkeyboards06 jQuery  #burningkeyboards
06 jQuery #burningkeyboardsDenis Ristic
 
ApacheCon NA11 - Apache Celix, Universal OSGi?
ApacheCon NA11 - Apache Celix, Universal OSGi?ApacheCon NA11 - Apache Celix, Universal OSGi?
ApacheCon NA11 - Apache Celix, Universal OSGi?abroekhuis
 
Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project WonderWO Community
 

La actualidad más candente (20)

Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
 
Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012
 
Prototype & jQuery
Prototype & jQueryPrototype & jQuery
Prototype & jQuery
 
AngularJS Compile Process
AngularJS Compile ProcessAngularJS Compile Process
AngularJS Compile Process
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 
Testing Web Applications with GEB
Testing Web Applications with GEBTesting Web Applications with GEB
Testing Web Applications with GEB
 
JavaScript JQUERY AJAX
JavaScript JQUERY AJAXJavaScript JQUERY AJAX
JavaScript JQUERY AJAX
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
jQuery from the very beginning
jQuery from the very beginningjQuery from the very beginning
jQuery from the very beginning
 
Workshop 6: Designer tools
Workshop 6: Designer toolsWorkshop 6: Designer tools
Workshop 6: Designer tools
 
Hacking Movable Type
Hacking Movable TypeHacking Movable Type
Hacking Movable Type
 
jQuery
jQueryjQuery
jQuery
 
Presentation
PresentationPresentation
Presentation
 
06 jQuery #burningkeyboards
06 jQuery  #burningkeyboards06 jQuery  #burningkeyboards
06 jQuery #burningkeyboards
 
ApacheCon NA11 - Apache Celix, Universal OSGi?
ApacheCon NA11 - Apache Celix, Universal OSGi?ApacheCon NA11 - Apache Celix, Universal OSGi?
ApacheCon NA11 - Apache Celix, Universal OSGi?
 
Jquery introduction
Jquery introductionJquery introduction
Jquery introduction
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
 
Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project Wonder
 

Destacado

A different thought angular js part-2
A different thought   angular js part-2A different thought   angular js part-2
A different thought angular js part-2Amit Thakkar
 
Angular js meetup
Angular js meetupAngular js meetup
Angular js meetupAnton Kropp
 
Design pattern in an expressive language java script
Design pattern in an expressive language java scriptDesign pattern in an expressive language java script
Design pattern in an expressive language java scriptAmit Thakkar
 
MongoDB - An Agile NoSQL Database
MongoDB - An Agile NoSQL DatabaseMongoDB - An Agile NoSQL Database
MongoDB - An Agile NoSQL DatabaseGaurav Awasthi
 
A different thought angular js part-3
A different thought   angular js part-3A different thought   angular js part-3
A different thought angular js part-3Amit Thakkar
 
A different thought AngularJS
A different thought AngularJSA different thought AngularJS
A different thought AngularJSAmit Thakkar
 
AngularJS Basics
AngularJS BasicsAngularJS Basics
AngularJS BasicsRavi Mone
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsRan Mizrahi
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsRavi Bhadauria
 
Practical AngularJS
Practical AngularJSPractical AngularJS
Practical AngularJSWei Ru
 
Building modular enterprise scale angular js applications
Building modular enterprise scale angular js applicationsBuilding modular enterprise scale angular js applications
Building modular enterprise scale angular js applicationsJonathan Fontanez
 
Angular js for enteprise application
Angular js for enteprise applicationAngular js for enteprise application
Angular js for enteprise applicationvu van quyet
 
Oldcastle Precast Spokane - Urban Modular Construction - The Grand Hotel
Oldcastle Precast Spokane - Urban Modular Construction - The Grand HotelOldcastle Precast Spokane - Urban Modular Construction - The Grand Hotel
Oldcastle Precast Spokane - Urban Modular Construction - The Grand HotelBlake Johnson
 

Destacado (16)

A different thought angular js part-2
A different thought   angular js part-2A different thought   angular js part-2
A different thought angular js part-2
 
Angular js meetup
Angular js meetupAngular js meetup
Angular js meetup
 
Design pattern in an expressive language java script
Design pattern in an expressive language java scriptDesign pattern in an expressive language java script
Design pattern in an expressive language java script
 
MongoDB - An Agile NoSQL Database
MongoDB - An Agile NoSQL DatabaseMongoDB - An Agile NoSQL Database
MongoDB - An Agile NoSQL Database
 
A different thought angular js part-3
A different thought   angular js part-3A different thought   angular js part-3
A different thought angular js part-3
 
Design a landing page
Design a landing pageDesign a landing page
Design a landing page
 
A different thought AngularJS
A different thought AngularJSA different thought AngularJS
A different thought AngularJS
 
AngularJS Basics
AngularJS BasicsAngularJS Basics
AngularJS Basics
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design Patterns
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjs
 
Practical AngularJS
Practical AngularJSPractical AngularJS
Practical AngularJS
 
076 Modular Construction
076 Modular Construction076 Modular Construction
076 Modular Construction
 
Benefits of developing single page web applications using angular js
Benefits of developing single page web applications using angular jsBenefits of developing single page web applications using angular js
Benefits of developing single page web applications using angular js
 
Building modular enterprise scale angular js applications
Building modular enterprise scale angular js applicationsBuilding modular enterprise scale angular js applications
Building modular enterprise scale angular js applications
 
Angular js for enteprise application
Angular js for enteprise applicationAngular js for enteprise application
Angular js for enteprise application
 
Oldcastle Precast Spokane - Urban Modular Construction - The Grand Hotel
Oldcastle Precast Spokane - Urban Modular Construction - The Grand HotelOldcastle Precast Spokane - Urban Modular Construction - The Grand Hotel
Oldcastle Precast Spokane - Urban Modular Construction - The Grand Hotel
 

Similar a Angular.js Fundamentals

AngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsAngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsMark
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Ruby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapMarcio Marinho
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiPivorak MeetUp
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.jsTechExeter
 
Spca2014 hillier 3rd party_javascript_libraries
Spca2014 hillier 3rd party_javascript_librariesSpca2014 hillier 3rd party_javascript_libraries
Spca2014 hillier 3rd party_javascript_librariesNCCOMMS
 
Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Joao Lucas Santana
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksHjörtur Hilmarsson
 
Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02Revath S Kumar
 
Single Page Web Apps with Backbone.js and Rails
Single Page Web Apps with Backbone.js and RailsSingle Page Web Apps with Backbone.js and Rails
Single Page Web Apps with Backbone.js and RailsPrateek Dayal
 
Working with Javascript in Rails
Working with Javascript in RailsWorking with Javascript in Rails
Working with Javascript in RailsSeungkyun Nam
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasminePaulo Ragonha
 
WebcampZG - Rails 4
WebcampZG - Rails 4WebcampZG - Rails 4
WebcampZG - Rails 4shnikola
 
Resource and view
Resource and viewResource and view
Resource and viewPapp Laszlo
 

Similar a Angular.js Fundamentals (20)

AngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsAngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.js
 
The Rails Way
The Rails WayThe Rails Way
The Rails Way
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Ruby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter Bootstrap
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy Koshovyi
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
 
Spca2014 hillier 3rd party_javascript_libraries
Spca2014 hillier 3rd party_javascript_librariesSpca2014 hillier 3rd party_javascript_libraries
Spca2014 hillier 3rd party_javascript_libraries
 
Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & Tricks
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02
 
Single Page Web Apps with Backbone.js and Rails
Single Page Web Apps with Backbone.js and RailsSingle Page Web Apps with Backbone.js and Rails
Single Page Web Apps with Backbone.js and Rails
 
Working with Javascript in Rails
Working with Javascript in RailsWorking with Javascript in Rails
Working with Javascript in Rails
 
Get AngularJS Started!
Get AngularJS Started!Get AngularJS Started!
Get AngularJS Started!
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
WebcampZG - Rails 4
WebcampZG - Rails 4WebcampZG - Rails 4
WebcampZG - Rails 4
 
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component DevelopmentEVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
 
Resource and view
Resource and viewResource and view
Resource and view
 
Basics of AngularJS
Basics of AngularJSBasics of AngularJS
Basics of AngularJS
 

Más de Mark

Go(lang) for the Rubyist
Go(lang) for the RubyistGo(lang) for the Rubyist
Go(lang) for the RubyistMark
 
Mangling Ruby with TracePoint
Mangling Ruby with TracePointMangling Ruby with TracePoint
Mangling Ruby with TracePointMark
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTestMark
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTestMark
 
GET /better
GET /betterGET /better
GET /betterMark
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScriptMark
 
Testing Your JavaScript & CoffeeScript
Testing Your JavaScript & CoffeeScriptTesting Your JavaScript & CoffeeScript
Testing Your JavaScript & CoffeeScriptMark
 
Building an API in Rails without Realizing It
Building an API in Rails without Realizing ItBuilding an API in Rails without Realizing It
Building an API in Rails without Realizing ItMark
 
5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(Mark
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the RubyistMark
 
RubyMotion
RubyMotionRubyMotion
RubyMotionMark
 
Testing JavaScript/CoffeeScript with Mocha and Chai
Testing JavaScript/CoffeeScript with Mocha and ChaiTesting JavaScript/CoffeeScript with Mocha and Chai
Testing JavaScript/CoffeeScript with Mocha and ChaiMark
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the RubyistMark
 
Testing Rich Client Side Apps with Jasmine
Testing Rich Client Side Apps with JasmineTesting Rich Client Side Apps with Jasmine
Testing Rich Client Side Apps with JasmineMark
 
DRb and Rinda
DRb and RindaDRb and Rinda
DRb and RindaMark
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Mark
 

Más de Mark (17)

Go(lang) for the Rubyist
Go(lang) for the RubyistGo(lang) for the Rubyist
Go(lang) for the Rubyist
 
Mangling Ruby with TracePoint
Mangling Ruby with TracePointMangling Ruby with TracePoint
Mangling Ruby with TracePoint
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTest
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTest
 
GET /better
GET /betterGET /better
GET /better
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
 
Testing Your JavaScript & CoffeeScript
Testing Your JavaScript & CoffeeScriptTesting Your JavaScript & CoffeeScript
Testing Your JavaScript & CoffeeScript
 
Building an API in Rails without Realizing It
Building an API in Rails without Realizing ItBuilding an API in Rails without Realizing It
Building an API in Rails without Realizing It
 
5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the Rubyist
 
RubyMotion
RubyMotionRubyMotion
RubyMotion
 
Testing JavaScript/CoffeeScript with Mocha and Chai
Testing JavaScript/CoffeeScript with Mocha and ChaiTesting JavaScript/CoffeeScript with Mocha and Chai
Testing JavaScript/CoffeeScript with Mocha and Chai
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the Rubyist
 
Testing Rich Client Side Apps with Jasmine
Testing Rich Client Side Apps with JasmineTesting Rich Client Side Apps with Jasmine
Testing Rich Client Side Apps with Jasmine
 
DRb and Rinda
DRb and RindaDRb and Rinda
DRb and Rinda
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010
 

Último

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????blackmambaettijean
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rick Flair
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 

Último (20)

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 

Angular.js Fundamentals

  • 3.
  • 4.
  • 8. Enough Angular to be Dangerous!
  • 11. • Controllers! • ngRoute! • Templates! • ngResource! • Directives! • Filters! • Scope! • Testing! • Code Organization! • Best Practices
  • 12. 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
  • 16. Features • Plain JavaScript • Data Binding • Routing/PushState • Testing • Templates/Directives/ Controllers • Modular • Dependency Injection • jqLite • Lightweight
  • 19. Backbone.js “minimal set of data-structure and view primitives for building web application with JavaScript”
  • 20. Ember “framework for creating ambitious web applications”
  • 21. AngularJS “Toolset for building the framework most suited to your application development”
  • 22.
  • 23.
  • 26. AngularJS Ember Backbone.js base 109kb 264kb 6.5kb templating language built-in 90kb (handlebars) ?? data adapter built-in 75kb (ember-data) 84kb (jQuery) support N/A 84kb (jQuery) 17kb (json2.js) 5.0kb (underscore.js ) 109kb 513kb 112.5kb
  • 29. 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
  • 31. Backbone.jsclass  App.Beer  extends   Backbone.Model       class  App.Beers  extends   Backbone.Collection   !  model:  Beer
  • 32. 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")
  • 35. 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
  • 36. 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")
  • 37. Ember DS.RESTAdapter.reopen      namespace:  'api/v1'       App.Store  =  DS.Store.extend      revision:  14      adapter:  DS.RESTAdapter.create()
  • 38. AngularJS App.factory  "Beer",  ($resource)  -­‐>      return  $resource  "/api/v1/ beers/:id",                                        {id:  "@id"},                                        {update:  {method:   "PUT"}}
  • 40. 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)
  • 41. Ember App.Router.map  -­‐>      @resource  "brewery",  {path:  "brewery/:brewery_id"}
  • 42. EmberApp.BreweryRoute  =   Ember.Route.extend      model:  (params)-­‐>           App.Brewery.find(params.brewery_i d)
  • 43. AngularJSApp.config  ($routeProvider)  -­‐>          $routeProvider          .when("/breweries/:id",  {              templateUrl:  "/assets/ brewery.html",              controller:  "BreweryController"          })          .when("/breweries/:id/edit",  {              templateUrl:  "/assets/ edit_brewery.html",              controller:   "EditBreweryController"          })
  • 45. 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
  • 46. Ember App.BreweryController  =  Ember.ObjectController.extend        save:  -­‐>          @store.commit()          #  further  code  omitted
  • 47. 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
  • 49. 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>
  • 50. 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>
  • 51. 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>
  • 52. <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
  • 53. <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
  • 54. <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
  • 55. <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
  • 56. 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>
  • 57. <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
  • 59. Backbone.js • Too simple • Not opinionated enough • “Memory” management • Unstructured • Spaghetti code • Lightweight • Not opinionated • Simple • Easy to read source • “widget” development Pros Cons
  • 60. 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
  • 61. 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
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 71. Directives <body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>! </body>
  • 72. Directives <body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>! </body>
  • 73. Directives <body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>! </body>
  • 74. Directives <body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>! </body>
  • 75. Directives <body ng-app>! <ng-view></ng-view>! <ul>! <li ng-repeat='comment in comments'>! {{comment.body}}! </li>! </ul>! </body>
  • 76. Demo
  • 77. Filters <ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>! </ul>
  • 78. Filters <ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>! </ul>
  • 79. Filters <ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>! </ul>
  • 80. <ul>! <li ng-repeat='c in comments | orderBy:"date"'>! {{c.author | uppercase}}! </li>! </ul> Filters
  • 81. Demo
  • 87. Demo
  • 93. Module Config Controller Factories Directives angular.module('app', []);
  • 94. Module Config Controller Factories Directives Filters angular.module('app', []);
  • 95. Module Config Controller Factories Directives Filters Routes angular.module('app', []);
  • 96. Demo
  • 98. 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
  • 99. 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
  • 100. 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
  • 101. 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
  • 102. 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
  • 103. 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
  • 104. 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
  • 105. 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
  • 106. 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
  • 107. 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
  • 108. 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
  • 109. Demo
  • 111. 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
  • 112. DirectivesApp.directive("upCaser", function() {! return {! link: function(scope, el, attrs) {! $(el).find('p').each(function(i, p) {! p = $(p);! p.text(p.text().toUpperCase());! });! }! };! });
  • 113. 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
  • 114. 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
  • 115. 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
  • 116. 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
  • 117. 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
  • 118. 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
  • 119. <div up-caser>! <p>some text</p>! <p>some other text</p>! </div> Directives
  • 121. 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
  • 123. 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
  • 124. Demo
  • 125. Events
  • 126. Scope
  • 128. 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
  • 129. 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
  • 130. 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
  • 131. 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
  • 132. 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
  • 133. 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
  • 134. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 135. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 136. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 137. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 138. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 139. App.controller('SomeController', function($scope) {! $scope.$on("up", function(data, message) {! $scope.up_message = message;! });! }); Events
  • 140. Demo
  • 141. App.controller('Main', function($scope) {! $scope.clicker = function() {! $scope.pressed = true;! };! }); Events
  • 142. App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! scope.$watch('pressed', function() {! if (scope.pressed) {! $(el).text('Oi!');! }! });! }! };! }); Events
  • 143. App.directive("alerter", function() {! return {! restrict: 'E',! link: function(scope, el, attrs) {! scope.$watch('pressed', function() {! if (scope.pressed) {! $(el).text('Oi!');! }! });! }! };! }); Events
  • 144. Demo
  • 146.
  • 147. App.controller('FooController', function($scope) {! ! $scope.setFoo = function(val) {! $scope.foo = val;! };! ! }); Testing
  • 148. 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
  • 149. 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
  • 150. 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
  • 151. 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
  • 152. 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
  • 153. 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
  • 154. 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
  • 155. 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
  • 156. 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
  • 157. 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
  • 158. 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
  • 159. Demo
  • 160. Part 2
  • 161. Setup!