SlideShare uma empresa Scribd logo
1 de 69
Javascript Frameworks
for Well Architected, Immersive Web Apps




                  Daniel Nelson
          Centresource Interactive Agency
A description of the problem
    and what we are seeking in a solution
An example unpacked
Traditional MVC Web App
Traditional MVC Web App


          Model
Traditional MVC Web App


          Model


         Controller
Traditional MVC Web App


          Model


         Controller


           View
Traditional MVC Web App


             Model
Server


            Controller


              View
Traditional MVC Web App

          Model


         Controller


           View


Server
Traditional MVC Web App

          Model

         Controller

           View

Server
Traditional MVC Web App

          Model

         Controller

           View

Server
                      My Web App
                      HTML
Traditional MVC Web App

          Model

         Controller

           View

Server
                       My Web App
                      HTTP
Traditional MVC Web App

          Model

         Controller

           View

Server
                       My Web App
                      HTML
Without the Refresh

          Model

         Controller

           View

Server
Without the Refresh

          Model

         Controller

           View

Server
                       My Web App
                      HTML+JS
Without the Refresh

          Model

         Controller

           View

Server
                       My Web App
                      XHR
Without the Refresh

          Model

         Controller

           View

Server
                       My Web App
                      JSON
How do we render the response?
Partials


<h1>My Web App</h1>

<div class="blurbs">
  <%= render :partial => "blurb",

</div>
             :collection => @blurbs %>        My Web App
<div class="blob">
  <%= render "blob" %>
</div>
Partials


<h1>My Web App</h1>

<div class="blurbs">
  <%= render :partial => "blurb",

</div>
             :collection => @blurbs %>        My Web App
<div class="blob">
  <%= render "blob" %>
</div>
Partials



json_response = {
  :html => render(:partial => "blurb",
                  :collection => @blurbs),
                                              My Web App
  :other_info => "blah"
}
Sending HTML in the JSON

          Model

         Controller

           View

Server
                       My Web App
                      XHR
Sending HTML in the JSON

          Model

         Controller

           View

Server
                       JSON
                      My Web App
                        with
                       HTML
Is this a good solution?
Degrades gracefully
Easy to test
It works
It works
(up to a point)
A More Accurate Picture

            Model       Browser
          Controller
                             View Logic
          View Logic

Server
                        My Web App
                       XHR
What happens when the front
 end of the application becomes
as sophisticated as the back end?
What Happened to our MVC?
                       Browser
                             JS Model
           Model
                           JS Controller
         Controller
                            View Logic
         View Logic

Server
                       My Web App
                      XHR
A better way
Decouple
Two Applications


Server
                                 Browser
   Model                               Model
                       JSON
                REST




 Controller                           Controller
                 API




                          JSON

                                           View
How do we achieve this?
Rails + Javascript Framework
Rails + Javascript Framework
 • AngularJS

 • Backbone.js

 • Batman

 • ExtJS/ExtDirect

 • Javascript   MVC
 • Knockout.js

 • Spine

 • SproutCore
What are we looking for?
What are we looking for?
in general
• documentation     & community
• testability

• ability   to organize code
• opinionated
What are we looking for?
in general
• documentation     & community
• testability

• ability   to organize code
• opinionated
What are we looking for?
in general                       in particular
• documentation     & community • decouple GUI from
                                  implementation logic
• testability
                                 • persisting   data abstracts XHR
• ability   to organize code
                                 • sensible   routing (for deep
• opinionated                      linking)
                                 • compatible with other tools
                                   (such as jQuery)
AngularJS
Documentation & community
        http://angularjs.org
Testability
AngularJS comes with testing built in
• Jasmine   & “e2e”
• every    step of the angularjs.org tutorial shows how to test

Fits naturally into the Rails testing ecosystem
• Jasmine   for unit specs
• RSpec    (or Cucumber) + Capybara for integration specs
• easier   in Rails than Angular alone
Organization & Opinionation
  will become apparent as we explore the code
A demo app
               Rails 3.1 + AngularJS


http://github.com/centresource/angularjs_rails_demo
Everything dynamic
 class ApplicationController < ActionController::Base
protect_from_forgery

before_filter :intercept_html_requests

layout nil

private

def intercept_html_requests
  render('layouts/dynamic') if request.format == Mime::HTML
end

def handle_unverified_request
  reset_session
  render "#{Rails.root}/public/500.html", :status => 500, :layout => nil
end
views / layouts / dynamic.html.erb

<!doctype html>
<html xmlns:ng="http://angularjs.org/">
<head>
  <meta charset="utf-8">
  <title>Angular Rails Demo</title>
  <%= stylesheet_link_tag "application" %>
  <%= csrf_meta_tag %>
</head>
<body ng:controller="PhotoGalleryCtrl">

 <ng:view></ng:view>

  <script src="/assets/angular.min.js" ng:autobind></script>
  <%= javascript_include_tag "application" -%>
</body>
</html>
Routes

/* app/assets/javascripts/controllers.js.erb */

$route.when('/photographers',
    {template: '<%= asset_path("photographers.html") %>', controller: PhotographersCtrl});

$route.when('/photographers/:photographer_id/galleries',
    {template: '<%= asset_path("galleries.html") %>', controller: GalleriesCtrl});

$route.when('/photographers/:photographer_id/galleries/:gallery_id/photos',
    {template: '<%= asset_path("photos.html") %>', controller: PhotosCtrl});

$route.otherwise({redirectTo: '/photographers'});

$route.onChange(function() {
  this.params = $route.current.params;
});
AngularJS controller


/* app/assets/javascripts/controllers.js.erb */

function GalleriesCtrl(Galleries, Photographers) {
  this.photographer = Photographers.get({ photographer_id: this.params.photographer_id });
  this.galleries = Galleries.index({ photographer_id: this.params.photographer_id });
}
Data binding


/* app/assets/templates/photographers.html */

<h1>Galleries of {{photographer.name}}</h1>

<ul id="galleries">
  <li class="gallery" ng:repeat="gallery in galleries">
    <a href="#/photographers/{{photographer.id}}/galleries/{{gallery.id}}/
photos">{{gallery.title}}</a>
  </li>
</ul>
Resources
/* app/assets/javascripts/services.js */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
isArray: true }});
});

angular.service('Photos', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
                                                              'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
Resources
/* app/assets/javascripts/services.js.erb */
                                      */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
isArray: true }});
});

angular.service('Photos', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
                                                              'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
Resources
/* app/assets/javascripts/services.js.erb */
                                      */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});        <%= photographers_path(':photographer_id') %>

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
isArray: true }});
});

angular.service('Photos', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
                                                              'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
Resources
/* app/assets/javascripts/services.js.erb */
                                      */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});        <%= photographers_path(':photographer_id') %>

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
                   <%= photographers_galleries_path(':photographer_id', ':gallery_id') %>
isArray: true }});
});

angular.service('Photos', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
                                                              'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
Resources
/* app/assets/javascripts/services.js.erb */
                                      */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});        <%= photographers_path(':photographer_id') %>

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
                   <%= photographers_galleries_path(':photographer_id', ':gallery_id') %>
isArray: true }});
});

angular.service('Photos', function($resource) {
                   <%= photographers_galleries_photos_path(':photographer_id', ':gallery_id') %>
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
                                                              'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
Resources
/* app/assets/javascripts/services.js.erb */
                                      */

angular.service('Photographers', function($resource) {
 return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }});
});        <%= photographers_path(':photographer_id') %>

angular.service('Galleries', function($resource) {
 return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET',
                   <%= photographers_galleries_path(':photographer_id', ':gallery_id') %>
isArray: true }});
});

angular.service('Photos', function($resource) {
                   <%= photographers_galleries_photos_path(':photographer_id', ':gallery_id') %>
 return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method:
'GET', isArray: true }});
});

angular.service('SelectedPhotos', function($resource) {
 return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' },
         <%= selected_photos_path(':selected_photo_id') %>    'index': { method: 'GET', isArray: true },
                                                              'update': { method: 'PUT' },
                                                              'destroy': { method: 'DELETE' }});
});
/* app/assets/javascripts/controllers.js.erb */
function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) {
  var self = this;

  self.photographer = Photographers.get({ photographer_id: this.params.photographer_id });
  self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.selected_photos = SelectedPhotos.index();



    self.selectPhoto = function(photo) {
      var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } });
      selected_photo.$create(function() {
        self.selected_photos.push(selected_photo);
      });
    }

    self.deleteSelectedPhoto = function(selected_photo) {
      angular.Array.remove(self.selected_photos, selected_photo);
      selected_photo.$destroy({ selected_photo_id: selected_photo.id });
    }

    self.saveSelectedPhoto = function(selected_photo) {
      selected_photo.$update({ selected_photo_id: selected_photo.id });
      $('input').blur();
    }
}
/* app/assets/javascripts/controllers.js.erb */
function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) {
  var self = this;

  self.photographer = Photographers.get({ photographer_id: this.params.photographer_id });
  self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.selected_photos = SelectedPhotos.index();



    self.selectPhoto = function(photo) {
      var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } });
      selected_photo.$create(function() {
        self.selected_photos.push(selected_photo);
      });
    }

    self.deleteSelectedPhoto = function(selected_photo) {
      angular.Array.remove(self.selected_photos, selected_photo);
      selected_photo.$destroy({ selected_photo_id: selected_photo.id });
    }

    self.saveSelectedPhoto = function(selected_photo) {
      selected_photo.$update({ selected_photo_id: selected_photo.id });
      $('input').blur();
    }
}
/* app/assets/javascripts/controllers.js.erb */
function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) {
  var self = this;

  self.photographer = Photographers.get({ photographer_id: this.params.photographer_id });
  self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id:
this.params.gallery_id });
  self.selected_photos = SelectedPhotos.index();



    self.selectPhoto = function(photo) {
      var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } });
      selected_photo.$create(function() {
        self.selected_photos.push(selected_photo);
      });
    }

    self.deleteSelectedPhoto = function(selected_photo) {
      angular.Array.remove(self.selected_photos, selected_photo);
      selected_photo.$destroy({ selected_photo_id: selected_photo.id });
    }

    self.saveSelectedPhoto = function(selected_photo) {
      selected_photo.$update({ selected_photo_id: selected_photo.id });
      $('input').blur();
    }
}
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
Two way data binding


<form ng:submit="saveSelectedPhoto(selected_photo)">
  <input ng:model="selected_photo.title" />
</form>




self.saveSelectedPhoto = function(selected_photo) {
   selected_photo.$update({ selected_photo_id: selected_photo.id });
   $('input').blur();
 }
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
/* app/assets/templates/photos.html */
<h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1>

<div id="outer_picture_frame">
  <div id="picture_frame">
    <div id="prev">&lsaquo;</div>
    <div id="photos" my:cycle>
      <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)"
ng:repeat="photo in photos">
        <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" />
        <span class="title">{{photo.title}}</span>
      </div>
    </div>
    <div id="next">&rsaquo;</div>
    <span class="caption">Click a photo to add it to your collection</span>
  </div>
</div>

<div id="selected_frame">
  <div id="selected_photos">
    <div class="selected_photo" ng:repeat="selected_photo in selected_photos">
      <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" />
      <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span>
      <form ng:submit="saveSelectedPhoto(selected_photo)">
        <input ng:model="selected_photo.title" />
      </form>
    </div>
  </div>
</div>
/* app/assets/javascripts/widgets.js */

angular.directive("my:cycle", function(expr,el){
   return function(container){
        var scope = this;
        var lastChildID = container.children().last().attr('id');

          var doIt = function() {
              var lastID = container.children().last().attr('id');
              if (lastID != lastChildID) {
                  lastChildID = lastID;
                  $(container).cycle({ fx: 'fade',
                                        speed: 500,
                                        timeout: 3000,
                                        pause: 1,
                                        next: '#next',
                                        prev: '#prev'});
              }
          }

          var defer = this.$service("$defer");
          scope.$onEval( function() {
              defer(doIt);
          });
      }
});
Thank You
http://github.com/centresource/angularjs_rails_demo



                    @bluejade

Mais conteúdo relacionado

Mais procurados

amis-adf-enterprise-mobility
amis-adf-enterprise-mobilityamis-adf-enterprise-mobility
amis-adf-enterprise-mobilityLuc Bors
 
243329387 angular-docs
243329387 angular-docs243329387 angular-docs
243329387 angular-docsAbhi166803
 
Beginners' guide to Ruby on Rails
Beginners' guide to Ruby on RailsBeginners' guide to Ruby on Rails
Beginners' guide to Ruby on RailsVictor Porof
 
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces Skills Matter
 
Implicit objects advance Java
Implicit objects advance JavaImplicit objects advance Java
Implicit objects advance JavaDarshit Metaliya
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSVisual Engineering
 
AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
AtlasCamp 2010: Understanding the Atlassian Platform - Tim PettersenAtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
AtlasCamp 2010: Understanding the Atlassian Platform - Tim PettersenAtlassian
 
Medium TechTalk — iOS
Medium TechTalk — iOSMedium TechTalk — iOS
Medium TechTalk — iOSjimmyatmedium
 
Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Elyse Kolker Gordon
 
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Community
 
AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI colleenfry
 
Intro to emberjs
Intro to emberjsIntro to emberjs
Intro to emberjsMandy Pao
 
Angular vs React for Web Application Development
Angular vs React for Web Application DevelopmentAngular vs React for Web Application Development
Angular vs React for Web Application DevelopmentFITC
 
Go Fullstack: JSF for Public Sites (CONFESS 2013)
Go Fullstack: JSF for Public Sites (CONFESS 2013)Go Fullstack: JSF for Public Sites (CONFESS 2013)
Go Fullstack: JSF for Public Sites (CONFESS 2013)Michael Kurz
 
StrongLoop Node.js API Security & Customization
StrongLoop Node.js API Security & CustomizationStrongLoop Node.js API Security & Customization
StrongLoop Node.js API Security & Customizationjguerrero999
 
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...David Kaneda
 

Mais procurados (20)

amis-adf-enterprise-mobility
amis-adf-enterprise-mobilityamis-adf-enterprise-mobility
amis-adf-enterprise-mobility
 
243329387 angular-docs
243329387 angular-docs243329387 angular-docs
243329387 angular-docs
 
Beginners' guide to Ruby on Rails
Beginners' guide to Ruby on RailsBeginners' guide to Ruby on Rails
Beginners' guide to Ruby on Rails
 
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
 
Implicit objects advance Java
Implicit objects advance JavaImplicit objects advance Java
Implicit objects advance Java
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJS
 
Mean stack Magics
Mean stack MagicsMean stack Magics
Mean stack Magics
 
AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
AtlasCamp 2010: Understanding the Atlassian Platform - Tim PettersenAtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
 
Medium TechTalk — iOS
Medium TechTalk — iOSMedium TechTalk — iOS
Medium TechTalk — iOS
 
Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017
 
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
 
AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI
 
Intro to emberjs
Intro to emberjsIntro to emberjs
Intro to emberjs
 
Angular vs React for Web Application Development
Angular vs React for Web Application DevelopmentAngular vs React for Web Application Development
Angular vs React for Web Application Development
 
Go Fullstack: JSF for Public Sites (CONFESS 2013)
Go Fullstack: JSF for Public Sites (CONFESS 2013)Go Fullstack: JSF for Public Sites (CONFESS 2013)
Go Fullstack: JSF for Public Sites (CONFESS 2013)
 
StrongLoop Node.js API Security & Customization
StrongLoop Node.js API Security & CustomizationStrongLoop Node.js API Security & Customization
StrongLoop Node.js API Security & Customization
 
ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...
How to Develop a Rich, Native-quality User Experience for Mobile Using Web St...
 
Wt unit 5 client &amp; server side framework
Wt unit 5 client &amp; server side frameworkWt unit 5 client &amp; server side framework
Wt unit 5 client &amp; server side framework
 
AspNetWhitePaper
AspNetWhitePaperAspNetWhitePaper
AspNetWhitePaper
 

Semelhante a Javascript Frameworks for Well Architected, Immersive Web Apps

Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationAndrew Rota
 
Create an application with ember
Create an application with ember Create an application with ember
Create an application with ember Chandra Sekar
 
Understanding angular js
Understanding angular jsUnderstanding angular js
Understanding angular jsAayush Shrestha
 
ASP.NET MVC as the next step in web development
ASP.NET MVC as the next step in web developmentASP.NET MVC as the next step in web development
ASP.NET MVC as the next step in web developmentVolodymyr Voytyshyn
 
Intoduction to Angularjs
Intoduction to AngularjsIntoduction to Angularjs
Intoduction to AngularjsGaurav Agrawal
 
Python Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - AppenginePython Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - AppenginePython Ireland
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 
Introduction to ASP.NET MVC
Introduction to ASP.NET MVC Introduction to ASP.NET MVC
Introduction to ASP.NET MVC Joe Wilson
 
MVC Demystified: Essence of Ruby on Rails
MVC Demystified: Essence of Ruby on RailsMVC Demystified: Essence of Ruby on Rails
MVC Demystified: Essence of Ruby on Railscodeinmotion
 
AngularJs Superheroic JavaScript MVW Framework Services by Miracle Studios
AngularJs Superheroic JavaScript MVW Framework Services by Miracle StudiosAngularJs Superheroic JavaScript MVW Framework Services by Miracle Studios
AngularJs Superheroic JavaScript MVW Framework Services by Miracle StudiosLearnimtactics
 

Semelhante a Javascript Frameworks for Well Architected, Immersive Web Apps (20)

Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP Application
 
Create an application with ember
Create an application with ember Create an application with ember
Create an application with ember
 
Understanding angular js
Understanding angular jsUnderstanding angular js
Understanding angular js
 
ASP.NET MVC as the next step in web development
ASP.NET MVC as the next step in web developmentASP.NET MVC as the next step in web development
ASP.NET MVC as the next step in web development
 
Angular beans
Angular beansAngular beans
Angular beans
 
Intoduction to Angularjs
Intoduction to AngularjsIntoduction to Angularjs
Intoduction to Angularjs
 
Ng-init
Ng-init Ng-init
Ng-init
 
Ng-init
Ng-init Ng-init
Ng-init
 
Python Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - AppenginePython Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - Appengine
 
Angular js
Angular jsAngular js
Angular js
 
Angular js
Angular jsAngular js
Angular js
 
AngularJS By Vipin
AngularJS By VipinAngularJS By Vipin
AngularJS By Vipin
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Basics of AngularJS
Basics of AngularJSBasics of AngularJS
Basics of AngularJS
 
Introduction to ASP.NET MVC
Introduction to ASP.NET MVC Introduction to ASP.NET MVC
Introduction to ASP.NET MVC
 
Mvc3 crash
Mvc3 crashMvc3 crash
Mvc3 crash
 
MVC Demystified: Essence of Ruby on Rails
MVC Demystified: Essence of Ruby on RailsMVC Demystified: Essence of Ruby on Rails
MVC Demystified: Essence of Ruby on Rails
 
Angular js anupama
Angular js anupamaAngular js anupama
Angular js anupama
 
AngularJs Superheroic JavaScript MVW Framework Services by Miracle Studios
AngularJs Superheroic JavaScript MVW Framework Services by Miracle StudiosAngularJs Superheroic JavaScript MVW Framework Services by Miracle Studios
AngularJs Superheroic JavaScript MVW Framework Services by Miracle Studios
 
Angular - Beginner
Angular - BeginnerAngular - Beginner
Angular - Beginner
 

Último

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 

Último (20)

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 

Javascript Frameworks for Well Architected, Immersive Web Apps

  • 1. Javascript Frameworks for Well Architected, Immersive Web Apps Daniel Nelson Centresource Interactive Agency
  • 2. A description of the problem and what we are seeking in a solution
  • 6. Traditional MVC Web App Model Controller
  • 7. Traditional MVC Web App Model Controller View
  • 8. Traditional MVC Web App Model Server Controller View
  • 9. Traditional MVC Web App Model Controller View Server
  • 10. Traditional MVC Web App Model Controller View Server
  • 11. Traditional MVC Web App Model Controller View Server My Web App HTML
  • 12. Traditional MVC Web App Model Controller View Server My Web App HTTP
  • 13. Traditional MVC Web App Model Controller View Server My Web App HTML
  • 14. Without the Refresh Model Controller View Server
  • 15. Without the Refresh Model Controller View Server My Web App HTML+JS
  • 16. Without the Refresh Model Controller View Server My Web App XHR
  • 17. Without the Refresh Model Controller View Server My Web App JSON
  • 18. How do we render the response?
  • 19. Partials <h1>My Web App</h1> <div class="blurbs"> <%= render :partial => "blurb", </div> :collection => @blurbs %> My Web App <div class="blob"> <%= render "blob" %> </div>
  • 20. Partials <h1>My Web App</h1> <div class="blurbs"> <%= render :partial => "blurb", </div> :collection => @blurbs %> My Web App <div class="blob"> <%= render "blob" %> </div>
  • 21. Partials json_response = { :html => render(:partial => "blurb", :collection => @blurbs), My Web App :other_info => "blah" }
  • 22. Sending HTML in the JSON Model Controller View Server My Web App XHR
  • 23. Sending HTML in the JSON Model Controller View Server JSON My Web App with HTML
  • 24. Is this a good solution?
  • 28. It works (up to a point)
  • 29. A More Accurate Picture Model Browser Controller View Logic View Logic Server My Web App XHR
  • 30. What happens when the front end of the application becomes as sophisticated as the back end?
  • 31. What Happened to our MVC? Browser JS Model Model JS Controller Controller View Logic View Logic Server My Web App XHR
  • 34. Two Applications Server Browser Model Model JSON REST Controller Controller API JSON View
  • 35. How do we achieve this?
  • 36. Rails + Javascript Framework
  • 37. Rails + Javascript Framework • AngularJS • Backbone.js • Batman • ExtJS/ExtDirect • Javascript MVC • Knockout.js • Spine • SproutCore
  • 38. What are we looking for?
  • 39. What are we looking for? in general • documentation & community • testability • ability to organize code • opinionated
  • 40. What are we looking for? in general • documentation & community • testability • ability to organize code • opinionated
  • 41. What are we looking for? in general in particular • documentation & community • decouple GUI from implementation logic • testability • persisting data abstracts XHR • ability to organize code • sensible routing (for deep • opinionated linking) • compatible with other tools (such as jQuery)
  • 43. Documentation & community http://angularjs.org
  • 44. Testability AngularJS comes with testing built in • Jasmine & “e2e” • every step of the angularjs.org tutorial shows how to test Fits naturally into the Rails testing ecosystem • Jasmine for unit specs • RSpec (or Cucumber) + Capybara for integration specs • easier in Rails than Angular alone
  • 45. Organization & Opinionation will become apparent as we explore the code
  • 46. A demo app Rails 3.1 + AngularJS http://github.com/centresource/angularjs_rails_demo
  • 47. Everything dynamic class ApplicationController < ActionController::Base protect_from_forgery before_filter :intercept_html_requests layout nil private def intercept_html_requests render('layouts/dynamic') if request.format == Mime::HTML end def handle_unverified_request reset_session render "#{Rails.root}/public/500.html", :status => 500, :layout => nil end
  • 48. views / layouts / dynamic.html.erb <!doctype html> <html xmlns:ng="http://angularjs.org/"> <head> <meta charset="utf-8"> <title>Angular Rails Demo</title> <%= stylesheet_link_tag "application" %> <%= csrf_meta_tag %> </head> <body ng:controller="PhotoGalleryCtrl"> <ng:view></ng:view> <script src="/assets/angular.min.js" ng:autobind></script> <%= javascript_include_tag "application" -%> </body> </html>
  • 49. Routes /* app/assets/javascripts/controllers.js.erb */ $route.when('/photographers', {template: '<%= asset_path("photographers.html") %>', controller: PhotographersCtrl}); $route.when('/photographers/:photographer_id/galleries', {template: '<%= asset_path("galleries.html") %>', controller: GalleriesCtrl}); $route.when('/photographers/:photographer_id/galleries/:gallery_id/photos', {template: '<%= asset_path("photos.html") %>', controller: PhotosCtrl}); $route.otherwise({redirectTo: '/photographers'}); $route.onChange(function() { this.params = $route.current.params; });
  • 50. AngularJS controller /* app/assets/javascripts/controllers.js.erb */ function GalleriesCtrl(Galleries, Photographers) { this.photographer = Photographers.get({ photographer_id: this.params.photographer_id }); this.galleries = Galleries.index({ photographer_id: this.params.photographer_id }); }
  • 51. Data binding /* app/assets/templates/photographers.html */ <h1>Galleries of {{photographer.name}}</h1> <ul id="galleries"> <li class="gallery" ng:repeat="gallery in galleries"> <a href="#/photographers/{{photographer.id}}/galleries/{{gallery.id}}/ photos">{{gallery.title}}</a> </li> </ul>
  • 52. Resources /* app/assets/javascripts/services.js */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('Photos', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 53. Resources /* app/assets/javascripts/services.js.erb */ */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('Photos', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 54. Resources /* app/assets/javascripts/services.js.erb */ */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); <%= photographers_path(':photographer_id') %> angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('Photos', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 55. Resources /* app/assets/javascripts/services.js.erb */ */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); <%= photographers_path(':photographer_id') %> angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', <%= photographers_galleries_path(':photographer_id', ':gallery_id') %> isArray: true }}); }); angular.service('Photos', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 56. Resources /* app/assets/javascripts/services.js.erb */ */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); <%= photographers_path(':photographer_id') %> angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', <%= photographers_galleries_path(':photographer_id', ':gallery_id') %> isArray: true }}); }); angular.service('Photos', function($resource) { <%= photographers_galleries_photos_path(':photographer_id', ':gallery_id') %> return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 57. Resources /* app/assets/javascripts/services.js.erb */ */ angular.service('Photographers', function($resource) { return $resource('photographers/:photographer_id', {}, { 'index': { method: 'GET', isArray: true }}); }); <%= photographers_path(':photographer_id') %> angular.service('Galleries', function($resource) { return $resource('photographers/:photographer_id/galleries/:gallery_id', {}, { 'index': { method: 'GET', <%= photographers_galleries_path(':photographer_id', ':gallery_id') %> isArray: true }}); }); angular.service('Photos', function($resource) { <%= photographers_galleries_photos_path(':photographer_id', ':gallery_id') %> return $resource('photographers/:photographer_id/galleries/:gallery_id/photos', {}, { 'index': { method: 'GET', isArray: true }}); }); angular.service('SelectedPhotos', function($resource) { return $resource('selected_photos/:selected_photo_id', {}, { 'create': { method: 'POST' }, <%= selected_photos_path(':selected_photo_id') %> 'index': { method: 'GET', isArray: true }, 'update': { method: 'PUT' }, 'destroy': { method: 'DELETE' }}); });
  • 58. /* app/assets/javascripts/controllers.js.erb */ function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) { var self = this; self.photographer = Photographers.get({ photographer_id: this.params.photographer_id }); self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.selected_photos = SelectedPhotos.index(); self.selectPhoto = function(photo) { var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } }); selected_photo.$create(function() { self.selected_photos.push(selected_photo); }); } self.deleteSelectedPhoto = function(selected_photo) { angular.Array.remove(self.selected_photos, selected_photo); selected_photo.$destroy({ selected_photo_id: selected_photo.id }); } self.saveSelectedPhoto = function(selected_photo) { selected_photo.$update({ selected_photo_id: selected_photo.id }); $('input').blur(); } }
  • 59. /* app/assets/javascripts/controllers.js.erb */ function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) { var self = this; self.photographer = Photographers.get({ photographer_id: this.params.photographer_id }); self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.selected_photos = SelectedPhotos.index(); self.selectPhoto = function(photo) { var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } }); selected_photo.$create(function() { self.selected_photos.push(selected_photo); }); } self.deleteSelectedPhoto = function(selected_photo) { angular.Array.remove(self.selected_photos, selected_photo); selected_photo.$destroy({ selected_photo_id: selected_photo.id }); } self.saveSelectedPhoto = function(selected_photo) { selected_photo.$update({ selected_photo_id: selected_photo.id }); $('input').blur(); } }
  • 60. /* app/assets/javascripts/controllers.js.erb */ function PhotosCtrl(Photos, Galleries, Photographers, SelectedPhotos) { var self = this; self.photographer = Photographers.get({ photographer_id: this.params.photographer_id }); self.gallery = Galleries.get({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.photos = Photos.index({ photographer_id: this.params.photographer_id, gallery_id: this.params.gallery_id }); self.selected_photos = SelectedPhotos.index(); self.selectPhoto = function(photo) { var selected_photo = new SelectedPhotos({ selected_photo: { photo_id: photo.id } }); selected_photo.$create(function() { self.selected_photos.push(selected_photo); }); } self.deleteSelectedPhoto = function(selected_photo) { angular.Array.remove(self.selected_photos, selected_photo); selected_photo.$destroy({ selected_photo_id: selected_photo.id }); } self.saveSelectedPhoto = function(selected_photo) { selected_photo.$update({ selected_photo_id: selected_photo.id }); $('input').blur(); } }
  • 61. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 62. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 63. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 64. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 65. Two way data binding <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> self.saveSelectedPhoto = function(selected_photo) { selected_photo.$update({ selected_photo_id: selected_photo.id }); $('input').blur(); }
  • 66. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 67. /* app/assets/templates/photos.html */ <h1>The {{gallery.title}} Gallery of {{photographer.name}}</h1> <div id="outer_picture_frame"> <div id="picture_frame"> <div id="prev">&lsaquo;</div> <div id="photos" my:cycle> <div class="photo" id="photo_{{photo.id}}" ng:click="selectPhoto(photo)" ng:repeat="photo in photos"> <img ng:src="{{photo.image_large_url}}" alt="{{photo.title}}" /> <span class="title">{{photo.title}}</span> </div> </div> <div id="next">&rsaquo;</div> <span class="caption">Click a photo to add it to your collection</span> </div> </div> <div id="selected_frame"> <div id="selected_photos"> <div class="selected_photo" ng:repeat="selected_photo in selected_photos"> <img ng:src="{{selected_photo.image_gallery_url}}" alt="{{selected_photo.title}}" /> <span class="delete" ng:click="deleteSelectedPhoto(selected_photo)">✕</span> <form ng:submit="saveSelectedPhoto(selected_photo)"> <input ng:model="selected_photo.title" /> </form> </div> </div> </div>
  • 68. /* app/assets/javascripts/widgets.js */ angular.directive("my:cycle", function(expr,el){ return function(container){ var scope = this; var lastChildID = container.children().last().attr('id'); var doIt = function() { var lastID = container.children().last().attr('id'); if (lastID != lastChildID) { lastChildID = lastID; $(container).cycle({ fx: 'fade', speed: 500, timeout: 3000, pause: 1, next: '#next', prev: '#prev'}); } } var defer = this.$service("$defer"); scope.$onEval( function() { defer(doIt); }); } });

Notas do Editor

  1. \n
  2. \n
  3. \n
  4. In a traditional model-view-controller web app, business logic is defined in the Model layer. The Controller responds to incoming requests by talking to the model layer and passing model objects to the View, which renders the objects for presentation in a browser.\n\nThe implementation of such a system is pure MVC. It is very easy to illustrate where the application executes in the client/server relationship: it all executes on the server.\n
  5. In a traditional model-view-controller web app, business logic is defined in the Model layer. The Controller responds to incoming requests by talking to the model layer and passing model objects to the View, which renders the objects for presentation in a browser.\n\nThe implementation of such a system is pure MVC. It is very easy to illustrate where the application executes in the client/server relationship: it all executes on the server.\n
  6. In a traditional model-view-controller web app, business logic is defined in the Model layer. The Controller responds to incoming requests by talking to the model layer and passing model objects to the View, which renders the objects for presentation in a browser.\n\nThe implementation of such a system is pure MVC. It is very easy to illustrate where the application executes in the client/server relationship: it all executes on the server.\n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n