Angular JS
Take a look on the previous article to refresh your memory
Directives
Directive it's just html annotation in some div that triggers corresponding js behavior.
<!-- directive ---- controller name -- alias to controller-->
<div ng-controller="StoreController as store">
Note: the alias is only visible only whithin this div.
Include directive
- ng-include - allows to include the specified file into your elements.
<div ng-show="tab.isSet(1)" ng-include="'product-description.html'">
Note: pass the name directly as a string using the single quotes ('...')
Custom directive
Instead of using ng-include
e.g. <h3 ng-include="'product-title.html'"></h3>
you can create a custom directive e.g. <product-title></product-title>
.
The main benefit of the using the custom directives is that those allows you to write HTML that expresses the behaviour of your application.
Directives can also be used for:
- Expressiong the Complex UI
- Calling events and registering event handlers
- Reusing common components
Element Directive creation
index.html
<product-description></product-description>
myApp.js
app.directive('productDescription', function(){
return {
restrict: 'E',
templateUrl: 'product-description.html'
};
});
product-description.html
<h3>
{{product.name}}
<em class="pull-right">$250.00</em>
</h3>
Note: dash in HTML translates to ...camelCase in JavaScript
Note: U still can use the regular directives on your custom one:
<product-description ng-show="tab.isSet(1)"></product-description>
Attribute Directive creation
index.html
<div product-review ng-show="tab.isSet(2)"></div>
myApp.js
app.directive('productReview', function(){
return {
restrict: 'A',
templateUrl: 'product-review.html'
};
});
product-review
<h3>
<strong>{{product.stars}} Stars</strong>
<em class="pull-right">{{product.description}}</em>
</h3>
Use Element Directives for UI widgets and Attribute Directives for mixin behaviors... like a tooltip.
Add Controller to your Custom Directive
There are two solutions for this problem:
-
Use
ng-controller
on your custom directive:<product-gallery ng-controller="GalleryController as galleryCtrl"></product-gallery>
-
Move the controller logic inside your directive with alias, to not forget to add it each time:
app.directive('productGallery', function(){ return { restrict: 'E', templateUrl: 'product-gallery.html', controller:function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }, controllerAs: 'galleryCtrl' }; }); <!-- so in html it will became just --> <product-gallery></product-gallery>
Using $scope to get rid of Controller alias
Using of $scope
as an function argument allows as to get
of controller alias and invoke the controller method directly with no reference to it.
app.directive('productGallery', function(){
return {
restrict: 'E',
templateUrl: 'product-gallery.html',
controller:function($scope){
// whatchout shared scope!
$scope.current = 0;
$scope.setCurrent = function(imageNumber){
$scope.current = imageNumber || 0;
};
}
};
});
Invocation to controller changed like this.
Before:
{{galleryCtrl.current}}
After:
{{current}}
Isolating the $scope
Directives inherit their parent’s Scope by Default.
So, shared scope can be a problem cause all views will have a common data.
(Similar case like with static fields & instance fields,
but even worse because actually properties are stored into the parent scope)
By passing an object to the scope option, you are creating an isolate scope. This tells the directive not to inherit or share the scope with other ones, but to keep scope inside of itself (like instance scope).
app.directive('productGallery', function(){
return {
restrict: 'E',
templateUrl: 'product-gallery.html',
// give this directive it's own scope
scope: {},
controller:function($scope){
$scope.albumName = '';
$scope.current = 0;
$scope.setCurrent = function(imageNumber){
$scope.current = imageNumber || 0;
};
}
};
});
Watch-out: Child no longer has access to the parent’s scope, cause it's not inherited anymore. So, if some data from parent scope is needed we have to pass it from outside and store in our inner scope:
<a class="card-notes" ng-repeat="product in products" … >
<product-gallery>albumName="{{product.albumName}}"</product-gallery>
</a>
A bit messy right? So, go ahead simplify it.
Binding a String to Our Scope
There are 3 options when binding data to an isolate scope:
- @ - Attribute string binding;
Binds a local/directive scope property to the evaluated value of the DOM attribute. So, u can use it with simple string values as well as with evaluated values{{}}
- = - Two-way model binding;
Binds a local/directive scope property to a parent scope property. So, u cant' use it with{{}}
The binding is bidirectional that allows the local/directive scope and the parent scope to share data. - & - Callback method binding.
Use it if you don't need to share data with the parent, but you just want to call a function defined in the parent scope.
Moving back to our code:
app.directive('productGallery', function(){
return {
restrict: 'E',
templateUrl: 'product-gallery.html',
// give this directive it's own scope
scope: {
albumName: '='
},
controller:function($scope){
$scope.current = 0;
$scope.setCurrent = function(imageNumber){
$scope.current = imageNumber || 0;
};
}
};
});
So, we are passing in albumName, just like we would pass in variable to a function:
<a class="card-notes" ng-repeat="product in products" … >
<product-gallery>albumName="product.albumName"</product-gallery>
</a>
Note: If u'd leave previous expression {{product.albumName}}
, u have to use @
instead e.g. albumName: '@'
.
How to Organize application Modules
It's better to keep the components behavior separately from each other. For this purpose we've to create separate modules to encapsulate related directives into them:
- app.js – top-level module attached via
ng-app
; - gallery.js – all the functionality for gallery and only galleries.
The flow is the following:
-
Create new *.js file and declare the appropriate module: gallery.js
(function() { var app = angular.module('gallery-directives', []); /*move your directives logic here*/ });
-
Add new created module into main dependencies module:
(function() { var app = angular.module('productStore', ['gallery-directives']);
-
Add the module *js file into index.html:
. . . <script src="angular.js"></script> <script src="app.js"></script> <script src="gallery.js"></script>
-
Move your directives into your module *.js file:
(function() { var app = angular.module('gallery-directives', []); app.directive("productGallery", function() { return { restrict: "E", templateUrl: "product-gallery.html", controller: function() { this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }, controllerAs: "gallery" }; });
-
Remove old directives from the main *.js file.
Use Services
Use the Services to fetch the data from API.
Services give you additional functionality:
- Fetching JSON data from a web service with
$http
- Logging messages to the JavaScript console with
$log
- Filtering an array with
$filter
All built-in Services start with a $
sign.
Use $http Service is to make an async request to a server ...
-
Using $http as a function with an options object:
$http({ method: 'GET', url: '/products.json' });
-
Using one of the shortcut methods:
$http.get('/products.json', { apiKey: 'myApiKey' });
Both return a Promise object with .success()
and .error()
If we fetch JSON via $http
, the result will be automatically
decoded into JavaScript objects and arrays.
To use the Service in Controller we should use the following funky syntax:
// we need to store what this is
var store = this;
// we need to initialize products as an empty array to use it before data will be loaded
store.products = [ ];
// ------------------------------- Service names -- passed as arguments -> DI
app.controller('SomeController', [ '$http', '$log', function($http, $log) {
// $http returns a Promise, so the success() gets the data
$http.get('/products.json').success(function(data){
store.products = data;
})
} ]);
In addition to get()
requests, $http can post()
, put()
, delete()
$http.post('/path/to/resource.json', { param: 'value' });
Good day. I was impressed with your article. Keep it up . You can also visit my site if you have time. Thank you and Bless you always.
ReplyDeleteOutsource Angularjs 2 Development in India