Angular JS/Dojo widget parsing - javascript

I have an Angular JS application, in which I would like (actually I MUST) use dojo widgets. In the HTML template for the controller, I can return HTML that has dojo markup in it. However, the dojo parsing doesn't happen automatically when a view gets re-rendered. The only way I have been able to trigger reparsing is by manually calling the parser after a slight delay by doing something like this in the controller and then calling it when I know the model has changed.
refreshDojo = function() {
setTimeout(function() {
require(["dojo/parser"], function(parser) {
parser.parse()
});
}, 10);
}
This isn't really feasible for two reasons:
Having to do anything after a timeout is bound to cause trouble by
either happening to quickly before the html has been processed or
two slowly, causing a flash of content before the widgets are
created.
Secondly, if I am not mistaken, parser.parse() parses the whole DOM
which is not very efficient if I only updated a single div.
Is there a way to know for certain when the DOM has stabilized so that I can be sure to trigger this at the right time? And is there a way to access the root element of the controller (It appears that you can no longer inject $element)?

You should decorate the $compile service, so before angular compiles anything it will let dojo compile it.
Here's a small example:
http://plnkr.co/edit/9iJJFLWDqGtyqLV5Mbe3?p=preview
Documentation on decorators: http://docs.angularjs.org/api/AUTO.$provide#decorator

You could create an attribute directive dojoType that requires the named widget module (and mixins if needed) and instantiates them instead of the dojo parser.
For example, if you've loaded a part of the DOM ('ajax'), then you would first create a new scope for it, compile and link it against the scope (the dojoType directive gets applied), then you would call the dojo parser and specify the rootNode.
dojoParser.parse({
rootNode: dom.byId(domid)
}).then(/* access the widgets or whatever */);

Related

knockout and applyBindings - how to control the scope?

I am new to knockout, so going to fire a lot of questions. But I'm not new to data binding. So I am knocking my head on the difference between my expectations and reality. Here is a very basic question about applyBindings.
It looks like applyBindings has the parameter "view model", which is to me the domain object graph (as javascript objects) plus perhaps additional helper things added for the purpose of the view creation. But what I am completely missing at first is the scope of the bindings! I expected this to be applied to the current parent DOM element. But no, it is applied globally, in the entire page!
So is the expectation that in one HTML document there can only ever be one view model? This is very surprising to me! How am I supposed to create a single page web app where I have one panel showing the address book, another panel showing my appointments, another panel showing one loan application to review, and yet another showing the underwriting of another loan? They are all completely different things, am I really supposed to link them all into a single view model???
In my expectation, you bind a javascript object to a DOM element, and everything in there renders it. With every new nested DOM element, the focus object may change. It may be flowing out of some foreach binding from the parent's object. But then two sibling (or cousin) DOM elements might be sitting side by side and having completely different view model, and also a different life cycle. Like while I am in my underwriting workflow, I quickly need to bring up an address book or my calendar. All of it in a single page app. There should be no global interference between different view models used by different unrelated DOM elements.
And yet here we are with knockout I see it has only one ko.applyBindings(viewModelObject) for a the entire page.
What am I missing? What is preventing us from modifying ko.applyBindings to take two arguments, the view model-object and the DOM element in which to show it? I could try doing that, but I am afraid if knockout has been designed in this global mindset, there might be lots of issues running the knockout machinery more than once on the same page?
I'm sorry if I am frustrating people by shooting an answer already. But since I have a very urgent project I need to try to resolve my issues ASAP, and I am reading the knockout source code, which is quite nicely organized and uses good names, so it's quite intelligible; therefore I have found the answer myself.
The answer is that, yes, you can applyBindings to a parent element that you choose. The document.body is only the default if you don't say anything.
Therefore, from now on, I shall (tell my team to) always call applyBindings with the second argument specified for the rootNode. Like this:
<div>
... all my UI elements for this thing ...
... then last element in this div:
<script type="text/javascript">ko.applyBindings(viewModelObject, document.currentScript.parentElement);</script>
</div>
so, that way I can have multiple view models each in their own DOM element.
And additionally also, I was wondering, does knockout not somehow assign the model object to the DOM element? I could do that in my script tag too:
<div>
... all my UI elements for this thing ...
... then last element in this div:
<script type="text/javascript">
const viewElement = document.currentScript.parentElement;
viewElement.viewModelObject = viewModelObject;
ko.applyBindings(viewModelObject, viewElement);
</script>
</div>
and this allows me then -- if only for debugging -- to find the current view model object on the DOM element that is the root of a view. (It would be nice if that would happen with all other descendant bindings too, but that is perhaps the subject of another question.)
In fact, I decided to put into our general configuration a hard replacement of the ko.applyBindings function:
ko._applyBindings = ko.applyBindings;
ko.applyBindings(viewModelObject, rootNode, extendContextCallback) {
rootNode = rootNode || document.currentScript.parentElement;
rootNode.viewModelObject = viewModelObject;
ko._applyBindings(viewModelObject, rootNode, extendContextCallback);
}
now I don't even have to convince my team to do it this way, they will automatically, even without being aware of it.

How to parse a html string to native angular template

Currently I'm rendering the html string using innerHtml property after bypassing the angular sanitizing mechanism this is working for rendering the contents. But not able to map the event handlers to a function in angular component.
E.g.the html string will contain a button like below
<button (click)="callme()">Click</button>
the callme function will be part of angular component file. I want this click event to be handled in angular function.
Is it possible to parse this string to html dom and handle the events in angular components.
Sample code which describes this scenario and using angular cdk Portals.
https://stackblitz.com/edit/angular-upaudh?file=src%2Fapp%2Fcdk-portal-overview-example.ts
There are 2 ways that you can implement it, it depends from what you want.
The easy way is to use innerHtml input like this:
HTML
<div [innerHTML]="htmlStr"></div>
TS
htmlStr: 'Hello';
If you want to create a dynamic template that will be translated to HTML at build time. Then you have to go with Portals.
PS: In your example (in the comments) you use [innerHtml] and try to bind it in a Portal. Again [innerHtml] will not be translated before runtime so your click event will not be in the same scope as your component. The thing that you ask is an open conversation in github: Issue. For now you can use the alternatives above to solve your case. You can use innerHtml, and then use renderer2 or native element reference to bind click events to your functions, after they have rendered on the view.

Can you "hijack" rendering part of a Ractive template?

I have a "SuperSelect" control currently implemented as a Ractive component, which augments a regular drop-down select list with searching, filtering, extended option descriptions, and various other goodies. This generally works really well, except that I now need to fill one of these SuperSelects with approximately 7,800 options, and it gets really slow, and slows down the rest of the page as well. The problem seems to be Ractive's internal memory usage; if I re-implement the SuperSelect in vanilla JS, most of the problem goes away. Unfortunately, I can't see a good way to actually make use of my more-efficient SuperSelect without tearing out Ractive completely every place that it's used, which seems like throwing the baby out with the bathwater.
So, basically, I need a way to insert a chunk of DOM that's managed by other code into the middle of a Ractive template, while still allowing the controlling code to be notified when relevant keypaths are updated by the containing Ractive instance, and as far as I can tell none of the existing plugin/extension methods quite fit the bill. So far, I've come up with two hacks combining multiple plugin methods that might work:
Combine an adapter and a decorator. In this case, the decorator would simply replace whatever element it was attached to with the DOM fragment for the SuperSelect. A special SuperSelect control object would then be added to the Ractive instance's data with an adaptor that would let it participate in 2-way binding with the rest of the template, and independently communicate with the decorator code to update the SuperSelect DOM.
Combine a decorator with a mini-component and ractive.observe. In this case, the decorator would again replace a particular template element with the SuperSelect DOM fragment, but it would only be used locally within a component whose template consists of nothing but that one decorated element. The component would serve as a means of resetting the keypath root, so that the decorator code can observe a static set of keypaths in order to update the state of the SuperSelect DOM regardless of how the SuperSelect is embedded in a larger parent ractive instance.
Is there any simpler way to do what I need?
Yes – you could create a component with an empty DOM node and re-render its contents inside an observe handler:
const SuperSelect = Ractive.extend({
template: `
<div><!-- we'll render this bit ourselves --></div>`,
onrender () {
const div = this.find( 'div' );
this.observe( 'items', items => {
// render the items however we want
});
}
});
More complete demo here: http://jsfiddle.net/9w9rrr9s/

angularjs - After changing views using routing the jquery elements doesn`t get rendered

My issue is that after changing views using routing, the jquery components in my page doesn´t get rendered. I have customized ui components like dropdowns that are not being processed. I saw this answer: Angular.js App with routing.. Cannot get jquery ui layout plugin working on 2nd view
But I can´t figure out how to make it work in my case. It seems that when angular renders the template, any but angularjs code is processed.
Thanks so much in advance.
Code: http://plnkr.co/1mwMcsqMxWGTheoQmJ22
In the example you've provided, you're not actually loading the file with the jQuery code in it (jqcode.js).
The real problem however, as you can see in this version of your example, is that the document ready event is executed before your template has been loaded and rendered. This means that the element your jQuery code is attempting to target does't exist when you try to manipulate it.
You should really look into Angular directives which is where you are advised to place any DOM manipulation logic within an Angular project:
If you have to perform your own manual DOM manipulation, encapsulate
the presentation logic in directives.
From the Angular documentation for controllers.

Difference between the directives and static DOM elements in AngularJS

I have already read the
Compilation process, and directive matching
on AngularJS Doc.
but I really didn't understand the directives.
Example:
I have static html:
<div class="test test2" cid="549" sid="a5e3c4f8a9">text-text-text</div>
when I do it manually, I know it will only created and called one time at browser's parse time.
but what happens when I create a directive with the same dom element ?
<x my-directive>text-text-text</x>
is this the same effect?
I am asking such a newbie question, because I am using over 200 elements on my html page.
If I change them to single directive: for sure it will be much more easier to manage them.
and its no problem if its only slow at browser's compile time but what happend in run time?
and I am sorry, if the qustion is not pro enought. I am just new to Stackoverflow.
Thank You
Daniel
If I understand you correctly, you want to know how AngularJS creates a directive and how many times your directive methods are called.
When you create a directive (with module.directive('myDirective', ...)), you are really just creating a definition. Every time you use that directive (like <div my-directive>), AngularJS will run through the process described in the guide: that is, it will compile and link each use. It has to be this way because the directive doesn't exist in isolation; it exists not only in the $scope in which it was called, but also it can make use of element attributes and transcluded contents. The definition occurs once, but each instance is compiled and linked.
Once the directive is created, it's technically done; if you don't set up any $watch or $observe or event bindings, your "directive" is now just whatever's in the DOM at the end of your link function - there's no more computation. In other words, what happens after compilation and linking is entirely up to you.
So back to your example: if you use 200 of the same directive on the page, the directive will be defined once, but all 200 will be compiled and linked separately. But I'm not really sure what you're implying by asking. What's the question behind your question?

Categories

Resources