How to bind actions to markup in ember template by {{{ }}} (Template helper)? - javascript

I am constructing a markup string with an onclick action as shown below.
helpInfoMarkup: computed('levelInfo.name', function() {
return '<a class="help-text" {{action "switchContext"}}' + get(this, 'levelInfo.name') + '</a>';
}
My .hbs file
<div>{{{helpInfoMarkup}}}</div>
However this is not binding to the ember action and just appears as part of the markup and does not work? Any thoughts on this ?

This can not work because the glimmer rendering engine (which is used by ember) does not operate on strings.
Instead your ember templates are compiled to a binary format during the build, and later at the runtime glimmer will directly produce DOM, not an HTML string.
This means you can never pass a glimmer template around as a string. You could bundle the template compiler with your app (but you should not) to compile the template in the browser, but even then you can not use it to produce an HTML string representation because in this step you loose information (for example action binding). This is because these action bindings are never part of the HTML, but glimmer directly attaches event listeners to the DOM nodes during rendering. By using a HTML string and {{{...}}} you render plain HTML, not a glimmer template, and so this wont work.
What you should do is move all your HTML to templates and use a component to embed it.
The only other possibility is to utilize did-insert from ember-render-modifiers to manually attach an event. so you could do this:
<div {{did-insert this.attachSwitchContextAction}}>{{{helpInfoMarkup}}}</div>
and then in your component:
#action
switchContext() {
}
#action
attachSwitchContextAction(elem) {
elem.addEventListener('click', (...args) => this.switchContext(...args));
}
get helpInfoMarkup() {
return '<a class="help-text" ' + get(this, 'levelInfo.name') + '</a>';
}
this way you work around the glimmer template engine altogether and fall back to manual DOM manipulation.

You can remove the helpInfoMarkup computed property and update your template to
<div>
<a class="help-text" {{on "click" this.switchContext}}>{{this.levelInfo.name}}</a>
</div>

Related

Use Angular templating in content which is injected using innerHTML

I have an Angular (4) template which injects what I give it using [innerHTML], and this cannot be changed as it's an external package. So originally I had something like this:
public getMessages(): InterfaceName {
return {
emptyMessage: `<span>Some content!</span>`
}
}
I then passed this into the template of the external element:
<external-element [messages]="this.getMessages()"></external-element>
This worked perfectly. However, the product I am working on needs to support internationalisation, so the strings all need to come from a separate JSON file, and Angular's TranslateService is used to retrieve the strings and render them. So in the internationalisation file, I had an array of messages which would each be posted to the emptyMessage object above. So I had this:
public getMessages(): InterfaceName {
const messages = this.translate.instant('PATH.TO.JSON.STRINGS');
return {
emptyMessage: `
<div>
<span *ngFor="let message of messages;">{{ message }}</span>
</div>`
}
}
However, this just rendered {{ message }}, presumably due to introducing the possibility of an XSS attack, Angular sanitised the HTML. I also tried to use [innerHTML]="message" but this just showed up blank.
So how can I get the content from the i18n file and render it, after which the content is injected using innerHTML, of which I have no control?

Returning dom element from json

I have a JSON like this:
{
"HOME_VIEW_CONTENT": {
"header": "<div>Hello <span className='someClass'>world!</span></div>",
"ingress": "Hello moon!"
}
}
then in react I have this code:
{content.header}
{content.ingress}
And this works, but i'm getting the div out as a string (naturally).
Is there a plugin or an easy way to convert the string with the div to an div object without using dangerouslyInsertInnerHtml in react?
I am using a webpack + react setup so i might use a loader? or Is there a react method for doing this?
React escapes the HTML to prevent XSS.
If you really want to render HTML, you can use dangerouslySetInnerHTML:
dangerouslySetInnerHTML={{__html: this.state.actions}}
React has this syntax so that you don't accidentally render text as HTML and introduce XSS bugs.
This is from the docs, i have not tested this as i did not need it so far.

Why can't Knockout custom components be named and registered in camelCase?

I have started working on Knockout recently, and have been assigned to create custom components that can be used in various application. While creating a component, I used camel case to name it, e.g: "datePicker".
Component code
ko.components.register("datePicker",{
viewModel: require('./components/date-picker-widget'),
template: require('raw!./components/date-picker-widget.html')
});
HTML Code
<datePicker params="{value:returnValue, initialValue:returnValue.initialValue}"></datePicker>
But this is what was rendered:
So its clear that Knockout expects component names in lower case (JSFiddle reference). The question remains is why?
I also saw similar constraints on naming of components in React, where you have to start the name with capital character only.
The key is here in the documentation:
By default, Knockout assumes that your custom element tag names correspond exactly to the names of components registered using ko.components.register. This convention-over-configuration strategy is ideal for most applications.
If you want to have different custom element tag names, you can override getComponentNameForNode to control this. For example,
ko.components.getComponentNameForNode = function(node) {
var tagNameLower = node.tagName && node.tagName.toLowerCase();
if (ko.components.isRegistered(tagNameLower)) {
// If the element's name exactly matches a preregistered
// component, use that component
return tagNameLower;
} else if (tagNameLower === "special-element") {
// For the element <special-element>, use the component
// "MySpecialComponent" (whether or not it was preregistered)
return "MySpecialComponent";
} else {
// Treat anything else as not representing a component
return null;
}
}
You can use this technique if, for example, you want to control which subset of registered components may be used as custom elements.
In fact, the documentation is not entirely up to par with the code, because it claims "correspond exactly", whereas in fact in the code only the tag name is lowercased, and the registration isn't.
In any case, because browsers return tagName in upper case, the above code will only work if your registration is in lowercase.
So, you can either use datePicker camelCased in your view and register it lower case:
<datePicker></datePicker>
ko.components.register("datepicker",{
viewModel: function() { },
template: '<strong>DatePicker stub is showing!</strong>'
});
$(function() { ko.applyBindings({}); });
Or you can monkey patch the register function:
<datePicker></datePicker>
var originalFn = ko.components.register;
ko.components.register = function(name, options) {
originalFn(name.toLowerCase(), options);
};
ko.components.register("datePicker",{
viewModel: function() { },
template: '<strong>DatePicker stub is showing!</strong>'
});
$(function() { ko.applyBindings({}); });
Though I'm not sure how stable and cross-browser compatible that would be.
In any case that shouldn't matter much, since you merely asked "why" this was like it was.
My suggestion: stick to the docs' style of dash-cased-element-names.
PS. The screenshot you posted is how the debugger rendered your elements, not how Knockout or the browser would have it. In fact, most likely if you use "view source" you'll get a camelCased element name; to be entirely sure you could inspect the raw body of the request.
Its not just the case with knockoutjs or react components, if you are writing xHTML then according to the specification tag names and attributes must be in lower case. (Ref: http://www.w3.org/TR/xhtml1/#h-4.2).
In most browsers, although the rendered html will have case insensitive tags (View Source), but the DOM built by browser(debugger tools, inspect element) will usually have all lowercase for tag names and attributes.
Although there is no wrong with case insensitive tags and attribute names, the web developers have, IMO, just adopted the convention of using lowercase names for XHTML or HTML.

Ember.js - get the model data of a DOM element

I have a DOM element and want to get the data model that it is bound to.
This has to be done outside of any ember controller / component. All I have to work with is the DOM element and the global Ember variable (because this piece of code runs from an externally loaded script file).
How can this be done?
It can't be done (not out of the box anyway). There is no relationship between DOM elements and models. The best way I could think of doing this would be to bind the ID of the model to and attribute on a particular DOM item. For example:
export default Ember.Component.extend({
model: null,
attributeBindings: ['data-model-id'],
'data-model-id': function() {
return this.get('model.typeKey') + ':' + this.get('model.id');
}.property('model.{typeKey,id}');
});
Then you'd get an element in the DOM like this:
<div id="ember189" data-model-id="user:86">...</div>
But this only works if your HTML is written such that each model gets its own DOM element. And you'd still have to access private API to get the store via App.__container__.lookup().
Unless you feel like you have a really good reason to do something like this, I would avoid it.

What is the difference between compile and link function in angularjs

Can someone explain in simple terms?
The docs seems a bit obtuse. I am not getting the essence and the big picture of when to use one over the other. An example contrasting the two would be awesome.
compile function - use for template DOM manipulation (i.e., manipulation of tElement = template element), hence manipulations that apply to all DOM clones of the template associated with the directive.
link function - use for registering DOM listeners (i.e., $watch expressions on the instance scope) as well as instance DOM manipulation (i.e., manipulation of iElement = individual instance element). It is executed after the template has been cloned. E.g., inside an <li ng-repeat...>, the link function is executed after the <li> template (tElement) has been cloned (into an iElement) for that particular <li> element. A $watch() allows a directive to be notified of instance scope property changes (an instance scope is associated with each instance), which allows the directive to render an updated instance value to the DOM -- by copying content from the instance scope into the DOM.
Note that DOM transformations can be done in the compile function and/or the link function.
Most directives only need a link function, since most directives only deal with a specific DOM element instance (and its instance scope).
One way to help determine which to use: consider that the compile function does not receive a scope argument. (I'm purposely ignoring the transclude linking function argument, which receives a transcluded scope -- this is rarely used.) So the compile function can't do anything you would want to do that requires an (instance) scope -- you can't $watch any model/instance scope properties, you can't manipulate the DOM using instance scope information, you can't call functions defined on the instance scope, etc.
However, the compile function (like the link function) does have access to the attributes. So if your DOM manipulations don't require the instance scope, you can use a compile function. Here's an example of a directive that only uses a compile function, for those reasons. It examines the attributes, but it doesn't need an instance scope to do its job.
Here's an example of a directive that also only uses a compile function. The directive only needs to transform the template DOM, so a compile function can be used.
Another way to help determine which to use: if you don't use the "element" parameter in the link function, then you probably don't need a link function.
Since most directives have a link function, I'm not going to provide any examples -- they should be very easy to find.
Note that if you need a compile function and a link function (or pre and post link functions), the compile function must return the link function(s) because the 'link' attribute is ignored if the 'compile' attribute is defined.
See also
Difference between the 'controller', 'link' and 'compile' functions when defining a directive
Dave Smith's excellent ng-conf 2104 talk on directives (the link goes to the section of the video that talks about compile and link)
I beat my head against the wall on this for a couple of days, and I feel that a bit more explanation is in order.
Basically, the docs mention that the separation is largely a performance enhancement. I would reiterate that the compile phase is mainly used when you need to modify the DOM BEFORE the sub-elements themselves are compiled.
For our purposes, I'm going to stress terminology, which is otherwise confusing:
The compiler SERVICE ($compile) is the angular mechanism that processes the DOM and runs the various bits of code in directives.
The compile FUNCTION is one bit of code within a directive, which is run at a particular time BY the compiler SERVICE ($compile).
Some notes about the compile FUNCTION:
You cannot modify the ROOT element (the one your directive affects), since it is already being compiled from the outer level of DOM (the compile SERVICE has already scanned for directives on that element).
If you want to add other directives to (nested) elements, you either:
Have to add them during the compile phase.
Have to inject the compile service into the linking phase and compile the elements manually. BUT, beware of compiling something twice!
It is also helpful to see how the nesting and explicit calls to $compile work, so I've created a playground for viewing that at http://jsbin.com/imUPAMoV/1/edit. Basically, it just logs the steps to console.log.
I'll state the results of what you'd see in that bin here. For a DOM of custom directives tp and sp nested as follows:
<tp>
<sp>
</sp>
</tp>
Angular compile SERVICE will call:
tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link
The jsbin code also has the tp post-link FUNCTION explicitly call the compile SERVICE on a third directive (up), which does all three steps at the end.
Now, I want to walk through a couple of scenarios to show how one might go about using the compile and link to do various things:
SCENARIO 1: Directive as a MACRO
You want to add a directive (say ng-show) dynamically to something in your template that you can derive from an attribute.
Say you have a templateUrl that points to:
<div><span><input type="text"></span><div>
and you want a custom directive:
<my-field model="state" name="address"></my-field>
that turns the DOM into this:
<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>
basically, you want to reduce boilerplate by having some consistent model structure that your directive can interpret. In other words: you want a macro.
This is a great use for the compile phase, since you can base all of the DOM manipulations on things you know just from the attributes. Simply use jQuery to add the attributes:
compile: function(tele, tattr) {
var span = jQuery(tele).find('span').first();
span.attr('ng-show', tattr.model + ".visible." + tattr.name);
...
return {
pre: function() { },
post: function() {}
};
}
The sequence of operations will be (you can see this via the jsbin mentioned earlier):
The compile SERVICE finds my-field
It calls the compile FUNCTION on the directive, which updates the DOM.
The compile SERVICE then walks into the resulting DOM, and COMPILES (recursively)
The compile SERVICE then calls pre-link top-down
The compile SERVICE then calls post-link BOTTOM UP, so my-field's link function is called AFTER interior nodes have been linked.
In the above example, no linking is needed, since all of the directive's work was done in compile FUNCTION.
At any point, the code in a directive can ask for the compiler SERVICE to run on additional elements.
This means that we can do exactly the same thing in a link function if you inject the compile service:
directive('d', function($compile) {
return {
// REMEMBER, link is called AFTER nested elements have been compiled and linked!
link: function(scope, iele, iattr) {
var span = jQuery(iele).find('span').first();
span.attr('ng-show', iattr.model + ".visible." + iattr.name);
// CAREFUL! If span had directives on it before
// you will cause them to be processed again:
$compile(span)(scope);
}
});
If you're sure that the elements you are passing to $compile SERVICE originally were directive-free (e.g. they came from a template you defined, or you just created them with angular.element()), then the end result is pretty much the same as before (though you may be repeating some work). However, if the element had other directives on it, you just caused those to be processed again, which can cause all sorts of erratic behavior (e.g. double-registration of events and watches).
Thus, the compile phase is a much better choice for macro-style work.
SCENARIO 2: DOM configuration via scope data
This one follows from the example above. Suppose you need access to the scope while manipulating the DOM. Well, in that case, the compile section is useless to you, since it happens before a scope is available.
So, let's say you want to pimp out an input with validations, but you want to export your validations from a server-side ORM class (DRY), and have them auto-apply and generate the proper client-side UI for those validations.
Your model might push:
scope.metadata = {
validations: {
address: [ {
pattern: '^[0-9]',
message: "Address must begin with a number"
},
{ maxlength: 100,
message: "Address too long"
} ]
}
};
scope.state = {
address: '123 Fern Dr'
};
and you might want a directive:
<form name="theForm">
<my-field model="state" metadata="metadata" name="address">
</form>
to auto-include the proper directives and divs to show the various validation errors:
<form name="theForm">
<div>
<input ng-model="state.address" type="text">
<div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...
In this case you definitely need access to the scope (since that is where your validations are stored), and are going to have to compile the additions manually, again being careful not to double-compile things. (as a side note, you would need to set a name on the containing form tag (I'm assuming theForm here), and could access it in link with iElement.parent().controller('form').$name).
In this case there is no point in writing a compile function. Link is really what you want. The steps would be:
Define a template that is completely devoid of angular directives.
Define a link function that adds the various attributes
REMOVE any angular directives that you might allow on your top-level element (the my-field directive). They have already been processed and this is a way to keep them from being double-processed.
Finish by calling the compile SERVICE on your top-level element
Like so:
angular.module('app', []).
directive('my-field', function($compile) {
return {
link: function(scope, iele, iattr) {
// jquery additions via attr()
// remove ng attr from top-level iele (to avoid duplicate processing)
$compile(iele)(scope); // will pick up additions
}
};
});
You could, of course, compile the nested elements one-by-one to avoid having to worry about the duplicate processing of ng directives when you compile the top-level element again.
One final note on this scenario: I implied you'd be pushing the definition of the validations from a server, and in my example I've shown them as data already in the scope. I leave it as an exercise for the reader to figure out how one might deal with needing to pull that data from a REST API (hint: deferred compile).
SCENARIO 3: two-way data binding via link
Of course the most common use of link is to simply hook up the two-way data binding via watch/apply. Most directives fall into this category, so it is adequately covered elsewhere.
From the docs:
Compiler
Compiler is an angular service which traverses the DOM looking for attributes. The compilation process happens into two phases.
Compile: traverse the DOM and collect all of the directives. The result is a linking function.
Link: combine the directives with a scope and produce a live view. Any changes in the scope model are reflected in the view, and any user interactions with the view are reflected in the scope model. Making the scope model a single source of truth.
Some directives such ng-repeat clone DOM elements once for each item in collection. Having a compile and link phase improves performance since the cloned template only needs to be compiled once, and then linked once for each clone instance.
So at least in some cases, the two phases exist separately as an optimization.
From #UmurKontacı:
If you are going to make DOM transformations, it should be compile. If you want to add some features that are behavior changes, it should be in link.
This is from Misko 's talk on directives. http://youtu.be/WqmeI5fZcho?t=16m23s
Think of the compiler function as
the thing that works on a template and the thing that is allowed to
change the template itself by, for example, adding a class to it or
anything like that. But it's the linking function that actually does
the work of binding the two together because the linking function has
access to the scope and it's the linking function that executes once
for each instantiation of the particular template. So the only kind of
things you can placed inside of the compile functions are things that
are common across all of the instances.
Little late to the thread. But, for the benefit of future readers:
I came across the following video which explains Compile and Link in Angular JS in a very great fashion:
https://www.youtube.com/watch?v=bjFqSyddCeA
It would not be pleasing to copy/type in all of the content here. I took a couple of screenshots from the video, which explain every stage of Compile and Link phases:
The second screenshot is little bit confusing. But, if we follow the step numbering, it is quite straight forward.
First cycle: "Compile" gets performed on all of the directives first.
Second cycle: "Controller" and "Pre-Link" gets performed (just one after another)
Third cycle: "Post-Link" gets performed in reverse order (starting from innermost)
Following is the code, which demonstrates the above:
var app = angular.module('app', []);
app.controller('msg', ['$scope', function($scope){
}]);
app.directive('message', function($interpolate){
return{
compile: function(tElement, tAttributes){
console.log(tAttributes.text + " -In compile..");
return {
pre: function(scope, iElement, iAttributes, controller){
console.log(iAttributes.text + " -In pre..");
},
post: function(scope, iElement, iAttributes, controller){
console.log(iAttributes.text + " -In Post..");
}
}
},
controller: function($scope, $element, $attrs){
console.log($attrs.text + " -In controller..");
},
}
});
<body ng-app="app">
<div ng-controller="msg">
<div message text="first">
<div message text="..second">
<div message text="....third">
</div>
</div>
</div>
</div>
UPDATE:
Part 2 of the same video is available here: https://www.youtube.com/watch?v=1M3LZ1cu7rw The video explains more about how to modify DOM and handle events during Compile and Link process of Angular JS, in a simple example.
Two Phases: Compile and Link
Compile:
Traverse the DOM tree looking for directives (elements / attributes / classes / comments). Each compilation of a directive may modify its template, or modify its contents which has not been compiled yet. Once a directive is matched, it returns a linking function, which is used in a later phase to link elements together. At the end of the compile phase, we have a list of compiled directives and their corresponding linking functions.
Link:
When an element is linked, the DOM tree is broken at its branch point in the DOM tree, and the contents are replaced by the compiled (and linked) instance of the template. The original displaced content is either discarded, or in the case of transclusion, re-linked back into the template. With transclusion, the two pieces are linked back together (kind of like a chain, with the template piece being in the middle). When the link function is called, the template has already been bound to a scope, and added as a child of the element. The link function is your opportunity to manipulate the DOM further and setup change listeners.
This question is old by I would like to make short summary which may help:
Compile called once for all directive instance
Compile main purpose is to return/create the link (and possibly pre/post) function/object. You can also init stuff that are shared between instances of the directive.
In my opinion, "link" is a confusing name for this feature. I would prefer "pre-render".
link is called for each directive instance and its purpose is to prepare the rendering of the directive in the DOM.

Categories

Resources