AngularJS : Custom directives scopes

In the previous post we saw custom directives and know that directives have access to the parent scope by default in AngularJS applications. If you want to make a reuseable directive you can’t rely on the parent scope that may change or vice versa. This could lead to unexpected results. So angular js provided a way to isolate the scope of our directive using the scope property.

index.html


<!DOCTYPE html>
<html lang="en-us" ng-app="angularApp">
<head>
<title>Html Link Fragments</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body>

<header>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand">AngularJS Demo</a>
</div>
</div>
</nav>
</header>
<div class="container" ng-controller="mainController">
<div class="list-group">
<book-result></book-result>
</div>
</div>
</body>
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="../js/main.js"></script>
</html>

main.js


var myApp = angular.module('angularApp', []);
myApp.controller('mainController', ['$scope', function($scope) {

$scope.book={
title:'Book1',
description:'Book1 description'
}

}]);

myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
}
}

});

Note the scope property in the directive.

bookResultDirective.html


<a href="#" class="list-group-item">
<h4>{{book.title}}</h4>
<p>{{book.description}}</p>
</a>

customDirectiveScope1

In Isolated scope, the directive does not share a scope with the controller; both directive and controller have their own scope. Data can be passed to the directive scope in three possible ways.

  1. Data can be passed as a string using the @ string literal
  2. Data can be passed as an object using the = string literal
  3. Data can be passed as a function using the & string literal

Lets see the first case with an example. So we have book object in out $scope and we need to access its title property in our isolated scope. Changing in our index.html. Rest all will remain same


<book-result book-title={{book.title}} ></book-result>

We used a custom attribute book-title that gets the value from the book object that in out $scopr. Capturing this in our directive. Remember the normalization rule. We could refer this by camelCase equivalent name in our directive. Chaning main.js


myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookTitle: '@'
}
}

});

We could use other name wee like
mybooktitle:’@bookTitle’
and use mybooktitle in our directive view but lets keep things simple. Using this in our bookResultDirective.html


<a href="#" class="list-group-item">
<h4>{{bookTitle}}</h4>
</a>

customDirectiveScope2

Lets see how to pass an object to the directive. We already have the object book in our $scope. Changing in index.html


<book-result book-object="book" ></book-result>

Note that {{book}} cannot be used as it is used to pass in text. For object “” needs to be used. Capturing it in main.js


myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '='
}
}

});

‘=’ also means that there is a two-way binding. So whatever happened to this object inside your directive will also affect object outside.
Using it in bookResultDirective.html


<a href="#" class="list-group-item">
<h4>{{bookObject.title}}</h4>
<p>{{bookObject.description}}</p>
</a>

customDirectiveScope3

What if we have an array of objects. So in our controller we have an books array on to our $scope.


var myApp = angular.module('angularApp', []);
myApp.controller('mainController', ['$scope', function($scope) {

$scope.books=[
{
title:'Book1',
description:'Book1 description'
},
{
title:'Book2',
description:'Book2 description'
}
]

}]);

myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '='
}
}

});

Using ng-repeat in our index.html


<div class="list-group">
<div ng-repeat="book in books">
<book-result book-object="book" ></book-result>
</div>
</div>

customDirectiveScope4

You can pass the array directly and use ng-repeat in your directive also.

Now lets see how to pass a function. Suppose we have a function on to our $scope in our main controller


myApp.controller('mainController', ['$scope', function($scope) {

$scope.books=[
{
title:'Book1',
description:'Book1 description'
},
{
title:'Book2',
description:'Book2 description'
}
]

$scope.getMoreDetail=function(bookName){
console.log(bookName);
}

}]);

index.html


<div class="list-group">
<div ng-repeat="book in books">
<book-result book-object="book" get-book-details="getMoreDetail(abookTitle)"></book-result>
</div>
</div>

We are passing getMoreDetail function to an attribute get-book-details. Also notice the parameter name. Getting this in our directive js – main.js


myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '=',
getBookDetails:'&'
}
}

});

bookResultDirective.html


<a href="#" class="list-group-item" ng-click="getBookDetails({abookTitle:bookObject.title})">
<h4>{{bookObject.title}}</h4>
<p>{{bookObject.description}}</p>
</a>

we can use the getBookDetails using ng-click and in order to tell angular what abookTitle is we have to use an mapping object.

customDirectiveScope5

You could use {{}} if you want to execute and get some text inside your directive.

Advertisements
%d bloggers like this: