Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 30 Next »

Hello Angular!

Data Binding and Responding to Scope Changes

This example demonstrates how to react on a model change to trigger some further actions. The value greeting will be changed whenever there's a change to the name model and the value is not blank.

<html>
  <head>
    <script src="js/angular.js"></script>
    <script src="js/app.js"></script>
    <link rel="stylesheet" href="css/bootstrap.css">
  </head>
  <body ng-app>
    <div ng-controller="MyCtrl">
      <input type="text" ng-model="name" placeholder="Enter your name">
      <p>{{greeting}}</p>
    </div>
  </body>
</html>
function MyCtrl($scope) {
  $scope.name = "";

  $scope.$watch("name", function(newValue, oldValue) {
    if (newValue.length > 0) {
      $scope.greeting = "Greetings " + newValue;
    }
  });
}

Why AngularJS?

TODO: 

What is the use case for this technology from the developers point of view

What benefit does a developer get from using this technology

What pain were we trying to solve by adopting this technology

What are the strengths and weaknesses of this technology

  • STRENGTHS
    • Two-way data binding
    • HTML Templates
    • Dependency Injection
    • Deep Linking
    • Directives!!!
    • Testable
    • Embeddable - you can attach the AngularJS application to a specific DOM element and not have to worry about nasty side effects outside of that element and into the rest of the page

What technologies complement this technology and can be used in tandem

How does it stand out from the crowd?

  • Automatic refresh and the two-way data binding frees developers from the tedious work of explicitly triggering UI repaints
  • Live DOM generated from HTML syntax is used as a templating language. More importantly, it is possible to extend an existing HTML vocabulary (by creating new directives), and then build UIs using a new HTML-based DSL
  • Declarative approach to the UI results in a very concise and expressive way
  • The excellent UI templating machinery doesn't put any constraints on the JavaScript code (for example, models and controllers can be created using plain, old JavaScript without the AngularJS APIs called all over the place)

Getting your development environment set up

TODO: add content here

About Angular

  • Open source project hosted on GitHub and licensed by Google, Inc. under the terms of the MIT license.
  • AngularJS is a relatively new actor on the client-side MVC frameworks scene, yet it has managed to attract a lot of attention, mostly due to its innovative templating system, ease of development, and very solid engineering practices
  • Prescriptive Framework - AngularJS is prescriptive in the sense that it has a recommended way of building web apps. It has its own spin on the ubiquitous MVC pattern that is especially well suited for JavaScript.

Core concepts

Modules

  • Advantages:
    • Keeps our global namespace clean
    • Eases writing tests as well as keeps them clean as easy to target isolated functionality
    • Eases to share code between applications
    • Allows different parts of the code to be loaded in any order

Scopes

  • Scopes objects that contain functionality and data to use when rendering the view. It is the single source of truth for all views. You can think of scopes as view models.
  • Basic Functions
    • They provide observers to watch for model changes
    • They provide the ability to propagate model changes through the application as well as outside
      the system to other components
    • They can be nested such that they can isolate functionality and model properties.
    • They provide an execution environment in which expressions are evaluated.

Scope Inheritance

Every part of an AngularJS application has a parent scope (at the ng-app level, this is called the $rootScope). All scopes are created with prototypal inheritance, meaning that they have access to their parent scopes.

By default, for any property that AngularJS cannot find on a local scope, AngularJS will crawl up to the containing (parent) scope (and so on and so forth until it reaches the $rootScope) and look for the property or method there.

In this example, we can then reference data on the ParentController's containing $scope on the child scope.

<div ng-controller="ParentController">
  <div ng-controller="ChildController">
    <a href="#" ng-click="sayHello()">Say hello</a>
  </div>
  {{ person }}
</div>
  var app = angular.module('myApp', []);

  app.controller('ParentController', function($scope) {
    $scope.person = { greeted: false };
  })

  app.controller('ChildController', function($scope) {
    $scope.sayHello = function() {
      $scope.person.name = "Chuck Norris";
      $scope.person.greeted = true;
    }
  })
  • Expressions

    • Expressions are similar to like the result of an eval(javascript) (roughly). They are processed by Angular and, therefore, have these important, distinct properties:
      • All expressions are executed in the context of the scope and have access to local $scope variables.
      • They do not throw errors if an expression results in a TypeError or a ReferenceError.
      • They do not allow for any control flow functions (conditionals; e.g., if/else).
      • They can accept a filter and/or filter chains
  • Controllers

    • The controller in AngularJS is a function that adds additional functionality to the scope of the view. They are used to set up an initial state and to add custom behavior to the scope object.
    • To create custom actions we can call in our views, we can just create functions on the scope of the controller.

Services

  • Services provide a method for us to keep data around for the lifetime of the app and communicate across controllers in a consistent manner.
  • Services are singletons objects that are instantiated only once per app (by the $injector) and lazy-loaded (only created when necessary). They provide an interface to keep together methods that relate to a specific function.

The most common and flexible way to create a service uses the angular.module API factory:

// Example service that holds on to the current_user for the lifetime of the app
angular.module('myApp.services', [])
  .factory('UserService', ['$http', function($http) {

    var current_user;

    return {
      getCurrentUser: function() {
        return current_user;
      },
      setUsername: function(user) {
        current_user; = user;
      }
    }

  }]);

Filters

Angular Filters are typically used to format expressions in bindings in your template. They transform the input data to a new formatted data type.

Implementing a Custom Filter to Reverse an Input String:

<body ng-app="MyApp">
  <input type="text" ng-model="text" placeholder="Enter text"/>
  <p>Input: {{ text }}</p>
  <p>Filtered input: {{ text | reverse }}</p>
</body>
var app = angular.module("MyApp", []);

// Create custom filter
app.filter("reverse", function() {
  return function(input) {
    var result = "";
    input = input || "";
    for (var i=0; i<input.length; i++) {
      result = input.charAt(i) + result;
    }
    return result;
  };
});

Building

TODO: add content here

Testing

TODO: add content here

Best Practices

There are some great resources available on this topic already. Instead of duplicating all of them, here are some links to review.

However, it does makes sense reiterate some of the most relevant ideas as well as a few that apply to Pentaho-specific development.

  • App Structure

    • Stick to the one file equals one AngularJS module principle. This will allow you to maintain relatively small, focused files and modules. Additionally you won't be concerned with the load order of those files. Also it will be possible to load individual modules under unit tests.
  • Data Binding

    • Due to the nature of javascript itself and how it passes by value vs. reference, it's considered a best- practice in Angular to bind references in the views by an attribute on an object, rather than the raw object itself.
  • Services

    • If you want your function to be called like a normal function, use factory. If you want your function to be instantiated with the new operator, use service. If you don't know the difference, use factory. Service or Factory?
  • Controllers

    • Create variables inside the controller to be explicit.
    • It is considered a best-practice to name our controllers NameCtrl in camelcase.
    • Keep controllers slim by using the dependency injection feature of AngularJS to access services.
  • Spreading route definitions among several modules

In a large scale web application, it is not uncommon to have dozens and dozens of different routes. While the $routeProvider service offers a very nice, fluent style API, route definitions can get quite verbose (especially when the resolve property is used). If we combine a large number of routes with the relative verboseness of each definition, we quickly end up maintaining a huge JavaScript file with several hundreds of lines of code! Worse yet, having all routes defined in one huge file means that this file must be modified quite frequently by all developers working on a project – a recipe for creating a bottlenecks and disastrous merge issues.

With AngularJS, we are not forced to define all the routes in one central file! If we take the approach of modularizing each functional area (has its own dedicated module), we can move routes linked to a certain part of an application to the corresponding module.

In the AngularJS module system, each and every module can have its associated config function, where we can inject $routeProvider service and define routes. For example: each of these submodules defines its own routes as follows:

angular.module('admin-users', [])
.config(function ($routeProvider) {
  $routeProvider.when('/admin/users', {...});
  $routeProvider.when('/admin/users/new', {...});
  $routeProvider.when('/admin/users/:userId', {...});
});

angular.module('admin-projects', [])
.config(function ($routeProvider) {
  $routeProvider.when('/admin/users', {...});
  $routeProvider.when('/admin/users/new', {...});
  $routeProvider.when('/admin/users/:userId', {...});
});

angular.module('admin', ['admin-projects', 'admin-users']);
  • Directives

    • While declaring an AngularJS directive, the naming convention followed is camelCase. For example, we would define the name of the directive as 'myDatePicker'. But when you actually use the directive in your HTML, it is the dash-separated version (my-date-picker).
    • To make your directives and attributes HTML5 compliant, you can prefix all AngularJS attributes with "data-" or "x-". That is, "x-ng-show" or "data-date-picker", and AngularJS will treat it just like you wrote it like a normal HTML element.

Plugging-In

TODO: add content here

  • No labels