requirejs loading async templates module pattern - javascript

I just decided to try require.js for the first time and for the most part, it works fine - until I get to the point where I try to handle template loading dynamically.
basically, i have a requirement that the app should not proceed until the templates have all been loaded and made available.
to that effect, a module 'templating' has been created. it imports a definition of an array of templates (already available) that it needs to load - require - before it returns.
I am pretty sure this is probably an anti-pattern so how would you fix it?
app -> requires "templating"
define templating ->
- loop through an array of templates and dynamically create a list
- define all templates (via text!) so later we can require("template-name")
- also tried, require all templates
What I observe is that the templating module loads and becomes available to the app before all the templates are loaded.
The XHR fetching the templates follows afterwards.
How do i prevent the module from returning before all the texts load and compile? pseudo code or links to examples would be fine.

We actually had one solution early on in a project and then moved to another one.
[1] Synchronization - Because we're using jQuery in our app we have the jQuery Deferred object available for use throughout the app. I created one instance of Deferred which only had its .resolved() called when all of my templates were loaded. Then each time I tried to use a template I had it wrapped in code like this:
$.when(cache.templatesLoadedPromise).done(
function () {
// Render the view.
$("...").html($.tmpl(cache.template, jsonData));
});
Then none of the renders would happen until the templates were available.
[2] Per module loading and registration - Nobody was that crazy about solution [1] and we wanted each module to load only the templates needed for that particular module. So we started just listing them as "text!something.tmpl" requirements and then doing a registration of that particular template as the first line within the module that had that as a requirement. In our case, we use Handlebars as the template engine together with ICanHandlebarz (similar to ICanHaz for Mustache). So I have a call to ich.addTemplate("something", something); up at the beginning of the module code.
In a few cases, multiple modules used the same templates so I had to test to see if the template was already registered before doing the registration.
I hope one of those would help you with your problem.

Related

Is it possible to influence a single Angular app from multiple JS scripts?

Quick Summary:
I need to allow two script files to handle different operations for the same angular app. One needs to initialize the app, the other needs to assign a templateCache object to a piece of JSON in localStorage.
Context:
I have several python files which compile/generate html and I have constructed an angular app with this emitted html for my site (which uses CGIs).
The basic construct of the site comes pieces of HTML, which fit together like so:
|------------Header---------------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|------------Footer---------------|
My Header creates the <head> tag, instantiates my ng-app and uses $templateCache to set up a template that I call from my Navigation code. I had to go with templateCache instead of ngView and ngRoute due to some limitations with how the CGIs emit the html, and the order in which this happens.
My "Navigation" python/html sets up my app with JS like so:
<script>
var responsiveCatalog = angular.module('responsiveCatalog', ['ngStorage']);
....controllers...
....config, etc....
</script>
This Navigation also includes my default templateCache object:
<div ng-include=" 'responsiveItems.html' "></div>
This is all working to show my first templateCache object in the Content section. However, I need to grab many pieces of information from the python generator for the "Content" section (a totally separate file from the "Navigation"), store this data as JSON in localstorage (hence the inclusion of the ngStorage module), and call that as my second templateCache option.
I am not actually sure that I can use two separate instances of Javascript to reference and influence the same Angular app. I know this seems like bad practice, but I am trying to prevent the need to tear down a huge piece of legacy architecture to influence the angular app from two Javascript files in harmony.
Thanks in advance.
You can do
angular.module('myAppName').controllers.... in different files, just make sure the myAppName the same. Bug I don't feel like it work with config, no time to test. If you need any communication between them, check $emit and $broadcast.

How do I instantiate require.js modules on different pages, partials with codeigniter?

I'm trying to update the javascript on a large scale, php/codeigniter based website. It has many, many php partials for code being loaded onto certain pages, but only one footer, header. Most of the partials have inline script tags in them for loading javascript. This needs to be fixed and because the site is very modular with components being used multiple times across pages, require.js seems like a pretty good solution.
So, it's instantiate javascript, we typically do this.
<script type="javascript" src="../js/scriptname.js">
<script type="javascript">
DP.scriptname.init(parameters)
</script>
I'd like to get away from this and just have a have a single entry point for the js using require.
My question is this: what's the best way to instantiate javascript for certain pages using require? Do I need I need to continue including my scripts in the partial and then do something like writing a require module for that specific page and then wrap it all in my data-main script like this? We're planning on using Backbone and Marionette as well, but I won't be able to use the Backbone router to do anything like setting hash URLs. Should I use the URLs to instantiate my require modules perhaps?
Ok, hope someone can help. My experience has typically been in building single page websites. This is different. Thanks
Cam
Well, if I understand your question correctly, you can use Require JS in such way.
First of all, write a config in which you can describe mapping between module names and concrete files. For example:
requirejs.config({
baseUrl: 'js/modules' // by default load any module using this path
});
After that you should refactor your existing module and adjust it to the AMD format (see http://requirejs.org/docs/whyamd.html)
As a result of this step you will have something like this (let's say in file 'js/modules/scriptname.js'):
// module has the same name as the file, which contains it - "scriptname"
define(function () {
// define the module value by returning a value
return function () {};
});
And as a final step you can refactor your inline script and use this module in such way:
<script type="javascript">
// when we place name of the module as a first argument of the "define"
// function, Require JS find file that contains it and load it asynchronously
define(["scriptname"], function (scriptname) {
// "scriptname" now contains value that we recieve from the module definition
scriptname.init(parameters);
});
</script>
Hope this helps.
Note. My solution based on this section of official Require JS documentation: http://requirejs.org/docs/api.html#jsfiles
This question seems to come up a lot, so I'll point you to some resources that may help:
How does RequireJS work with multiple pages and partial views? - https://stackoverflow.com/a/10816983/617615
Modular HTML components with RequireJS - http://simonsmith.io/modular-html-components-with-requirejs/
Example RequireJS-based project that has multiple pages that share a common set of modules - https://github.com/requirejs/example-multipage

Understanding dojo's require in combination with diji/layout

I'm trying to understand dojo in combination with dijit layouts. More specifically: do I need to require the layout widgets in JS or is using html data attributes enough?
I've read the Layout documentation and it seems like you need 3 things:
JavaScript: require the layout widgets you want to use
HTML markup
CSS
But it's not clear to me why I would need to require the layout widgets in JavaScript with:
require(["dojo/parser", "dijit/layout/BorderContainer", "dijit/layout/ContentPane"]);
Which is stated in this example.
I've created a demo without requiring "dijit/layout/BorderContainer" and "dijit/layout/ContentPane" and that's working fine.
Is it safe to leave these out of the require statement?
You should require your modules rather than letting the parser load them because ideally you want all of your modules loaded before the parser runs so that you can bundle all of your dependant modules into a layer file.
This means that you can create a layer with all of your common modules so that you don't need to have 100s of tiny .js requests hit your server, and instead you can load a bulkier one.
Also, if you have custom modules you should load all of the dependant widgets inside of the custom module instead of letting the parser detect.
dojo/parser loads all dependencies for you, so you don't need to require them programmatically in JavaScript, but you need them there once you decide to build your code, i.e. put all the dependencies into a single file, which is the reason, why parser warns you, when it loads dependencies for you:
This auto-requiring is also the reason, why parser.parse() returns a promise, it's because it might be asynchronously loading dependencies:
parser.parse().then(function(/* Array */ widgetInstances) {
});

Lazy Loading templates in Meteor

I use require.js to do lazy loading for a Javascript app. I would love to switch to a meteor stack but right now it looks like Meteor sends the whole app (all the templates) through on the initial load. Has anyone had success with require.js and meteor or any other implementation?
You're asking different questions, but certainly they are connected. The first is about loading additional javascript code into your meteor app. Of course you can use thing like requirejs. This should work fine supposing your lazy code is located in the public directory of your meteor project. However, my experience is that requirejs goes mad when the contents of public gets updated often, so for example in the development environment. Maybe it's a matter of customizing the library, but I would rather recommend using some lightweight homebrewed package. Look here, if you need some inspiration.
The second question is about lazy template definition. Each template consists of two parts. The first is its html code, written in handlebars syntax, the second is all the javascript code which you write to define how your template should behave (e.g. helpers, event handlers). The second part is easy, as long as we assume that we already know how to load the lazy code (see the above paragraph) and the template, lets call it myLazyTemplate, is already defined, so basically speaking Template.myLazyTemplate is not undefined. So how to achieve the latter?
To dynamically define a new template you'll need to call
Template.__define__(name, raw_func)
on the client. So the last question is "what is raw_func?". This is a compiled version of your html code which is normally created automatically on the server and then sent down the wire to the client when the app gets loaded (look here to see how it's done in meteor). But we want to do it dynamically, right?
So the idea is to compile the template code manually with a help of the Handlebars.to_json_ast routine. You can feed it with your template html code, and the output is some javascript array that can be sent to the client anytime by the method we've already talked about. The last thing you need to do is to call Handlebars.json_ast_to_func on the client, using the data sent from the server as the only argument. The output produced by Handlebars.json_ast_to_func is the raw_func you can use to produce myLazyTemplate template.
I'm aware that this is only a rough idea, not the whole solution to your problem. I hope this will help you to figure out the final solution on your own.

Organizing javascript in Rails app

I have several javascript (coffee script) files in my Rails javascripts directory. The code is logically divided into different files. However, each file starts with $(document).ready -> and some files share common helper functions.
What is the best way to factor out the helper functions? Should I just put them all in some other file that gets included earlier in application.js?
Also, is it normal to have the code divided up the way mine is, where every page does $(document).ready ->? Doesn't this mean that all of my code is called on every page, regardless of whether or not it is relevant? Are there alternatives to this organization?
I'm not sure if this question is specific to Rails or relevant to javascript and jQuery in general.
I do think this is a Rails question, and a good one.
The normal paradigm in Rails, where "global" stuff goes in application.* is a little messed up with the asset pipeline, since the application.js really acts as a manifest, rather than a common file. Of course you could add stuff there, or even create an application.js.coffee for your common code. I decided to create a file called common.js.coffee (and in another case shared.js.coffee), which in my case was automatically handled by the require_tree . directive.
(Update based on comment from #jonathan-tran) In some cases, you may just want methods called on document ready for all pages -- for example, I used this to make a datepicker available to any field in any view. If, instead you want methods (actually global functions) available to be callable, you'll need to export the coffeescript functions to variables, for example by attaching to the window object. You can do both in a shared file.
It is true that if you use generators you'll end up with files for every controller which, if there's no specialized code result in a series of redundant $(document).ready -> statements when assets are compiled. So just get rid of the ones you don't use. But following the pattern of separating functionality specific to a page makes good sense to me and works well -- I know where to look to find stuff and that's worth a lot as a project grows.
And another rule I have learned with Rails: go with the flow. If that's how Rails does it, it's probably a good way. They do indeed think about these things. Don't fix what works :-)

Categories