SlideShare a Scribd company logo
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

More Related Content

What's hot

amis-adf-enterprise-mobility
amis-adf-enterprise-mobilityamis-adf-enterprise-mobility
amis-adf-enterprise-mobility
Luc Bors
 
243329387 angular-docs
243329387 angular-docs243329387 angular-docs
243329387 angular-docs
Abhi166803
 
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 Java
Darshit 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 ReactJS
Visual Engineering
 
Mean stack Magics
Mean stack MagicsMean stack Magics
Mean stack Magics
Aishura Aishu
 
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 — iOS
jimmyatmedium
 
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
Elyse 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 emberjs
Mandy 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 Development
FITC
 
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 & Customization
jguerrero999
 
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
 

What's hot (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
 

Similar to 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 Application
Andrew Rota
 
Create an application with ember
Create an application with ember Create an application with ember
Create an application with ember
Chandra Sekar
 
AgularJS basics- angular directives and controllers
AgularJS basics- angular directives and controllersAgularJS basics- angular directives and controllers
AgularJS basics- angular directives and controllers
jobinThomas54
 
Understanding angular js
Understanding angular jsUnderstanding angular js
Understanding angular js
Aayush 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
 
Angular beans
Angular beansAngular beans
Angular beans
Bessem Hmidi
 
Intoduction to Angularjs
Intoduction to AngularjsIntoduction to Angularjs
Intoduction to Angularjs
Gaurav Agrawal
 
Ng-init
Ng-init Ng-init
Ng-init
Hamdi Hmidi
 
Ng-init
Ng-init Ng-init
Ng-init
Hamdi Hmidi
 
Python Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - AppenginePython Ireland Nov 2009 Talk - Appengine
Python Ireland Nov 2009 Talk - AppenginePython Ireland
 
Angular js
Angular jsAngular js
AngularJS By Vipin
AngularJS By VipinAngularJS By Vipin
AngularJS By Vipin
Vipin Mundayad
 
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
 
Basics of AngularJS
Basics of AngularJSBasics of AngularJS
Basics of AngularJS
Filip Janevski
 
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 Rails
codeinmotion
 
Angular js anupama
Angular js anupamaAngular js anupama
Angular js anupama
Anupama Prabhudesai
 
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
Learnimtactics
 

Similar to 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
 
AgularJS basics- angular directives and controllers
AgularJS basics- angular directives and controllersAgularJS basics- angular directives and controllers
AgularJS basics- angular directives and controllers
 
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
 

Recently uploaded

GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Nexer Digital
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
James Anderson
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
Pierluigi Pugliese
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
Aftab Hussain
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 

Recently uploaded (20)

GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 

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); }); } });

Editor's Notes

  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