AngularJS - When should we avoid using {{}}? [duplicate] - javascript

In angular sometimes i have seen curly brackets but some times not.i search a lot but i couldn't find correct question
with curly bracket
ng-src="{{imageSrc}}
without curly bracket
ng-hide="imageSrc"
what i'm asking is why we cannot write ng-hide as
ng-hide="{{imageSrc}} // doesn't work anyway
why there is 2 different syntax for src and hide?

It simply depends on the way the directive you are using is "declared".
If the directive has the following declaration:
scope:{
ngHide: '='
}
then, you don't have to use double mustaches because the directive expects an object
If the directive is declared like the following :
scope:{
ngMin:'#'
}
then, it expects a value. If your value comes from a javascript variable, then you have to use curly braces to interpolate the string contained into your variable.
EDIT :
It has been a long time since I read angular source code.
I haven't found any source code to prove my point :
ngController which expects a string is declared like the following
var ngControllerDirective = [function() {
return {
restrict: 'A',
scope: true,
controller: '#',
priority: 500
};
}];
https://github.com/angular/angular.js/blob/master/src/ng/directive/ngController.js#L3
ngMaxLength
var maxlengthDirective = function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
var maxlength = -1;
attr.$observe('maxlength', function(value) {
var intVal = toInt(value);
maxlength = isNaN(intVal) ? -1 : intVal;
ctrl.$validate();
});
ctrl.$validators.maxlength = function(modelValue, viewValue) {
return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
};
}
};
};
https://github.com/angular/angular.js/blob/master/src/ng/directive/validators.js#L186

Beacuse they mean two different things.
When you use this:
<span data-ng-bind="test">
This means that angular will go to the scope and get value from there with test as key. So value will be $scope.test. But attribte value will be "test"
When you use
ng-src="{{imageSrc}}
then value will be evaluated and placed to the attribute. So value willbe $scope.imageSrc and attribute value will be $scope.imageSrc.
But. Not all tags can wait for evaluation. They think that value {{}} is correct and will not be changed. This cause to bad request. To fix this problem ng-src was created.

You can't write because both have different meaning see this link
,It's all about expression and template argument.
https://docs.angularjs.org/api/ng/directive/ngSrc
ng-src=template
You can find it in argument
https://docs.angularjs.org/api/ng/directive/ngHide
ng-hide=expression
You can also find it in argument

Related

AngularJS ([$parse:syntax] Syntax Error: Token ':' is an unexpected token at) when value is URL

I have created a directive as below:
angular.module('mymodule')
.directive('httpsimg', function () {
return {
restrict: 'E', //E = element, A = attribute, C = class, M = comment
//# reads the attribute value, = provides two-way binding, & works with functions
scope: {
style: '#',
width: '=',
height: '=',
'src': '='
},
template: '<img src="{{src}}" width="{{width}}" height="{{height}}" style="{{style}}" />',
link: function ($scope, element, attrs) {
var srcparts = $scope.src.replace('http','https');
$scope.src=srcparts;
} //DOM manipulation
}
});
then I use that in my Html code as below:
<httpsimg src="http://www.mytest.com/imgdir/logo.png" width="50" />
if inside my directive change src to one way binding(#) as below:
scope: {
style: '#',
width: '=',
height: '=',
'src': '#'
}
it works fine, I mean I can get value and there is no error. But if I change # to = to have two way binding I see the error
Also I found out that the issue is because of Url so if I change the url to a simple world without dot and slash it works fine.
= two way binding is to bind expressions, e.g. variables:
<httpsimg src="foo">
Where foo is a scope variable. https://… is most certainly not a scope variable, and is invalid syntax for a variable or expression. If you use = binding, you need to supply a literal value as expression literal:
<httpsimg src="'http://www.mytest.com/imgdir/logo.png'">
^ ^
Just as you would write a string literal in Javascript. Perhaps to illustrate it even better:
<httpsimg src="'http://' + 'www.mytest.com/imgdir/logo.png'">
Since you're not binding a variable expression, there's no point in using two way binding. If you just want to pass a literal value, that's exactly what # is for.

angular when to use curly brackets

In angular sometimes i have seen curly brackets but some times not.i search a lot but i couldn't find correct question
with curly bracket
ng-src="{{imageSrc}}
without curly bracket
ng-hide="imageSrc"
what i'm asking is why we cannot write ng-hide as
ng-hide="{{imageSrc}} // doesn't work anyway
why there is 2 different syntax for src and hide?
It simply depends on the way the directive you are using is "declared".
If the directive has the following declaration:
scope:{
ngHide: '='
}
then, you don't have to use double mustaches because the directive expects an object
If the directive is declared like the following :
scope:{
ngMin:'#'
}
then, it expects a value. If your value comes from a javascript variable, then you have to use curly braces to interpolate the string contained into your variable.
EDIT :
It has been a long time since I read angular source code.
I haven't found any source code to prove my point :
ngController which expects a string is declared like the following
var ngControllerDirective = [function() {
return {
restrict: 'A',
scope: true,
controller: '#',
priority: 500
};
}];
https://github.com/angular/angular.js/blob/master/src/ng/directive/ngController.js#L3
ngMaxLength
var maxlengthDirective = function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
var maxlength = -1;
attr.$observe('maxlength', function(value) {
var intVal = toInt(value);
maxlength = isNaN(intVal) ? -1 : intVal;
ctrl.$validate();
});
ctrl.$validators.maxlength = function(modelValue, viewValue) {
return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
};
}
};
};
https://github.com/angular/angular.js/blob/master/src/ng/directive/validators.js#L186
Beacuse they mean two different things.
When you use this:
<span data-ng-bind="test">
This means that angular will go to the scope and get value from there with test as key. So value will be $scope.test. But attribte value will be "test"
When you use
ng-src="{{imageSrc}}
then value will be evaluated and placed to the attribute. So value willbe $scope.imageSrc and attribute value will be $scope.imageSrc.
But. Not all tags can wait for evaluation. They think that value {{}} is correct and will not be changed. This cause to bad request. To fix this problem ng-src was created.
You can't write because both have different meaning see this link
,It's all about expression and template argument.
https://docs.angularjs.org/api/ng/directive/ngSrc
ng-src=template
You can find it in argument
https://docs.angularjs.org/api/ng/directive/ngHide
ng-hide=expression
You can also find it in argument

Passing format string to a directive and interpolating it

I want to pass a format string to a directive and make the directive interpolate it with an object. The problem is that if I use curly brackets Angular tries to interpolate the string before the directive is even created. If I escape the curly brackets, Angular isn't showing the values of the object (the string isn't interpolated correctly).
How can I pass a format string to a directive?
This is my demo code template:
<div test-directive item-text="{{ x }} - {{ y }}"></div>
Angular app/directive:
var app = angular.module('plunker', ["test-directive"]);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
var dir = angular.module('test-directive', []);
dir.directive("testDirective", ['$interpolate', function($interpolate) {
return {
template: "<div>{{ text }}</div>",
link: function ($scope, element, attrs) {
var obj = {
x: 6,
y: 9
};
$scope.text = $interpolate(attrs.itemText)( obj );
}
}
}]);
Demo: http://plnkr.co/edit/vUVVuLVBptEmUcv3y7o4?p=preview
Edit:
Applying #Lucas's annswer to my original problem isn't solving the issue. For some reason the attr gets wiped, even if I'm not erasing it anywhere.
Please check the line 301: http://plnkr.co/edit/6bjW35D3W1dTGQz9kNSn?p=preview
Note that itemLabel isn't changed anywhere.
The way to do it is to change it as
$scope.text = $interpolate(element.attr(attrs.$attr.itemText))( obj );
http://plnkr.co/edit/ctPalH7Szqqnpit5t0On?p=preview
Not sure what the actual usecase is, but from your example, you don't seem to need interpolation (so you shouldn't use it and avoid the overhead).
What you (seem to) need, is to evaluate a expression in some context (e.g. obj).
One way of doing it, is to use Scope's $eval() method; e.g.:
<div test-directive item-text="x+' - '+y" ...
...
link: function postLink(scope, elem, attrs) {
var obj = {x: 6, y: 9};
scope.text = scope.$eval(attrs.itemText, obl);
}
Updated Plnkr
EDIT: Admitedly, this beats the purpose of simplicity, so here is another approach using a helper service that replaces the start-/end-symbols and interpolates: Demo

Angular directive: return object from collection

I try to implement a custom directive that list all available plans and allow user to choose one.
When plan is selected, the parent scope must be updated with selected object (it is linked with two way binding)
It should behave exactly the same as angular ng-options does, but I have hard time fighting the Javascript object replacement.
What I have right now works (with some clutter removed):
In HTML:
<choose-plan ng-model='plan' plans='plans' choose-plan-title='Premium plans' />
In Controller:
$scope.plan = {}
Plans.get (resource) ->
$scope.plans = resource.plans
return
It does not work with $scope.plan = undefined obviously, but I look for the solution that does.
In JS (Coffeescript) directive:
angular.module('tv-dashboard').directive 'choosePlan', [
'lodash'
(lodash) ->
'use strict'
restrict: 'E'
scope:
plan: '=ngModel'
plan_collection: '=plans'
title: '#choosePlanTitle'
link: (scope, element, attrs) ->
# HACK two way binding does not replace the value. Investigate
scope.choosePlan = (available_plan) ->
# scope.plan = available_plan # Does NOT update the parent scope binded ng-model
angular.copy available_plan, scope.plan
return
scope.isSelected = (available_plan) ->
return unless available_plan?
available_plan.id == scope.plan.id
presentPlans = (collection) ->
angular.copy(collection).map (resource_plan) ->
price_parts = resource_plan.interval_price.split '.'
resource_plan['integer_price'] = price_parts[0]
resource_plan['decimal_price'] = price_parts[1]
resource_plan
chooseDefaultPlan = (collection) ->
scope.choosePlan lodash.last collection
unWatchCollection = scope.$watch 'plan_collection', (collection) ->
return unless collection? && collection.length > 0
scope.plans = presentPlans collection
chooseDefaultPlan scope.plans
unWatchCollection()
return
return
templateUrl: 'form/choose_plan.html'
]
But if you take a look on the isSelected function, you notice that i have to compare objects using the id field. Comparison by == (=== in JS) does not return true.
Is there a way I can replace the parent scope plan without dancing around with angular.copy available_plan, scope.plan and forced to use the id field comparison?
You should use require: 'ngModel' to inject the model attached to the element or its parent element on which the directive is bound to. Here is a demo.
One way to solve this is to share an object and write into its properties
$scope.plan = {
// value: set in directive
}
scope.choosePlan = (available_plan) ->
scope.plan.value = available_plan
return
scope.isSelected = (available_plan) ->
return unless available_plan?
available_plan == scope.plan.value
Your code as jsfiddle: http://jsfiddle.net/ht6tmhfu/1/
Fixed code as jsfiddle: http://jsfiddle.net/ht6tmhfu/3/

AngularJS: 2-way binding of custom string serialization

Short Question:
How to create a <input type="text"> which contains a custom-format string serialization of an object in a way that editing the string updates the model and vice versa?
I think AngularJS’ directives are the way to go, but i can’t get it pinned down.
Long Question:
Prequel
I have a object which is my application’s “master model”. it can be serialized to a string of a specific format:
it has 2-3 attributes, whose serializations are joined by “;” (no trailing “;” if the third is missing)
attributes 2 and 3 are lists of objects, and serialized by joining those with “,”.
the serialization of the objects is just one of their string attributes, or two ow them with “x” between.
so i have a constructor (accepting a spec string), and a toString function. Following; the latter for clarity:
World.prototype.toString = function() {
var spec = [];
spec[0] = this.version;
spec[1] = this.layers.map(function(layer) {
var c = (layer.c > 1) ? layer.c + 'x' : '';
return c + layer.id; //e.g. 'T' or '2xT'
}).join(',');
//spec[2] in python: ','.join(option.id for option in options if option.checked)
var options = this.options.filter(function(option) {
return option.checked;
});
if (options.length > 0)
spec[2] = options.map(function(option) {
return option.id;
}).join(',');
return spec.join(';');
};
The directive i tried to use looks thusly, but the $watch only fires once.
angular.module('layersApp', []).directive('spec', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch('world', function(val) {
element.val(val.toString());
console.log('object updated', element.val());
}, true);
element.blur(function(e) {
scope.world = new World(element.val());
});
}
};
});
Actual long question
What i want is an easy way to make this work,
<input type="text" data-ng-model="theWorld" spec>
where spec is the custom directive shown above, setting up the two-way binding
Outlook
it would be awesome if this could result in a generic “serialization” directive used like that:
<input type="text" data-serialization="string2Foo, foo2String" data-ng-model="foo">
Which would look up the object foo, and the functions string2Foo and foo2String to setup custom (de)serialization.
I think you can use of $parsers and $filters of ngModel controller.
Here is the simplest example of doing it.
http://plnkr.co/edit/13PJN2
It should be easy to add validation, too.
I tried to make it accept custom serializer from parent scope, but failed to do so. Not sure about it.

Categories

Resources