When I look in the dojo documentation for template all I get is for dijit and examples only show you been able to use them in a widgit. I'm looking for the equivalent of the below methods in js prototype
var tmpl = new Template(url)
tmpl.evaluate(templateObj)
Does dojo have a template method that you can use in a dojo.declare( class ){} like you can do in js prototype. If not how could I go about similar functionality
Thanks
You may be interested in dojo.string.substitute (you'll need to dojo.require("dojo.string")).
http://dojotoolkit.org/api/dojo/string/substitute
[Edit] Also, if you're interested in acquiring a template for use in substitution from a URL on the same server, you may also want to look into dojo.cache (which is also what is often used to fetch widget templates):
http://dojotoolkit.org/reference-guide/dojo/cache.html
To clarify missingno's response, I don't think dojo.parser is what you're interested in right now; its job is to scan the DOM and transform DOM nodes into widgets and other Dojo components. dijit._Templated only uses dojo.parser when child widgets are involved (i.e. widgetsInTemplate is true); on the other hand, it uses dojo.string.substitute in all cases, to initially parse ${...} strings (e.g. ${id}) in the template.
I don't know Prototype, but this sounds like dojo.parser stuff. It is what is used by dijit._Templated behind the scenes (you can chack that in the source code if you want...)
Just note that you probably wouldn't need to cal this yourself - there is parseOnLoad=true for automatically parsing your initial HTML.
Related
I just started learning about the HTML custom elements, and through reading a series of intros, tutorials, and documentation, I think I have a good handle on how it works, but I have a philosophical question on the proper way to use or not use the <template> tag.
Custom elements give you the ability to encapsulate new functionality, simplifying the structure of your HTML document, and allowing you to simply insert a <my-custom-element>...</my-custom-element> tag instead of <div class="my-custom-element"><span class="part1">...</span><span class="part2">...</span></div>.
The class definition for the element then sets up the structure and functionality of that element. A bunch of the tutorials then describe how to use <template>...</template> and <slot>...</slot> to set up the contents of the custom element. You would then have to then include the template code in every HTML document in which you want to use the element rather than setting it up in the custom element class's constructor. Doesn't this run counter to the fact that custom elements help simplify and encapsulate functionality in a way that makes them more portable? Or am I misunderstanding the proper usage and/or placement of the template within the document?
Looking through SO, the closest I can find to addressing this is this question:
How to stamp out template in self contained custom elements with vanilla js?
But the answer essentially sidesteps this all together and says "Don't use <template>," and so doesn't really clear up my confusion.
Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:
<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>
So it doesn't need to be included in a every HTML document. This post shows a minimal example.
HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.
On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).
Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...
Note that without HTML Imports you can still import yourself some HTML documents with fetch():
fetch( "template.html" )
.then( stream => stream.text() )
.then( text =>
customElements.define( "c-e", class extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = text
}
} )
)
Update 2019
HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).
Disclaimer: I'm an author of the rich-component library mentioned below.
After some time of experimenting with custom elements and recently raising a full blown project based solely upon them I'd like to share my insights on this:
any component tiny as it is, is a candidate to grow to some beast
HTML part of it may grow to a point where it is very non-convenient to keep it within JS
do use template, built and parsed once and from that point cloned and injected into the shadow root - this is the same best practice as to use document fragment instead of mutating a living DOM
if the template contents should be changed from component's instance to instance - some kind of data binding framework may be used, and if minimalist approach on those is taken - it might still be easier and more performant to deal with a cloned-from-template document fragment than operate on string or template literals
In order to not write the same dozens of lines over and over again I've prepared rich-component library, which:
normalizes some API for template provisioning and all those 'clone template, create shadow, inject template's content into it' lines of repeating code
known to fetch html contents when html URL is provided
caches the templates so the fetch is done only once
We use DNN and often need to pass a few context specific values (like page id or module-on-page-id) into an AngularJS app. We've developed our own conventions how to do this, but would like to hear from others how they approach this to find a best practice.
So basically the situation is that the server-page has information needed by the JS. Using WebAPI is not an option, as these values are known in the page, but not in a separate request. Things I've seen so far have been:
Use in-view-razor like href="#Tab.TabId/{{...}}" (I don't like this)
Place the values in the ng-init like ng-init="config = { prop1: '#Tab.TabId' }"
Create a separate <script> tag where we generate a module on the fly containing these values so angular.module("config", []).constant('prop1', '#Tab.TabId')
Create a json with razor in the page somewhere and inject all of it as a module into the app using a generic code which does the same as #3, just with cleaner code re-use.
I've seen all these and have also used all. Currently we avoid #1 as we believe it's not good to mix templating languages and because it doesn't allow externalizing parts of the view. So basically we use #2 as for quick-and-simple (and a bit dirty) + #3/#4 for larger projects.
Do you have a better way, or which way would you prefer?
We are using variant #4.
This has the advantage that the JSON defines the exact interface for the config needed by the JS module. And Razor is great to generate URLs using #Url.Action.
we use NewtonSoft and do JSONConvert.SerializeObject(ObjectName) and then pass it over as a Session from the controller and then use #Html.Raw(ObjectName) and its a JSON Object that can be utilized easily in javascript...
I have a jQuery heavy app I'm in the middle of that has many jQuery plugins and I want to restructure the app, so I'm looking at different frameworks like Angular, React, Riot 2.0, etc.
I like React and Riot, but I can't figure out how I'd make your typical jQuery app (unmetered access to the DOM) into this new world of virtual DOM, etc.
Some of these components are complex and I wouldn't want to rewrite them all into the "VDOM way" and create another nightmare.
Does anyone have an answer to this? Maybe jQuery plugin heavy apps aren't suited, or is there a way to split/combine the UI using both, or maybe a React/Riot like framework that would play well with jQuery?
To access the DOM with jQuery inside a Riot 2.0 custom tag, you can use the mount event and this.root like this:
<my-custom-tag>
<h3>Riot.JS Custom tag + jQuery plugin</h3>
<p>My paragraph</p>
<script>
this.on('mount', function() {
var $node = $(this.root);
$node.find('p').html('updated by jQuery!');
});
</script>
</my-custom-tag>
I am not sure it is the "best practice" but it works with Riot 2.0.10 (Feb 19, 2015).
If the custom tag contains form or input elements, it's simpler, you can access them by their name property, you don't need the mount event:
<my-custom-form>
<input name="email" type="text">
<script>
$(this.email).val('mike#worldcompany.com');
</script>
</my-custom-form>
Riot 2.0 was released just 4 days ago so there are obviously no extensions yet.
But it's a good candidate to transform jQuery- based apps to the "modern world" with using custom tags or "components" in React community.
Custom tags lets you build reusable pieces of client side code with a less need for jQuery selectors and manipulation methods. And HTML and JS is less scattered on the file system.
And Riot 2.0 is designed to play well together with jQuery. You can freely use jQuery plugins together with custom tags so you can transform your app iteratively - one tag at the time if you like.
This is an excellent guide to wrapping DOM libs with React components:
https://github.com/rpflorence/react-training/blob/gh-pages/lessons/05-wrapping-dom-libs.md
In angular someone has probably already recreated the thing you want. You use directives to either implement reusable components or wrap existing ones. To wrap a plugin:
initialize in link (based on isolate scope)
use scope.$watch(key, fn) to update the plugin when something changes
use scope.$apply() in plugin callbacks to tell angular something might have changed, update any relevant two way bindings, and invoke any relevant expression bindings
use scope.$on('$destroy', fn) to clean up
See ui-bootstrap for examples and api design.
The same applies to React, but components instead of directives.
initialize in componentDidMount (based on props)
update the plugin in componentDidUpdate (based on props)
call this.props.onWhatever in plugin callbacks
clean up in componentWillUnmount
See react-bootstrap for examples and api design.
And I don't think Riot is relevant here.
We had the same problem, i.e. turn a pretty big global jquery based admin frontend into nestable conflict free components - w.o much training effort for our partners - so riotjs was just ideal for us.
We agreed on the solution below (see also Michael Rambeau's answer) and are up to now quite content with that.
Via a mixin we give all our riot component tags this function:
var ComponentTagsMixin = {
init: function () {
//<other stuff needed in all tags>
// jquery local to the vDom of this instance:
$: function(selector) {
// you could fail here if (!this.isMounted)
return $(this.root.querySelector(selector))
}
}
```
and so, within the tags you simply turn $ into this.$.
Example, jquery nestable:
$('#nestable').nestable(....)
$('.dd').nestable('expandAll');
into
this.$('#nestable').nestable(....)
this.$('.dd').nestable('expandAll');
and now this allows to have many of those components within the same page w/o conflict and local namespaces and data.
As already pointed out, this is only accessible after the tag is mounted, so run your stuff within an this.on('mount'...) block.
This works remarkably well with legacy jquery libs since they typically allow the user to redefine their Dom selectors for double run-ability within a page anyway.
A psuedo controller method
#RequestMapping("/foo")
public String getFoo(Model model) {
model.add("foo", repo.findFoo());
model.add("bar", repo.findBar());
model.add("barOptions", repo.findBarOptions(bar));
return "fooView";
}
Let's say the client uses Expression language to render foo and bar; but we use JavaScript to render barOptions.
<html>
<script>
var options = <mytag:toJSON object="${barOptions}"/>;
$("#options").renderOptions( options );
</script>
<body>
<mytag:renderFoo foo="${foo}"/>
<mytag:renderBar foo="${bar}"/>
<ul id="options"></ul>
</body>
</html>
Common conventions tells me this is bad. But the essence of MVC, where the controller sends data and the view determines how to use it, tells me this is good. Is there a better way to do the same thing? Is there any reason why this isn't commonly done? I could request the JSON using a separate call, but then I have to make more requests for the page to load, and there may be logic to determine barOptions in the controller method getFoo() based on other input at the time of the page load.
At first glance I can not say that I see anything blatantly wrong with approach. The only aspect that initially took me off guard was your need to convert data in the model object to json.
For me, JSON usually implies that there is some sort of server side object that needs to be converted so that a client side javascript can access or manipulate its structure in a javascript way. I guess without knowing more of the purpose of the options list, I can't see a reason why json serialization is required here.
By using a Tag to convert a model object to JSON, we avoid an
additional request made by the client
But if we assume that JSON is a requirement (perhaps for some third party jquery plugin), then I absolutely do not see anything wrong with approach.
What is special or different about the barOptions unordered list, why does it have be rendered with json? Why not just use a for loop to build the list items? Or you can have a custom tag that builds out the ul entirely.
Aside from that, I missing the point as how one may perceive this as being bad code.
Normally JSON is requested by JavaScript with an Ajax call, but if in your case it's available at page rendering time, I see nothing wrong with your solution. It's clean, compact code and easy to read. Looks perfectly OK to me, the alternative would be to loop on the options array with a forEach, but this approach looks better.
I encountered a problem that took me some time to debug where a plug-in that I was using for jQuery (in this case jFeed) was not working. The problem ended up being because we also used Amazon Associates product previews. The product previews code ends up including a number of other JS files through document.write(), including another copy of jQuery. Because the product previews code appeared below the jFeed script, jQuery was redefined without the getFeed function.
Is there a best practice to ensure that certain objects like jQuery only get defined once on a page? I'm thinking of something like #ifndef with C/C++, but I don't know how it would work in this case where I didn't write the code that dynamically pulled in jQuery again.
I think in your situation, it would probably be best to redefine the jQuery variable as something else. The other jQuery code might use a different version so you might want to define a new variable which would indicate which jQuery you're using.
You could so something like this:
<script>
var $jMain = jQuery;
</script>
You would then just use the $jMain instead of jQuery or $. It'll be up to you to you to ensure you have the correct jQuery object when you do this. Here's the documentation.
Unfortunately the environment inside one JS sandbox (like within a window or frame of a browser) was not really designed to support the modern world of pulling in scripts from various places; there's no way you can say "define this object and make it resistant to redefinition". (You can even redefine most of the Javascript built-ins if you try!)
Your best shot is to make sure that your code is eval'd last, which gives you final say over the state of the environment when it runs. That doesn't mean other code can't come along later and clobber your definitions, but that's generally really bad form. You can do this by having your script tag be the last element in the body of the document, for example.
See also this jQuery method, which won't help you directly, but gets you thinking about some solutions to page sharing: http://api.jquery.com/jQuery.noConflict/