I'm reviewing this tutorial which uses a tooltip directive like this:
CASE A: No surrounding []
<p tooltip="Tooltip from text">Tooltip from text</p>
CASE B: Surrounding []
<p [tooltip]="template">Tooltip from TemplateRef</p>
I just want to make sure I understand this correctly.
If we use the tooltip directive without brackets, then the text string gets passed to the directive (Tooltip from text").
When we surround the tooltip with brackets, then the template property, which should be available on the component view (The view using the directive), gets passed in?
If we use the tooltip directive without brackets, then the text string gets passed to the directive
Yes Exactly.
For the second case, [tooltip]="template"
[prop] is for object binding to properties (#Input() of an Angular component or directive or a property of a DOM element) of the angular Component.
Related
I am an absolute beginner with Angular 2 and I have the following dount related the correct syntax of the ngStyle directive.
I have this example (that works fine):
<p [ngStyle]="{backgroundColor: getColor()}">Server with ID {{ serverID }} is {{ getServerStatus() }}</p>
I know that, in this case, the ngStyle directive is adding something like to:
style="background-color: green;"
at my HTML paragraph.
My doubt is related the correct meaning of this syntax. Why is it:
[ngStyle]="{backgroundColor: getColor()}"
and not
ngStyle="{backgroundColor: getColor()}"
Why is it into the [...]? What it exactly means?
It's called property binding. With the brackets the compiler tries to evaluate the expression. Without it, you are just passing a string.
So with the brackets, you are passing a javascript object:
{
backgroundColor: getColor()
}
Whereby the compiler will call the getColor() method from the component to get the right color.
On the other hand, and going off topic here, but if you want to pass a string while using brackets, you should wrap them in single quotes:
<div [property]="'hiii'"></div>
Angular 2 has 3 types of directives:
Attribute directives.
Structural directives.
Components.
ngStyle is an attribute directive. And all attribute directive to which we need to pass/assign values are written inside square brackets.
The built-in NgStyle directive in the Template Syntax guide, for example, can change several element styles at the same time.
I'm wondering if there is any way I can get the original {{expression}} after angular has complied the directives and interpolated the expressions. For instance if there is a text e.g. Hi, Filip and the user clicks on it, I want to be able to show a pop-up with Hi, {{name}}.
Now, one way I thought of doing that is by analysing the DOM before angular (e.g. during run) and then saving the expressions as additional attributes to the parent element. However, I run into various problems with that (e.g. if parent has other child elements and they are removed, e.g. with ng-if, then I can't reliably know which expression belongs to which text node).
Since Angular keeps watchers for these expressions, it must have a reference to the text nodes they are applied on. Is there any way I could access those?
The second question is, can I somehow get the original element of ng-repeat (before it was compiled and transcluded), for the similar purpose (allowing the user to modify it on-the-fly).
I want to avoid introducing new directives as this is meant to work on existing angular applications.
I'm not concerned about performance or security (i.e. this is not for production applications but rather for prototyping/debugging).
Use factories to supply to your html expressions with reusable logic. Assumes you're using controller as syntax so you can the controller's scope as this in your view.
// factory
function() {
return 'bob';
}];
// in your controller
['somefactory', function(factory) {
this.factoryString = factory.toString(); // => "function() { return 'bob'; }"
this.factory = factory;
}];
// view
<div>hi {{this.factory()}} you were made with {{this.factoryString}}</div>
// interpolated
<div>hi bob, you were made with function() { return 'bob' }</div>
I didn't test any of that though.
Let's say I have this code
table(my-attr="value")
...complex component Jade...
and I would like render that my-attr base on property delivered into component. Since v-if works on whole element I cannot do something like
table(my-attr="value", v-if="myProp")
table(v-else)
because I would have to duplicate all the code inside table.
How can I achieve that?
You can use v-bind or interpolate the value directly with {{}}
// (sorry, no jade)
<table v-bind:attribute1="someMethod" attribute2="{{anotherMethod}}">
Now someMethod and anotherMethod should be data, computed properties, or methods of your component, and should return either the attribute's desired value or false. In the latter case, the attribute will not be added to the element at all.
Update: Note that interpolations in attributes have been removed in Vue 2
I have a view as follwing,
<li ng-repeat="img in people.images">
<img ng-src="{{img}}" ng-click="setImage(img)">
</li>
Its working. But my doubt here is, the attribute ng-click should have been set the img inside doubly braces to be executed as in ng-src as ng-click="setImage({{img}})". Its shown below,
<li ng-repeat="img in people.images">
<img ng-src="{{img}}" ng-click="setImage({{img}})">
</li>
But the later is not working.
How the expression is parsed here and how does ng-click vary from ng-src?
I really confused here. Any help highly appreciated.
The difference is the following: Some of the directives use expressions, some don't. This is how they "vary" from each other. You may use the angular documentation to see, which directive use expressions and which don't.
Example for ng-click: https://docs.angularjs.org/api/ng/directive/ngClick It uses an expression:
Example for ng-src: https://docs.angularjs.org/api/ng/directive/ngSrc It doesn't use an expression:
ngClick lets you define a JavaScript-like expression, while ngSource lets you define an usual String, where you also may define an expression, inside the double braces. According to the AngularJS documentation, everything inside of the double braces is also an expression: https://docs.angularjs.org/guide/expression.
I think that the "need" to have the difference between "Expression" for "ngClick" and "String" for "ngSrc" come from the original attributes they derive from: onclick and src.
The attribute src is used to reference a resource as String, while using the attribute onclick, you may use javascript. The usual case is to call a function in onclick, i.e. onclick="doSomething();". I assume that angularjs uses these attributes as a base, this is why the workflow is similar. ngClick lets you use usual javascript expressions, while ngSrc lets you use a String and add a {{}} expression if you need to.
The benefit of using ngSrc with a variable, for example ngSrc="http://localhost/{{myRessource}}" is, that it is evaluated only after $scope.myRessource is set, not before. It uses the observer pattern to render the view as soon as the variable is set, as far as I know.
According to your example, the second one which doesn't work:
<li ng-repeat="img in people.images">
<img ng-src="{{img}}" ng-click="setImage({{img}})">
</li>
It doesn't work because of a syntax error. ngClick uses javascript expression, and the syntax of setImage({{img}}) is not correct - you would not use double brackets around variables either in a javascript function.
Further: $interpolate and $parse
Like stated above, some directives, like ngClick use expressions, while other directives like ngSrc use plain Strings combined with {{}}. The difference between the two on angularJs side is the following:
a directive using an expression is evaluated by angularjs using $parse (Read here from heading "Text and attribute bindings")
a directive using a String is evaluated by angularjs using $interpolate (Read here from heading "Context")
Example:
$scope.varOne = "asdasdasd";
var test1 = $interpolate("http://localhost/{{varOne}}");
$scope.displayOne = test1($scope);
At first, we declare a variable varOne. $interpolate returns a function which needs to be called with the scope, we will bind this to the local variable test1. Then we will call test1 using the $scope. As a result, we will have on displayOne the String http://localhost/asdasdasd.
var test2 = $parse("1+5-3");
$scope.displayTwo = test2($scope);
$parse is evaluating javascript expressions, as a test we may use the calculation "1+5-3". After calling test2, the result will be 3.
I prepared a fiddle, so you can see this: http://jsfiddle.net/wSN54/6/
You may also try using brackets in the $parse evaluation (the same that happens in your second example):
$scope.varTwo = 2;
$scope.varThree = 3;
var test2 = $parse("{{varTwo + varThree}}");
$scope.displayTwo = test2($scope);
This must resolve in an error, because the double braces are not used in usual javascript expression:
Check it out fiddle: http://jsfiddle.net/wSN54/8/
As far as I know, if you put img param inside double brackets, when the page is rendered, you will notice that the parameter inside setImage function is a value of img. If you put img without brackets you will have rendered "setImage(img)" and in both case that will work.
Example:
imagine that you have one item in people.images model, and its for example "example.jpg". If you use brackets in setImage function, when the page is rendered you will see the attribute "ng-click=setImage('example.jpg')", in another approach you will have "ng-click=setImage(img)". In first case AngularJS don't need to parse value from your parameter, because you already put 'example.jpg' like a parameter, in another case AngularJS will parse value from img parameter/item before your function is executed.
TLDR; ng-src $interpolate the argument as template where ng-click $parse the expression
Long version
Copied from angular official docs
param=ngSrc, type=template
param=ngRepeat, type=repeat_expression
details refer to https://docs.angularjs.org/api/ng/directive/ngSrc and https://docs.angularjs.org/api/ng/directive/ngRepeat
What is expression - https://docs.angularjs.org/guide/expression
What is template(markup) - https://docs.angularjs.org/api/ng/service/$interpolate
According to the documentation a template can be a function which takes two parameters, an element and attributes and returns a string value representing the template. It replaces the current element with the contents of the HTML. The replacement process migrates all of the attributes and classes from the old element to the new one.
The compile function deals with transforming the template DOM. It takes three parameters, an element, attributes and transclude function. The transclude parameter has been deprecated. It returns a link function.
It appears that a template and a compile functions are very similar and can achieve the same goal. The template function defines a template and compile function modifies the template DOM. However, it can be done in the template function itself. I can't see why modify the template DOM outside the template function. And vice-versa if the DOM can be modified in the compile function then what's the need for a template function?
The compilation function can be used to change the DOM before the resulting template function is bound to the scope.
Consider the following example:
<div my-directive></div>
You can use the compile function to change the template DOM like this:
app.directive('myDirective', function(){
return {
// Compile function acts on template DOM
// This happens before it is bound to the scope, so that is why no scope
// is injected
compile: function(tElem, tAttrs){
// This will change the markup before it is passed to the link function
// and the "another-directive" directive will also be processed by Angular
tElem.append('<div another-directive></div>');
// Link function acts on instance, not on template and is passed the scope
// to generate a dynamic view
return function(scope, iElem, iAttrs){
// When trying to add the same markup here, Angular will no longer
// process the "another-directive" directive since the compilation is
// already done and we're merely linking with the scope here
iElem.append('<div another-directive></div>');
}
}
}
});
So you can use the compile function to change the template DOM to whatever you like if your directive requires it.
In most cases tElem and iElem will be the same DOM element, but sometimes it can be different if a directive clones the template to stamp out multiple copies (cf. ngRepeat).
Behind the scenes, Angular uses a 2-way rendering process (compile + link) to stamp out copies of a compiled piece of DOM, to prevent Angular from having to process (= parse directives) the same DOM over and over again for each instance in case the directive stamps out multiple clones resulting in much better performance.
Hope that helps!
ADDED AFTER COMMENT:
The difference between a template and compile function:
Template function
{
template: function(tElem, tAttrs){
// Generate string content that will be used by the template
// function to replace the innerHTML with or replace the
// complete markup with in case of 'replace:true'
return 'string to use as template';
}
}
Compile function
{
compile: function(tElem, tAttrs){
// Manipulate DOM of the element yourself
// and return linking function
return linkFn(){};
}
}
The template function is called before the compile function is called.
Although they can perform almost identical stuff and share the same 'signature', the key difference is that the return value of the template function will replace the content of the directive (or the complete directive markup if replace: true), while a compile function is expected to change the DOM programmatically and return a link function (or object with pre and post link function).
In that sense you can think of the template function as some kind of convenience function for not having to use the compile function if you simply need to replace the content with a string value.
Hope that helps!
One of the best uses of the template function is to conditionally generate a template. This allows you to automate the creation of a template based on an attribute or any other condition.
I have seen some very large templates that use ng-if to hide sections of the template. But instead of placing everything into the template and using ng-if, which can cause excessive binding, you can remove sections of the DOM from the output of the template function that will never be used.
Let's say you have a directive that will include either sub-directive item-first or item-second. And the sub-directive will not ever change for the life of the outer directive. You can adjust the output of the template, prior to the compile function being called.
<my-item data-type="first"></my-item>
<my-item data-type="second"></my-item>
And the template string for these would be:
<div>
<item-first></item-first>
</div>
and
<div>
<item-second></item-second>
</div>
I agree that this is an extreme simplification, But I have some very complicated directives and the outer directive needs to display one of, about, 20 different inner directives based on a type. Instead of using transclude, I can set the type on the outer directive and have the template function generate the correct template with the correct inner directive.
That correctly formatted template string is then passed on to the compile function, etc.