Simple AngularJS directive with attributes is not working - angularjs

I have created simple directive with a few parameters passed, but my parameters are not passed to Template from Usage code. (they are not displayed on page, no error is displayed too). I also used '#' instead of '=' but there was no real improvement.
I just want to display my attribute parameters in my directive code:).
Thanks for help.
Usage code:
<fine-faulty-directive fineCount='111' finePercentage='11'
faultCount='222' faultPercentage='11'></fine-faulty-directive>
Directive code:
Application.Directives.directive('fineFaultyDirective', function () {
return {
restrict: 'E',
templateUrl: 'src/Dashboard/Views/fine_faulty.html',
//replace: true,
scope: {
fineCount: '=',
finePercentage: '=',
fauntCount: '=',
faultPercentage: '='
},
link: function (scope, elem, attrs) {
console.log(scope);
}
};
});
Simple templateURL:
{{faultPercentage}}% - {{faultCount}}

The attributes fineCount, finePercentage, faultCount, faultPercentage are normalized names (such as directive's name), so if you want to set it to the element, each attribute name must be fine-count, fine-percentage, fault-count, fault-percentage...
I hope I helped.

Because of
<fine-faulty-directive fineCount='111' finePercentage='11'
faultCount='222' faultPercentage='11'></fine-faulty-directive>
AngularJS will translate finePercentage to finepercentage and fineCount to finecount
to avoid such circumstances, use fine-percentage instead of finePercentage
and fine-count instead of fineCount
NOTE: all attribute which are camel cases in attribute should be separated with - or : so that you can get them camel case in your JS code.

Related

Nested Directives and NgModel

I feel like I'm missing a fundamental concept of Angular directives.
Referring to this Plnkr: http://plnkr.co/edit/WWp9lB6OvxHL8gyBSU5b?p=preview
I have a model of:
{
message: string,
value: number
}
And I have an itemEditor directive to edit that model:
.directive('itemEditor', function() {
return {
replace: true,
templateUrl: 'item.editor.html',
require: 'ngModel',
model: {
item: '=ngModel'
}
};
})
But I want to delegate the editing of value to a custom control:
.directive('valuePicker', function() {
return {
replace: true, // comment this to make it work
templateUrl: 'value.picker.html',
require: 'ngModel',
scope: {
ngModel: '='
},
controller: Controller
};
function Controller($scope, Values) {
$scope.values = Values;
console.log({scope:$scope});
}
})
At current, this code gives the error:
Error: $compile:multidir
Multiple Directive Resource Contention
Commenting the replace: true will allow this code to work. However, I lose the styling instructions from the parent template. I.E: the class form-control is not merged onto the select element.
What is the angular way to make this work?
You are calling value-picker twice here
<value-picker class="form-control" style="width:100%" name="item" value-picker ng-model="item.value"></value-picker>
The value-picker element contains value-picker attribute as well, both being treated as directive which in conflict causing multiple directive error. Remove the attribute value-picker, either call it as element or attribute. Or you can restrict the directive to a specific declaration.
Also remove ng-model from select element of value.picker.html template, which is causing another error:
Multiple directives [ngModel, ngModel] asking for 'ngModel'
So replace: true replaces and appends the current directive attributes to the root level of template element (in your case its select)
Updated Plnkr

What is the difference between using "require:ngController" and "controller:" in a directive?

I'm switching from a directive created using:
return {
restrict: 'E',
templateUrl: '/src/templates/noise/swatches.html',
link: link,
controller: "swatchesController"
};
and
<swatches-directive></swatches-directive>
to using:
return {
restrict: 'E',
templateUrl: '/src/templates/noise/swatches.html',
link: link,
require: "ngController"
};
and
<swatches-directive ng-controller="swatchesController"></swatches-directive>
This seems to have unanticipated side-effects on existing watches belonging to other directives against scope variables that the swatches-directive assigns to. From what I understand, the new way introduces a new scope, so assigning watched variables to the parent scope seems like it should work, but those watches refuse to trigger.
Are there fundamental differences between the two methods used above?
Few points to note:
You should use the controllerAs property to segregate the
controllers. It's considered as a best practice. You can read it here
Ideally, you should provide the controller within the directive itself. The way you're doing it causes spagetti scopes. Keep the directive scope separate from parent scopes. If you want you can pass the required dependencies to a directive.
return {
restrict: 'E',
templateUrl: '/src/templates/noise/swatches.html',
link: link,
require: "ngDirective", //Get the controller of that directive
link : function(scope, element, attributes, ngController){
//With require, you say: get the controller of that directive. You can
//then use it as 4th parameter (ngController)
}
};
Note: you can pass multiple controllers
return {
restrict: 'E',
templateUrl: '/src/templates/noise/swatches.html',
link: link,
require: ["ngDirective1", "ngDirective2"], //Get the controller of that directive
link : function(scope, element, attributes, controllers){
var ctrl1 = controllers[0];
var ctrl2 = controllers[1];
//Require can be a string or, as this example, an array.
}
};
The directive passed inside require must be in your directive.
If it is not, you can say: find it on container elements with ^:
require : '^ngDirective'

Using Skobbler in AngularJS directive: 'Map container is already initialized.'

Using a directive to render the Skobbler leaflet:
angular.module('app.directives').directive('skm', function()
{
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div></div>',
link: function(scope, element, attrs) {
var map = L.skobbler.map('skm', scope.skm);
// ....
}
}
});
This directive is used on different routes/partials in a similar way:
<skm id="skm"></skm>
Version info:
Skobbler 2.0
AngularJS 1.2.16
When moving from page 1 to page 2, no problem. However, going back to page 1:
Error: Map container is already initialized.
I've tried storing the map object and using map.remove(), which removes the error but stops the map from rendering.
FIXED: See my answer below. (Use unique ID's in each partial and grab those)
Fixed: Simply put a unique ID in each partial and grab the ID in the directive, then create the leaflet with this ID.
Partial 1:
<skm id="skm1"></skm>
Partial 2:
<skm id="skm2"></skm>
Directive:
angular.module('app.directives').directive('skm', function()
{
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div></div>',
link: function(scope, element, attrs) {
var map = L.skobbler.map(attrs.id, scope.skm);
// ....
}
}
});
What didn't work:
replacing the outerHTML with original HTML on directive $destroy (directive didn't play well)
map.remove() (Skobbler didn't care)
Shouting profanities at the Skobbler library (Skobbler didn't care)
Skobblerjs is a "thin" fork of leafletjs so (most likely) the error comes from the leaflet logic and not the skobbler logic.
There are some other mentions of this error, see of any of following are of help to you:
https://groups.google.com/forum/#!topic/leaflet-js/xDNcNBAZq8o
http://forums.enyojs.com/discussion/540/leaflet-maps
If not - then include a jsfiddle or plunker example so that we're able to debug

ANGULAR: Not able to pass string containing {{}} in directive scope using # after upgrading to angular version to 1.2.3

I'm passing "/foo/bar/{{value}}" as a string using # to the directive so I can use the interpolate method to construct hrefs for a dropdown. I think angular is looking for value when compiling instead of passing the whole thing as a string. This was working fine in the angular v1.2.0.
The directive code goes like,
return {
templateUrl: '/views/directives/directive-name.html',
restrict: 'E',
replace: true,
scope: {
title: '#',
optHref: '#'
},
link: link: function postLink(scope, element, attrs) {
var hrefFormatter;
hrefFormatter = $interpolate(attrs.optHref);
scope.getHref = function(value, label) {
return hrefFormatter({ value: value, label: label });
};
}
Invoked like,
<directive-name
title ="name"
opt-href="/foo/bar/{{value}}" <
</directive-name>
Appreciate any pointers on what might have changed in angular to cause this or other pointers.
Instead of:
opt-href="/foo/bar/{{value}}"
Try this:
opt-href="'/foo/bar/{{value}}'"
Mind the additional '.
Thanks Michal! Used a controller function before the link to preserve the attrs.optHref in the scope. Something like,
controller: function ($scope, $element, $attrs) {
$scope.optHref = $attrs.optHref;
}
In the controller function the {{value}} was already interpolated in scope but existed in attrs. But in the link fn {{value}} was interpolated in attrs as well- so preserved it in scope and used scope.optHrefin the link fn.

angularjs directive with dynamic templates and string interpolation

The idea is to replace the directive element with the dynamic template which refers to interpolated strings.
If I use element.html() in my directive then the strings are interpolated fine but this leaves the original custom directive html element.
If I use element.replaceWith() then strings are not interpolated. I guess it has related to scope but can't figure out what's wrong.
Plunker: http://plnkr.co/edit/HyBP9d?p=preview
UPDATE
Found the solution. Using element.replaceWith($compile(html)(scope)); works.
Updated plunker: http://plnkr.co/edit/HyBP9d?p=preview
Not sure what objective is regarding replaceWIth. The problem is likely that when you replace an elememnt, you replace all events and data bound to it. This would include the angular scope for the element.
For demo provided could do it like this:
app.directive('status', function($compile) {
var linker = function(scope, element, attrs) {
element.contents().wrap('<h'+attrs.value+'>')
};
return {
restrict: 'E',
replace: true,
template:'<div>{{value}}</div>',
transclude: true,
link: linker,
scope: {
value: '='
}
};
});
DEMO:http://plnkr.co/edit/QDxIwE?p=preview

Resources