AngularJS Overview and Best Practices
Agenda
- Core Angular Features
- Data Bindings
- Controllers
- Services and Dependency Injection
- Directives
- Filters
- Best Practices
- Working with .NET MVC
Core Angular Features
- Data Bindings (One and Two Way Bindings)
- INotifyPropertyChanged (Observers) vs. Digest Cycle
- Main Elements:
- Controllers
- Factories
- Directives
- Filters
- Dependency Injection
Data Bindings
- Synchronization of data between the view and the model
- One Way / Two Way / (Three Way)
Data Bindings
One Way
<!DOCTYPE html>
<html ng-app>
<head>
<script src="angular.js"></script>
</head>
<body ng-init="firstName = 'Peter'; lastName = 'Pan';">
<strong>First name:</strong> {{firstName}}<br />
<strong>Last name:</strong> <span ng-bind="lastName"></span>
</body>
</html>
Data Bindings
Two Way
<!DOCTYPE html>
<html ng-app>
<head>
<script src="angular.js"></script>
</head>
<body ng-init="firstName = 'Peter'; lastName = 'Pan';">
<strong>First name:</strong> {{firstName}}<br />
<input type="text" ng-model="firstName"><br /><br />
<strong>Last name:</strong> <span ng-bind="lastName"></span><br />
<input type="text" ng-model="lastName">
</body>
</html>
Data Bindings
Lists
<!DOCTYPE html>
<html ng-app>
<head>
<script src="angular.js"></script>
</head>
<body ng-init="persons = [{'name': 'Person 1'},
{'name': 'Person 2'}, {'name': 'Person 3'}]">
<ul>
<li ng-repeat="person in persons">
<span>{{person.name}}</span>
</li>
</ul>
</body>
</html>
Controllers
- Control the data of AngularJS applications
- Glue between the View and the Data
<div ng-controller="PersonController as pc">
First Name: <input type="text" ng-model="pc.firstName"><br>
Last Name: <input type="text" ng-model="pc.lastName"><br>
<br>
Full Name: {{pc.firstName + " " + pc.lastName}}
</div>
(function(){
'use strict';
angular.module('person').controller('PersonController', ['$scope', function ($scope) {
$scope.firstName = 'Peter';
$scope.lastName = 'Pan';
}]);
})();
Services and Dependency Injection
- Share information across your application
- helpful Interfaces
Dependency Injection = Takes control of creating components, resolving their dependencies, and providing them to other components as requested
Services and Dependency Injection
(function(){
'use strict';
angular.module('person')
.controller('PersonController', ['$scope', 'persons', 'notifications'
function ($scope, persons, notifications) {
$scope.persons = persons.getAll().then(function(response) {
notifications.success('Persons loaded.');
}, function(error) {
notifications.error('An error occured while loading the data.');
});
}
]);
})();
Services and Dependency Injection
(function(){
'use strict';
angular.module('person')
.factory('persons', ['apiService', function (apiService) {
// Interface
var persons = {
getAll: getAll,
get: get,
add: add
};
return persons;
// Implementation
function getAll() {
return apiService.call('GET', '/persons/getAll');
}
]);
})();
Services and Dependency Injection
Factory vs. Service
- Factory gets invoked and cached
- Service gets new-ed (constructor)
- It is up to you what you use
Directives
- Extend HTML with new elements or attributes
- Can be seen as components
- Parts:
- Attribute or set that we use to declare an HTML node
- JavaScript that implements the functionality
Directives
(function(){
'use strict';
angular.module('person')
.directive('personList', [function () {
return {
restrict: 'AECM',
replace: true, // make sure there is only one top level node
transclude: true,
controller: function($scope, $element, $attrs),
require: 'ngModel', // ^, ?
link: function($scope, $element, $attributes, controller),
scope: {
data: '=' // @, =, &, ? e.g. =? or =?my-data
},
template: '<div>Test</div>', // or as function
templateUrl: './test.html',
compile: function(tElement, tAttrs), // pre-link, post-link
priority: 0 // default
};
]);
})();
Directives
<span person:list="data"></span>
<span person_list="data"></span>
<span person-list="data"></span>
<span data-person-list="data"></span> // Best Practice
<span x-person-list="data"></span>
// or as an element
<person-list></person-list> // no support for IE8 and below
- Directive name in camelCase can be used as:
Directives
- Built-In Directives:
- ngModel
- ngShow
- ngHide
- ngRepeat
- ...
Filters
- Filters can be added to expressions and directives using a pipe character
- Data Transformation
- currency
- filter
- lowercase
- uppercase
- orderBy
Filters
Search: <input ng-model="searchText">
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:searchText">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
</tr>
</table>
Name only <input ng-model="search.name"><br>
Phone only <input ng-model="search.phone"><br>
Equality <input type="checkbox" ng-model="strict">
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friendObj in friends | filter:search:strict">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
Filters
(function(){
'use strict';
angular.module('common.filters')
.filter('reverse', [function () {
function reverse(input) {
input = input || '';
var result = "";
for (var i = 0; i < input.length; i++) {
result = input.charAt(i) + result;
}
return result;
}
reverse.$stateful = true; // executes each digest cycle
return reverse;
]);
})();
<div>{{ lagerregal | reverse }}</div>
Best Practices
- Define one component per file
- Use IIFEs (Immediately Invoked Function Expressions) to wrap AngularJS components
(function() {
// prevents from certain actions from being taken and throws more exceptions.
'use strict'
app.module(...
})();
Best Practices
- Use controllerAs Syntax also in directives
<div ng-controller="ParentController as pc">
{{ pc.name}}
<div ng-controller="ChildController as cc">
{{ cc.name }}
</div>
</div>
- Declare variables always at the top
return {
restrict: 'EA',
scope: {
value: '='
},
link: linkFn,
controller: ExampleController,
controllerAs: 'vm'
};
Best Practices
- Use named functions over anonymous functions
- more readable code
(function() {
'use strict';
angular.module('dashboard').controller('Dashboard', Dashboard);
dataservice.$inject = ['$http', 'logger'];
function Dashboard($http, logger) { }
})():
- Keep Controllers focused
- define per concern
- move reusable logic to common services
Best Practices
- Use function declaration to hide implementation details
- keep accessible members up top and assign functions
(function() {
'use strict';
angular.module('dashboard').factory('persons', persons);
function persons() {
// Interface
var persons = {
getAll: getAll
}
return persons;
// Implementation
function getAll() {}
}
})():
Best Practices
- Seperate data calls in seperate services (e.g. ApiService)
- Use promises for data calls (Callbacks 2.0)
- No Pyramid of Doom (clearness)
- Synchronization ($q.all)
$http.get('/Checklist/Index')
.then(function(response) { return transform1(response) })
.then(function(response) { return transform2(response) })
.then(function(response) { return transform3(response) });
Best Practices
- Restrict directives to E (Element) and A (Attribute)
- Abstraction by Modularization
- Create modules for concerns
- No DOM-Manipulation in Controllers
- Business Logic belongs to services
- Makes it easy to share among different controllers and other services
- Reduce Code Redundancy by abstracting common functionallity to services (e.g. ApiService)
Best Practices
- data attribute for valid HTML
<div data-ng-controller="ParentController as pc">
<div data-my-directive>
<span>Text goes here</span>
</div>
</div>
- Declare specific Model-Factories (in this case ViewModels)
- .NET ViewModel changes need to be copied to the corresponding angular models
Working with .NET MVC
- Frontend (AngularJS) is seperated from the Backend (.NET Controllers and Models)
- Interaction between Frontend <=> Backend via JSON
- .NET ViewModels will be serialized and mapped to angular models
- Frontend SPA Routing can / will be handled by AngularJS
- .NET Views will no longer be needed
- Validation: no jQuery
Example Directives
- Develop a directive which creates a simple WYSIWYG Editor
2. Enhance the WYSIWYG directive so that values in ngModel
are parsed and formatted as markdown
- The View expects raw HTML and displays raw HTML
- The models saves the HTML as markdown
Example Directives
3. Develop a directive with a Datepicker?
- Pass the date format as an attribute
- Enhance the directive so that the format can be selected with a <select>
ng-neusta
By Dominic E.
ng-neusta
- 825