Dynamic template in AngularJS Directive (Not templatUrl) - angularjs

I have seen the ability to have a dynamic templateUrl within a Directive in Angular but while researching I have yet to see a dynamic template.
.directive('col', function () {
var template = '<div class="one" ng-transclude></div>';
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
if (attrs.two !== undefined) {
template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
template = '<div class="three" ng-transclude></div>';
} else {
template = '<div class="one" ng-transclude></div>';
}
console.log(template);
}
};
})
HTML:
<col three>Three</col>
<col two>Two</col>
<col>Nothing</col>
The console shows appropriately:
<div class="three" ng-transclude></div>
<div class="two" ng-transclude></div>
<div class="one" ng-transclude></div>
However the output shows the default / initial <div class="one" ng-transclude></div>

This is because the template is collected before the link function is fired, if your only trying to change the classes then just do something like this.
.directive('col', function () {
var template = '<div class="{{class}}" ng-transclude></div>';
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
$scope.class = attrs.three ? 'three' : attrs.two ?'two' : attrs.one ? 'one' : '';
}
};
});
Other than that I don't know how else you would accomplish it.

When you say 'template = 'xxx' in your link function it is referencing your template variable, not the property on the returned object. Try this:
.directive('col', function () {
var result = {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
if (attrs.two !== undefined) {
result.template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
result.template = '<div class="three" ng-transclude></div>';
} else {
result.template = '<div class="one" ng-transclude></div>';
}
console.log(result.template);
}
};
return result;
})

If the template is dynamically constructed based on the attributes then you can just supply a template function passing in the attributes
.directive('col', function () {
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: function(element, attrs) {
var template = null;
if (attrs.two !== undefined) {
template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
template = '<div class="three" ng-transclude></div>';
} else {
template = '<div class="one" ng-transclude></div>';
}
return template;
},
link: function (scope, element, attrs) {
}
};
})
If the template is dynamically constructed based on the model then it's bit more involved. Note that the transclusion is done explicitly rather than using the ng-transclude directive.
.directive('col', function () {
return {
restrict: 'E',
scope: {
myVariable: '='
},
transclude: true,
replace: true,
link: function (scope, element, attrs, nullController, transclude) {
var template = null;
if (scope.myVariable == 'two') {
template = '<div class="two"></div>';
} else if (scope.myVariable == 'three') {
template = '<div class="three"></div>';
} else {
template = '<div class="one"></div>';
}
element.html(template);
$compile(element.contents())(scope);
transclude(scope.$parent, function(clone, scope) {
element.children().append(clone);
});
}
};
})

Related

how angular directive bind controller's service data

controller:
service.checkSub(function(data){
$scope.showSub = data.subscribe? false : true;
})
directive:
app.directive('showSub', function() {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
templateUrl: '<div data-ng-show="show">test</div>',
link: function(scope, element, attrs) {
console.log(scope.showSub); // undifined
if(scope.showSub) {
scope.show = true;
}else {
scope.show = false;
}
}
}
});
<show-sub show="showSub"></show-sub>
why the scope.showSub in directive is undefined ,and I want to use it to control the directive? how should I do it?
The scope.showSub gives undefined, because when loading in the directive, the showSub of your controller scope isn't filled yet. What you can do to fix it:
Change templateUrl to template
Change ng-show="show" to ng-show="showSub"
Lose the link function (it is not needed, as you can directly bind to the scope variables in your template)
code:
app.directive('showSub', function($timeout) {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
template: '<div data-ng-show="showSub">test</div>',
link: function(scope, elem) {
// this function isn't needed, but to show you it gives undefined due to the async call
console.log(scope.showSub); // undefined
$timeout(function(){
console.log(scope.showSub); // true
}, 1500);
}
}
});
Here is a jsfiddle
Your directive is fine but problem with the service.
service.checkSub(function(data){
$scope.showSub = data.subscribe? false : true;
})
$scope.showSub should be in parent scope.
make sure you have data in $scope.showSub
You can get value of showSub by scope.$parent.showSub
So your code will be like ..
app.directive('showSub', function() {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
templateUrl: '<div data-ng-show="show">test</div>',
link: function(scope, element, attrs) {
console.log(scope.$parent.showSub);
if(scope.$parent.showSub) {
scope.show = true;
}else {
scope.show = false;
}
}
}
});

custom directive for radio buttons in angular js

create the custom directive for radio buttons in angular js
i tried so far
HTML:
<my-raido></my-raido>
<my-raido></my-raido>
<my-raido></my-raido>
Angular js:
var App = angular.module('myRaido',[]);
App.directive('myRaido',function() {
return {
restrict : 'E',
template: '<div class="myClass"><input type="radio"></div>',
replace:true,
link:function (scope, element, attrs) {
element.click(function () {
console.log("clicked....");
// body...
});
console.log("link should be work as per our expections...", + attrs.valve);
}
}
});
Advance Thanks
You should set a name attribute on your radio elements to make them a part of a group.
angular.module('app', [])
.directive('myRadio', function() {
return {
restrict : 'E',
template: '<div class="myClass">\
<label>\
<input type="radio">\
<span></span>\
</label>\
</div>',
replace: true,
link: function (scope, element, attrs) {
var $radio = element.find('input');
var $span = element.find('span');
$radio.attr('name', attrs.group);
$span.html(attrs.label);
}
}
});
If more than one input[radio] controls have a same name, they will behave as a group.
You would use this directive like this:
<my-radio group="group1" label="Option1"></my-radio>
<my-radio group="group1" label="Option2"></my-radio>
<my-radio group="group1" label="Option2"></my-radio>
var App = angular.module('myRaido',[]);
App.directive('myRaido',function() {
return {
restrict : 'E',
template: '<div class="myClass"><input type="radio" name='fooRadios'></div>',
replace:true,
link:function (scope, element, attrs) {
element.click(function () {
console.log("clicked....");
// body...
});
console.log("link should be work as per our expections...", + attrs.valve);
}
}
});

Directive works with TemplateUrl but not Template

This is a strange one. I am new to angular, only been learning for a week or so.
I have two directives.
The first:
app.directive('block', function() {
return {
restrict: 'E',
replace: true,
scope:true,
link: function(scope, element, attrs) {
scope.blockCss = function() {
return attrs.block;
}
scope.blockSize = function() {
return attrs.blocksize;
}
scope.category = function() {
return attrs.category;
}
},
templateUrl: function(elem,attrs) {
return attrs.block + '.html';
}
}
});
This works perfectly on an element called:
<block blocksize="3" category="foo1" blockcss="bar1"></block>
Now the kicker.
An identical directive but changing from TemplateUrl to Template so as to have something more dynamic:
app.directive('block', function() {
return {
restrict: 'E',
replace: true,
scope:true,
link: function(scope, element, attrs) {
scope.blockCss = function() {
return attrs.block;
}
scope.blockSize = function() {
return attrs.blocksize;
}
scope.category = function() {
return attrs.category;
}
},
template: function(elem,attrs) {
return '<div class="blocksize{{blockSize()}} {{blockCss()}}" ng-repeat="item in items | orderByPriority | filter:{category:category()} | orderBy:myorder" ng-include src="\'{{blockCss()}}.html\'"></div>';
}
}
});
I might be an idiot but if someone could point out to me what I am missing I would appreciate it.
Further to the above, I would like to be able to refer to the attributes in the element (e.g. the 'block' element) inside the template or template URL - that is why I have the scope definitions (which need to be scope children).
Really looking forward to any kind of reply.

AngularJS - accessing parent directive properties from child directives

This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}

AngularJS - Share ngModel with nested directives and transcluded directives

I am trying to create 3 directives:
.directive('dirOne', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
controller: function ($scope, $element, $attrs) {
this.add = function (tag) {
tag && $scope.tags.push(tag);
};
},
template: '<div><p>Bucket from directive: {{tags}}</p><div ng-transclude></div></div>'
};
})
.directive('dirTwo', function () {
return {
restrict: 'A',
replace: true,
require: '^dirOne',
link: function (scope, element, attrs, dirOne) {
scope.add = function (tag) {
dirOne.add(tag);
};
},
template: '<div>'+
' <input type="text" ng-model="query" datum="sugestions" dir-three>' +
' <button ng-click="add(query)">add</button>' +
'</div>'
};
})
.directive('dirThree', ['$compile', function ($compile) {
var itemsTemplate = '<span class="sugestions"><span ng-repeat="item in datum|filter:query">{{item.name||item}}</span></span>';
return {
restrict: 'A',
transclude: true,
replace: true,
require: 'ngModel',
scope: {
datum: '=',
query: '=ngModel'
},
link: function (scope, element, attrs) {
var parent;
element.wrap('<span>');
parent = element.parent();
parent.append(angular.element($compile(itemsTemplate)(scope)));
}
};
}])
In the dirTwo and dirThree, I have an input <input type="text" ng-model="query" datum="sugestions" dir-three> with ngModel, this input needs to access and modify the content of ngModel, so that the scope is not isolated.
http://jsfiddle.net/joaoneto/hbABU/3/
Update
I Updated version, fix some mistakes I had committed, the content that was being transcluded in dirTwo, should not have the "ADD" function, and belongs to dirTree, hope it helps someone and apologize for peopl take to update this entry... see in http://jsfiddle.net/hbABU/4/

Resources