Sunday, January 22, 2017

How can I make my ng-include template to have two way binding?

Leave a Comment

I have a tab-content component which gets a dynamic url from the controller

<tab-content url="{{tabContentUrl}}"></tab-content> 

Directive of the component:

myApp.directive("tabContent", () => {     return {         restrict: "E",         scope: true,         link: (scope, element, attrs) => {             scope.getContentUrl = () => {                 return attrs.url || "views/new-week-stats.html";             }         },         template: "<div ng-include='getContentUrl()'></div>"     } }); 

At this point, it can only inject the initial values of the $scope in the controller. When something is changed in the scope it is not refreshed in the DOM. How can I enable two way binding in the directive?

4 Answers

Answers 1

Quick Answer

You have two options.

  1. Same scope as parent with two way binding (scope:false):

HTML:

<tab-content ng-model="tabContentUrl"></tab-content> 

JS:

myApp.controller("MainCtrl", function($scope){     $scope.default = "views/new-week-stats.html";     $scope.tabContentUrl = "views/example.html"; });  myApp.directive("tabContent", () => {     return {         restrict: "E",         scope: false,         template: "<div ng-include='tabContentUrl || default'></div>"     } }); 
  1. Isolated scope with two way binding (scope:{url: '='}):

HTML:

<div ng-controller="MainCtrl">     <tab-content url="tabContentUrl" default="views/new-week-stats.html">     </tab-content> </div> 

JS:

myApp.controller("MainCtrl", function($scope){     $scope.tabContentUrl = "views/example.html" });  myApp.directive("tabContent", () => {     return {         restrict: "E",         scope: {             url: '=',             default: '@'         },         template: "<div ng-include='url || default'></div>"     } }); 


Explanation:

All directives have a scope associated with them. They use this scope for accessing data/methods inside the template and link function. By default, unless explicitly set, directives don’t create their own scope. Therefore, directives use their parent scope (usually a controller) as their own.

In your code on line 4 you specified scope: true,

When a directive scope is set to “true”, AngularJS will create a new scope object and assign to the directive. This newly created scope object is prototypically inherited from its parent scope (the controller scope where it’s been used).

In total there are three options:

Same scope as parent with two way binding.

scope: false, 

Isolated scope but a one way binding.

scope: true, 

Isolated scope with options.

scope: {     "@"   // Creates a copy as text     "="   // Direct model with two-way binding     "&"   // For passing functions } 

Answers 2

ngInclude directive already does what you want for you, just use the directive scope 2 way bindings.

myApp.directive("tabContent", () => {     return {         restrict: "E",         scope: {             url: '='         },         template: '<div ng-include="url || 'views/new-week-stats.html'"></div>'     } }); 

That way, any change in the url on the tabContent element will trigger a change in the ngInclude.

Answers 3

add a watcher in your directive:

HTML

<tab-content url="tabContentUrl"></tab-content> 

JS

myApp.directive( "tabContent", () => {     return {         restrict: "E",         scope   : true,         link    : ( scope, element, attrs ) => {             scope.$watch( () => scope.$eval( attrs.url ), newValue => {                 scope.url = newValue;             } );          },         template: "<div ng-include='url'></div>"     }; } ); 

You can also use $parse instead of $eval, but you don't got an isolated scope, so it should be ok, as long as you don't overwrite the parameters in your directive

Answers 4

You can use an observer to dynamically change the scope value inside your directive.

link: function(scope, element, attrs){       attrs.$observe('tabContentUrl', function(val) {         scope.tabContentUrl = val;       }) } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment