Angular rocks my socks

GeekcampSG 2013
7 Sep 2013
Ruiwen / Conor
The Pinchos





angularjs

Because mental Javascript-to-HTML translation is a PITA


Because widgets are fantastic

because our asses are worth saving

I am not a compiler.

angular 

<ul id="items">  <li ng-repeat="i in items">    <div class="view">      <input class="toggle" type="checkbox" {{ i.checked ? 'checked': '' }}>      <label>{{ i.title }}</label>      <a class="destroy"></a>    </div>    <input class="edit" type="text" value="{{ i.title }}">  </li></ul>
  <script type="text/template" id="item-template">
    <div class="view">
      <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
      <label><%- title %></label>
      <a class="destroy"></a>
    </div>
    <input class="edit" type="text" value="<%- title %>" />
  </script>

backbone

// The DOM element for a todo item...
  var TodoView = Backbone.View.extend({

    //... is a list tag.
    tagName:  "li",

    // Cache the template function for a single item.
    template: _.template($('#item-template').html()),

    // The DOM events specific to an item.
    events: {
      "click .toggle"   : "toggleDone",
      "dblclick .view"  : "edit",
      "click a.destroy" : "clear",
      "keypress .edit"  : "updateOnEnter",
      "blur .edit"      : "close"
    },

    // The TodoView listens for changes to its model, re-rendering. Since there's
    // a one-to-one correspondence between a **Todo** and a **TodoView** in this
    // app, we set a direct reference on the model for convenience.
    initialize: function() {
      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'destroy', this.remove);
    },

    // Re-render the titles of the todo item.
    render: function() {
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.toggleClass('done', this.model.get('done'));
      this.input = this.$('.edit');
      return this;
    },

    // Toggle the `"done"` state of the model.
    toggleDone: function() {
      this.model.toggle();
    },

    // Switch this view into `"editing"` mode, displaying the input field.
    edit: function() {
      this.$el.addClass("editing");
      this.input.focus();
    },

    // Close the `"editing"` mode, saving changes to the todo.
    close: function() {
      var value = this.input.val();
      if (!value) {
        this.clear();
      } else {
        this.model.save({title: value});
        this.$el.removeClass("editing");
      }
    },

    // If you hit `enter`, we're through editing the item.
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },

    // Remove the item, destroy the model.
    clear: function() {
      this.model.destroy();
    }

  });
 

<pony pinkie-pie> 

<!-- Facebook SDK -->
<fb app-id='12345678'></fb>





<!-- Facebook SDK -->
<fb app-id='12345678'
fb-init="$scope.doStuff()"></fb>


// Facebook SDK
angular.module('bp-utils')
  .directive('fb', ['$FB', function($FB) {
    return {
      restrict: "E",
      replace: true,
      template: '<div id="fb-root"></div>',
      compile: function(tElem, tAttrs) {
        return {
          post: function(scope, iElem, iAttrs, controller) {
            var fbAppId = iAttrs.appId || '';
            var fb_params = {
              appId: iAttrs.appId || "",
              cookie: iAttrs.cookie || true,
              status: iAttrs.status || true,
              xfbml: iAttrs.xfbml || true,
            };

            // Setup the post-load callback
            window.fbAsyncInit = function() { if ('fbInit' in iAttrs) { iAttrs.fbInit(); } };

            (function(d, s, id, fbAppId) {
              var js, fjs = d.getElementsByTagName(s)[0];
              if (d.getElementById(id)) return;
              js = d.createElement(s); js.id = id; js.async = true;
              js.src = "//connect.facebook.net/en_US/all.js";
              fjs.parentNode.insertBefore(js, fjs);
            }(document, 'script', 'facebook-jssdk', fbAppId));
          }
        };
      }
    };
  }])

use-ajax

 angular.module('bp-utils')

    .directive('useAjax', [
        '$rootScope',
        '$location',
        'progressbar',
        'bp-utils.pushState',
        function($rootScope, $location, progressbar, pushState) {
            return {
                restrict: 'A',
                link: function(scope, element, attr) {

                    var url = attr.action || 'search'
                      , method = attr.method || 'get'
                      , action = attr.useAjax || function() {}
                      , afterSubmit = scope.$eval(attr.uaAfter)
                      , beforeSubmit = scope.$eval(attr.uaBefore);

                    // override
                    beforeSubmit = beforeSubmit || function(event, options) {};

                    // override
                    afterSubmit = beforeSubmit || function(event, options) {};

                    submit = function(event, pageNumber) {
                        var data = '';
                        data += $(element[0]).serialize();

                        beforeSubmit(data);

                        $.ajax({
                            url: url,
                            data: data,
                            type: method,
                            success: function(data, status, xhr) {
                                if (action && scope[action]) scope[action](data);
                                if (!scope.$$phase) scope.$apply();
                            },
                            error: function(xhr, status, error) {

                            },
                            complete: function(data) {
                                afterSubmit(data);
                            }
                        });

                        event.preventDefault();
                        return false;
                    };

                    element.on('submit', submit);
                }
            };
        }
    ])
;

 

Asians love tests

describe('BillPin Desktop Web', function() {

	var $LS;  // Embedded iframe's localStorage
	var user;

	beforeEach(function() {
        // Do some setup
    });

	describe('When logging in through Landing', function() {
        it('should show the Landing page', function() {
            browser().navigateTo('/');
            expect(element('#id_username').count()).toBe(1);
            expect(element('#id_password').count()).toBe(1);
        });

        it('logging in should show the app page', function() {

            element('#login_link').click();
            element('#id_username').val('billtest@billpin.com');
            element('#id_password').val('test1234');
            element('.red_button').click();
        });
    });

 

TIME FOR YOUR DEPENDENCY INJECTION

TRADITIONAL MODULE Structure

 var billpinModule = angular.module('billpin', [
    'models', 
    'controllers', 
    'directives', 
    'filters', 
    'services'
]);


TOO MAINSTREAM. ANOTHER WAY?

CAN SOCKS SHOW US THE WAY?


REFLECT YOUR PURPOSE


 var billpinModule = angular.module('billpin', [
    'controllers',
    'utils',
    'ngCookies',
    'bp-facebook',
    'bp-invite',
    'bp-user',
    'bp-history',
    'bp-currency',
    'bp-home',
    'bp-photos',
    'bp-pay-me',
    'app-templates',
    'component-templates'
]);

 

grunt

all at once



HEAVY MUCH?


 

HATE IS THE PUREST FORM OF LOVE

THE HONEYMOON PERIOD

ADVANCED



Title

Geekcamp2013

By Ruiwen Chua

Geekcamp2013

  • 849