How to have dynamic template in angularjs directive - javascript

I want to create a directive that be dynamic. In this directive define a template that have an input element. In fact this element ng-model must be dynamic, and use $scope.name in controller.
app.directive('helloWorld', function() {
return {
restrict: 'E',
replace: true,
scope: {
name: '#',
path:'#',
},
template: '<input\
type="text"\
name="{{name}}"\
ng-model="{{name}}"\
/>\,
link: function(scope, elem, attrs) {
},
controller:{
$scope.$watch($scope.name, function (newValue, oldValue) {
}
}
});

A working JSFiddle
Code
var app = angular.module('app',[])
app.directive('helloWorld', function() {
return {
restrict: 'E',
scope: {
name: '#',
path:'#',
},
template: '<input type="text" name="{{name}}" ng-model="name"/> {{name}}',
controller: function($scope) {
$scope.name = "initial value";
$scope.$watch('name', function (newValue, oldValue) {
console.log("newValue: ",newValue);
})
}
}
});

Firstly, your directive syntax is wrong, here is the right one:
app.directive('helloWorld', function() {
return {
restrict: 'E',
scope: {
name: '#',
path:'#',
},
template: '<input type="text" name="{{name}}" ng-model="name">',
link: function(scope, elem, attrs) {
},
controller: function($scope) {
$scope.name = 'asd';
$scope.$watch('name', function (newValue, oldValue) {
})
}
}
});
Secondly, if you are looking to have a dynamic model, you should use scope: {name: '='} as # is for one-time binding
edit
changed name="name in template to name="{{name}}"
And here is a demo

Related

Use ng-model in directive and controller

Is it possible to access in Directive the ng-model that is linked to the Controller?
<input type="text" ng-model="ctrl.valuelist.value" />
Controller:
ctrl.valuelist.value = 'initial value';
In directive:
function Directive($window) {
return {
restrict: 'E',
scope: {
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.valuelist.value = 'New value';
}
};
}
function Directive($window) {
return {
restrict: 'E',
scope: {
ngModel: '=' //<----- access to the model by this
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.ngModel = 'New value';
}
};
}
I'm a pleb, so be kind.
You can pass attributes to your directive by two way('=') this mean when you make change from directive will reflect in controller as well
function Directive($window) {
return {
restrict: 'E',
scope: {
list: '='
},
controller: 'ctrl as ctrl',
link: function(scope, elt, attrs){
// something like this:
scope.list.value = 'New value';
}
};
}
And you must pass valuelist to your directive example:
<directive list='valuelist'></directive>

How do I pass a Child Directive's Data to a Parent Directive in AngularJS

How can I pass a child attribute directive's scope or attr value to a parent directive?
Given widget directive, with in-viewport attribute directive, I want to update the attribute inView each time the document is scrolled, and pass the updated value to the parent directive widget:
<widget in-viewport></widget>
In Viewport directive: passed in as an attribute of parent directive "widget"
angular.module('app').directive('inViewport', function() {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(document).on('scroll', function() {
// I've tried binding it to attr and parent scope of "widget" directive
attr.inView = isElementInViewport(element);
scope.inView = isElementInViewport(element);
});
}
};
});
Widget Directive:
angular.module('app').directive('widget', function() {
return {
restrict: 'AE',
scope: {
inView: '='
},
transclude: false,
templateUrl: 'directives/widgets/widgets.tpl.html',
link: function(scope) {
console.log('In Viewport: ', scope.inView); // Null
Here is the way you can access parent directive variables,
angular.module('myApp', []).directive('widget', function() {
return {
restrict: 'E',
template: '<viewport in-view="variable"></viewport> <h1>{{variable}}</h1>',
link: function(scope, iAttrs) {
scope.variable = 10;
}
}
}).directive('viewport', function() {
return {
restrict: 'E',
scope: {
inView: "=",
},
template: '<button ng-click="click()">Directive</button>',
link: function(scope, iElement, iAttrs) {
scope.click = function() {
scope.inView++;
}
}
}
});
HTML
<div ng-app="myApp" ng-controller="Ctrl1">
<widget></widget>
</div>
Here is the working jsfiddle
http://jsfiddle.net/p75DS/784/
If you have any question, ask in the comment box
Here is a working fiddle using your directive structure:
http://jsfiddle.net/ADukg/9591/
Markup is like this:
<div ng-controller="MyCtrl" style="height: 1200px;">
{{name}}
<hr>
<widget in-viewport></widget>
</div>
Just scroll the window to trigger the event. Note that the parent directive has a watch just to prove that the var gets updated...
var myApp = angular.module('myApp',[]);
myApp.directive('inViewport', function($timeout) {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(window).bind('scroll', function() {
console.log('Called');
$timeout(function() {
scope.inView++;
}, 0);
});
}
};
});
myApp.directive('widget', function() {
return {
restrict: 'AE',
transclude: false,
template: '<p>This is a widget</p>',
link: function(scope) {
scope.inView = 0;
console.log('In Viewport: ', scope.inView); // Null
scope.$watch('inView', function(newVal, oldVal) {
console.log('Updated by the child directive: ', scope.inView);
});
}
}
});
function MyCtrl($scope) {
$scope.name = 'Angular Directive Stuff';
}
You can expose an API on your parent directive and use isolateScope() to access it.
Here's a working fiddle.
var app = angular.module("app",[]);
app.directive("widget", function($rootScope){
return {
template: "<div>Scroll this page and widget will update. Scroll Y: {{scrollPosY}}</div>",
scope: {}, // <-- Creating isolate scope on <widget>. This is REQUIRED.
controller: ['$scope', function DirContainerController($scope) {
$scope.scrollPosY = 0;
// Creating an update function.
$scope.update = function(position) {
$scope.scrollPosY = position;
$scope.$digest();
};
}],
}
});
app.directive("inViewport", function($window, $timeout, $rootScope){
return {
restrict: 'A',
link:function(scope, element, attrs, parentCtrl){
// Get the scope. This can be any directive.
var parentScope = element.isolateScope();
angular.element(document).on('scroll', function() {
// As long as the parent directive implements an 'update()' function this will work.
parentScope.update($window.scrollY);
console.log('parentScope: ', parentScope);
});
}
}
});

Nested directives with ng-repeat causes to strange behaviour

Let's assume that i have a 2 directives: outer and inner. In first directive, i have some data and i want to insert second directive with ng-repeat in it. So my directives looks like:
outer.js
directive('outer', function($compile) {
return {
restrict: 'E',
template: '<div class="options"></div>',
scope: true,
bindToController: {
options: '='
},
link: function(scope, element) {
var list = $(element).find('.options');
// Here we dynamically insert html for second directive
$('<inner><span ng-bind="$select.getValue(item)"></span></inner>').appendTo(list);
$compile(list.contents())(scope);
},
controllerAs: '$select',
controller: function($rootScope) {
this.items = ['aaa', 'bbb'];
this.getValue = function(item) {
$rootScope.log += '\n' + item;
return item;
};
}
};
}
inner.js
directive('second', function($compile) {
return {
restrict: 'E',
require: '^first',
replace: true,
transclude: true,
template: '<ul><li><span></span></li></ul>',
link: function(scope, element, attrs, $select, transclude) {
var choices = $(element.find('li')[0]);
choices.attr('ng-repeat', 'item in $select.items');
var inner = $(choices.find('span')[0]);
transclude(scope, function(clone) {
inner.append(clone);
});
$compile(choices)(scope);
}
};
});
So, in my example, in each $digest cycle $select.getValue should be called twice: for 'aaa' and for 'bbb'. But actually it calls 3 times and first time is called for undefined, but i can't understand why
Any ideas?
See example:
var myApp = angular.module('myApp', []);
myApp.directive('outer', function($compile) {
return {
restrict: 'E',
template: '<div class="options"></div>',
scope: true,
bindToController: {
options: '='
},
link: function(scope, element) {
var list = $(element).find('.options');
$('<inner><span ng-bind="$select.getValue(item)"></span></inner>').appendTo(list);
$compile(list.contents())(scope);
},
controllerAs: '$select',
controller: function($rootScope) {
this.items = ['aaa', 'bbb'];
this.getValue = function(item) {
$rootScope.log += '\n' + item;
return item;
};
}
};
})
.directive('inner', function($compile) {
return {
restrict: 'E',
require: '^outer',
replace: true,
transclude: true,
template: '<ul><li><span></span></li></ul>',
link: function(scope, element, attrs, $select, transclude) {
var choices = $(element.find('li')[0]);
choices.attr('ng-repeat', 'item in $select.items');
var inner = $(choices.find('span')[0]);
transclude(scope, function(clone) {
inner.append(clone);
});
$compile(choices)(scope);
}
};
});
.log {
white-space: pre;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body ng-app="myApp">
<span class="log" ng-bind="log"></span>
<outer></outer>
</body>

How to make two way binding between angular directives without using "dots"

Suppose, I have controller:
angular.module('tf').controller('Ctrl', function($scope){
$scope.params = {
orderBy: null
};});
And a directive "common":
angular.module('tf').directive("common", function() {
return {
restrict: 'E',
replace: true,
template: '<div><outer order-by="orderBy"><inner order-by-field="name1"></inner><inner order-by-field="name2"></inner></outer></div>',
controller: function ($scope) {
},
scope: {
orderBy: '='
},
link: function (scope, element, attrs) {
}
}});
Controller is using directive within it's template:
<div ng-app="tf">
<div ng-controller="Ctrl">
<common order-by="params.orderBy"></common>
<div style="color:red">{{params.orderBy}}</div>
</div>
This directive is using directive "outer":
angular.module('tf').directive("outer", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
controller: function ($scope) {
this.order = function (by) {
$scope.orderBy = by
};
},
scope: {
orderBy: '=',
}
}});
Which is parent for the directive "inner":
angular.module('tf').directive("inner", function() {
return {
require: '^outer',
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-click="onClicked()">{{orderByField}}</div>',
controller: function ($scope) {
$scope.onClicked = function () {
$scope.outer.order($scope.orderByField);
}
},
scope: {
orderByField: '#'
},
link: function (scope, element, attrs, outer) {
scope.outer = outer;
}
}});
The directive "outer" shares "order" method with directive "inner" by it's controller. The directive "inner" is accessing it by using "require" mechanism.
For some reason, this is not working as expected (Property of the controller isn't updated each time it's changed by directive). If I place "orderBy" into object (e.g. {"order": {"by": null }} ) and use object instead of string value, everything is working as expected ( controller scope is properly updated by the directive). I know about "always use dots" best practices principle, but I don't wanna use it here, because it would make my directive's API less intuitive.
Here is jsfiddle:
http://jsfiddle.net/A8Vgk/1254/
Thanks

AngularJS : Interpolating attributes

Say I have the following two directives:
angular.module('foo').directive('outer', [function(){
return {
restrict: 'E',
scope: {
inner: '#',
innneParams: '#'
},
template: "<div {{inner}}{{innerParams}}></div>",
link: function(scope, elem, attrs){
console.debug("I AM IN YOUR OUTER DIRECTIVE PASSING YOUR D00DZ!")
}
}
}]);
angular.module('foo').directive('innerDir', [function(){
return {
restrict: 'EA',
scope: {
innerParam: '='
},
template: "<div>{{massagedInner}}</div>",
link: function(scope, elem, attrs){
console.debug('I AM IN YOUR INNER DIRECTIVE MASSAGING YOUR D00DS!')
scope.massagedInner = scope.innerParam + "FROM YOUR DOGE!"
}
}
}]);
And the following HTML:
<outer inner="inner-dir" my-awesome-scope-value="myAwesomeScopeValue" inner-params="inner-param='myAwesomeScopeValue'"></outer>
The outer directive console debug triggers, the inner one does not. Is there a good way for me to achieve this kind of behaviour?
Plunk: http://plnkr.co/edit/jXbtWvYvtFXCTWiIZUDW?p=preview
There are quite a few things you're doing wrong. I've made the changes that I thought are close to what you wanted it to do and you can change the code from here.
Here's a working version
And this is what script.js now looks like;
angular.module('foo', []);
angular.module('foo').controller('fooController', ['$scope', function(scope){
scope.myAwesomeScopeValue = 'O HAI THERE'
}]);
angular.module('foo').directive('outer', [function(){
return {
restrict: 'E',
scope: {
// inner: '#',
// innnerParams: '#'
innerParam: '#'
},
template: "<div inner {{inner}} {{inner-param}}></div>",
link: function (scope) {
console.log('OUTER', scope.innerParam);
}
}
}]);
angular.module('foo').directive('inner', [function(){
return {
restrict: 'A',
// scope: {
// innerParam: '='
// },
replace: false,
template: "<div>{{massagedInner}}</div>",
link: function(scope, elem, attrs){
scope.massagedInner = scope.innerParam + "FROM YOUR DOGE!"
console.log('INNER');
}
}
}]);
For brevity, I've left some of your lines commented out. I hope this helps.

Categories