AngularJS - dynamically load templateURL when passing object into attribute - javascript

In my controller HTML I am passing an object into a directive as such:
<div cr-count-summary countdata="vm.currentCountData"></div>
The vm.currentCountData is an object that is returned from a factory
My directive code is below:
function countSummary() {
var directive = {
scope: {
countData: '='
},
link: link,
templateUrl: function(element, attrs) {
if (attrs.countdata.type === 'Deposit') {
return 'app/count/countsummary/countDeposit.html';
} else {
return 'app/count/countsummary/countRegisterSafe.html';
}
}
}
}
I have verified that vm.currentCountData is a valid object with a .type property on it. However, it doesn't recognize it. I have tried simplifying things by just passing in countdata="Deposit" in the controller HTML. I've also changed attrs.countdata.type to just attrs.countdata and it does evaluate as the string.
When I have it set up as I have shown above the directive templateUrl function seems to evaluate prior to the controller
I've looked at this, but it seems to only be evaluating strings
What do I need to do in order to allow attrs recognize the object?

This is not possible in this way, because at the time of evaluating templateUrl function angular doesn't have any scope variable, scope gets created after the compile function of directive generates preLink & postLink.
I'd prefer you to use ng-include directive inside the directive template, and then on basis of condition do load the desired template in it.
Markup
<div cr-count-summary count-data="vm.currentCountData"></div>
Directive
function countSummary() {
var directive = {
scope: {
countData: '='
},
link: link,
template: "<div ng-include=\"countdata.type === 'Deposit' ? "+
"'app/count/countsummary/countDeposit.html' :" +
"'app/count/countsummary/countRegisterSafe.html'\">"+
"</div>"
}
}

Related

how can I pass 'scope' argument outside the directive

how can I pass 'scope' argument outside the directive?
i need use it in some other component..
my code:
(function () {
angular.module('dmv.shared.components').
directive('doImportPackage', ['Package', function (Package) {
return {
restrict: 'A',
scope: {
onStart: '<',
onFinish: '<',
onError: '<'},
link: function (scope, element, attributes) {
}
tnx !!
You can do this via a controller. Since AngularJS works in 2-way data binding principle, these variables you assigned will already be updated from where you referenced, and you can use them with other directives too. For example, I assume that you use your directive as follows:
<do-import-package
on-start="myCtrl.onStart"
on-finish="myCtrl.onFinish"
on-error="myCtrl.onError">
</do-import-package>
You have following corresponding variables in myCtrl controllor:
this.onStart = some value;
this.onFinish = some value;
this.onErrod = some value;
Under normal conditions, you can bind other directive's attributes to these values and they will be updated in 2-way. For example, if you use the following directive, both directives should be updated with the same values.
<other-directive
on-start="myCtrl.onStart"
on-finish="myCtrl.onFinish"
on-error="myCtrl.onError">
</other-directive>

scope variable undefined in directive link function

I have a directive which has this piece of code:
return {
restrict: "E",
scope: {
moduleProgress: "=",
modules: "=",
parentScroll: "="
},
templateUrl: 'app/program/module.list.template.html',
link: function(scope,el,attrs,ctrl,tf) {
$templateRequest("app/program/chapter.list.template.html").then(function(template) {
var template = angular.element(template);
var elements = [];
console.log(scope);
attrs.$observe("modules", function(val) {
scope.modules.forEach(function(module) {
module.open = false;
});
});
For some reason, the first time I navigate to the view that contains this directive everything works fine, but from the second time onwards I always get a Cannot call method 'forEach' of undefined error. I read through some similar questions on SO which mentioned interpolated attributes not being instantly available on the link function, so they reccomended the use of $observe. I even added a console.log(scope) and the modules property shows up on the object. Any idea of what might be causing this?
It should be rather $scope.$watch rather that attrs.$observe as $observe required # in isolated scope of directive & on html it would be modules="{{module}}", For big object DOM will get messed up so I'd prefer you to use $watch which make sense
Code
scope.$watch("modules", function(newVal) {
if(newVal){
scope.modules.forEach(function(module) {
module.open = false;
});
}
});

Angular passing parameter to directive

I m trying to pass some information through a view in a directive and it doesnt seem to work at all. In fact, even if I bind the scope, I have the string value writter :
Here's the directive
angular.module('app')
.directive('anomalieTableau', function () {
var controller = ['$scope', '$attrs', 'srv_traitementsSite', function ($scope, $attrs, srv_traitementsSite) {
$scope.anomalies = [];
var idSite = $attrs.idsite;
alert(idSite);
var idAgence = $attrs.idagence;
var dateDebut = $attrs.datedebut;
var dateFin = $attrs.datefin;
var promise = undefined;
if (idAgence && idSite) {
promise = srv_traitementsSite.TraitementSiteAgenceAnomalie(idSite, idAgence, dateDebut, dateFin);
}
promise.then(function (response) {
$scope.anomalies = response.data;
}, function (response) {
});
}];
return {
restrict: 'EAC',
templateUrl: 'tpl/directive/AnomalieTableauDirective.html',
controller: controller,
scope: {
idsite: '=',
idagence: '=',
datedebut: '=',
datefin: '='
}
};
});
Here's the HTML call :
<div anomalie-tableau idsite="site._id" idagence="AgenceId" datedebut="dateDebutStats"
datefin="dateFinStats" class="col-md-12"></div>
And this is the alert result in the directive :
site._id
Instead of :
123456789
EDIT : Changing site._id by {{site._id}} in the attribute directive's call, it changes nothing and gives me this error :
Syntax Error: Token 'site._id' is unexpected, expecting [:] at column
3 of the expression [{{site._id}}] starting at [site._id}}].
What am I doing wrong ?
Attributes are always strings. So you will need to interpolate the value ({{site._id}}) and then possibly convert the string ($attrs.idsite) to your desired type.
In respect to your scope-settings: You then have to use $scope instead of $attrs (and dont need the interpolation) since angular will copy those values to your scope. If you use = it will be a two-way-binding and you dont need to interpolate the values in your directive-attribute.
If you for some reason need to use $attrs you can do the interpolation yourself. Remove the scope-settings, use idsite="{{...}}" and inject the $interpolation Service into your directive.
Then use $interpolate($attrs.idsite)($scope.$parent) to get the value (string).
http://plnkr.co/edit/zhBXyiz82EdmysLzeBNL?p=preview
(note that in my plnkr I used # in the scope-settings. You can remove that or leave it. With the # angular will execute the interpolation for you and store the value in the $scope object of your directive.)

Angular JS $interpolate query

I newbie to Angular JS and way trying to understand directive functionality which is written as below
function mydirective($interpolate, $compile) {
return {
restrict: 'E',
scope: {
mycontent: '=',
myurls: '=',
mydata: '#'
},
replace: true,
template: '<div ng-bind-html="html"></div>',
link: function ($scope, element, attrs) {
$scope.$watch('mycontent', function (value) {
var html = $interpolate(value)($scope);
element.html(html);
$compile(element.contents())($scope);
});
}
}
}
I am not able to understand following from above.
1) What does this $interpolate(value)($scope) do ? what is this second argument $scope.
2) What is this $compile function doing ?
3) div ng-bind-html="html" in template what does it do ?
What does this $interpolate(value)($scope) do?
Gets the literal value of the value variable
what is this second argument $scope.
It provides the context for data-binding
it('should interpolate with undefined context', inject(
function($interpolate)
{
expect($interpolate("Hello, world!{{bloop}}")()).toBe("Hello, world!")
}
));
What is this $compile function doing?
It converts the string into Angular markup
div ng-bind-html="html" in template what does it do?
It compiles the HTML string within $scope.html and adds it to the markup
References
AngularJS Guide: Scope Watch Depths
AngularJS Source: interpolateSpec.js
AngularJS Expression Security Internals
Difference in $interpolate between AngularJS 1.0 and 1.2

Angular JS: How do I set a property on directive local scope that i can use in the template?

I want to access a variable in this case map in the directive without having to predefine it like setting it as an attr of the directrive map="someValue". And also i dont want to use scope.$apply because i actually only want the variable in the isolated scope of the directive. Is this even possible ?
What is the best practice here? Basically my directive needs to do both. Access the parent scope and have its own scope which with i can build the template with.
Thank you everybody.
Here my Js code:
.directive('myFilter', function() {
return {
restrict: 'E',
scope: {
source: '=source',
key: '=key',
},
link: function(scope, element, attrs) {
scope.$on('dataLoaded', function(e) {
scope.map = {};
angular.forEach(scope.source, function(paramObj) {
if (!scope.map[paramObj[scope.key]]) {
var newEntry = {
value: paramObj[scope.key],
isChecked: false
}
scope.map[paramObj[scope.key]] = newEntry;
}
});
});
}
}
});
and my html:
<my-filter source="metaPara.filteredParameters" key="'value'">
<table class="table table-borered">
<tr data-ng-repeat="item in map">
<td>{{item.value}}</td>
</tr>
</table>
</my-filter>
You might want to refer to the Angular documentation for directives, again.
If you want an isolate-scope (a scope which has no access to ancestors), then use
scope : { /* ... */ }
otherwise, if you want a unique scope, which does have access to ancestors, use
scope : true
Then, you can put your HTML-modifying or event-listening (that doesn't rely on ng-click or something else Angular already covers) in
link : function (scope, el, attrs, controller) { }
...and you can put all of your regular implementation inside of
controller : ["$scope", function ($scope) {
var myController = this;
myController.property = "12";
}],
controllerAs : "myController"
So that in your template you can say:
<span>{{ myController.property }}</span>
You can also use a pre-registered controller, which you call by name:
controller : "mySimpleController",
controllerAs : "myController"
Also, rather than using $scope.$apply, I'd recommend using $timeout (has to be injected).
The difference is that $scope.$apply will only work at certain points -- if you're already inside of a digest cycle, it will throw an error, and not update anything.
$timeout( ) sets the updates to happen during the next update-cycle.
Ideally, you should know whether or not you need an $apply or not, and be able to guarantee that you're only using it in one spot, per update/digest, but $timeout will save you from those points where you aren't necessarily sure.

Categories

Resources