I'm using text plugin to import my templates. Now, in production I don't want that there are many XHR requests for templates so I would like to get them in one go.
My idea was to have build task which would take template and create something like:
define("my-template.tpl", function() {
return '<div>my content</div>'
}
and I was able to do this and define statement has been run, but requirejs still does XHR request when I have
define("my function", ["my-template.tpl"], function() {
<body of function here>
});
Did someone do any minification of text! plugin templates? Am I doing something wrong here?
RequireJS always requests all your modules asynchronously.
If you want one single call, you can build your app using the optimizer.
You do not need this template wrapper (as you noticed by yourself, it won't change anything). The files required using the text plugin will be included in the built file too.
Related
I'm starting a new angular project from a scratch and I want to avoid using require.js, browserify or webpack entirely. I'm gonna put all my code into separate files - no monolithic controllers.js or directives.js files...
Few options that are left are:
Use <script> tags and always put new files at the bottom (downside I might mess up order of the files at some point)
Use concatination mechanism, i.e. gulp-concat or grunt-concat
Both of them are ugly - first is maintenance nightmare and the second requires build step and file watcher setup...
The closest thing that I could use is require.js but I'm really disgusted with how the code looks after combining angular with require:
define([], function () {
angular.module('app').controller('BlaBlaController', function () { ... });
})
Since angular is handling all the DI for me require.js is used only for file loading which is kinda sad and makes require.js overkill for such a small task.
What I want to make is extend $inject service so that it would know how to load external files and that's how imagine it should look like:
Create di.config.js file where I specify paths for various "injectables" (much like require.conf.js
Create a simple script loader that would know how to recursively load modules
Now for DI mechanism
--
if (injectableIsLoaded) {
angular.module('app').controller('Controller', function (dep1) {});
} else if (injectableHasPath) {
diLoader.load(injectablePath).then(evalModule);
} else {
throw "No path for module";
}
Does anyone know if that would be possible at all?
What I win from this - I don't need to wrap my angular code in weird require.js calls, don't need a build step, can keep all files separete and load them separatly on demand...
I was trying to create a script that would include all the required ones to make my Angular app work but doesn't seem to work:
function include(destination) {
var e = window.document.createElement('script');
e.setAttribute('src', destination);
window.document.body.appendChild(e);
}
include('../Scripts/myApp/main.js');
include('../Scripts/myApp/appConfig.js');
include('../Scripts/myApp/MyAppController.js');
include('../Scripts/myApp/Service1.js');
include('../Scripts/myApp/Service2.js');
Unfortunately, if I try to include the app scripts like that (it works btw, they are loaded into the page perfectly), I get an error as angular detects my "ng-app='myAppName'" directive but finds no module declared for it (I suppose because the script hasn't been loaded yet when angular checks). If I then try to at least place the module instantiation on a script called directly by the HTML page (the main.js) then it complains for the controller, services, etc. not existing and if I attach those to the app inside their own files (app.controller(), app.factory, ...) it does not complain but it doesn't happen either.
Looks like loading the scripts this way makes Angular not execute them as they may be loaded too late or something. The only way of doing it has been to set a script tag calling the scripts on the HTML.
But I have the feeling that I'm missing something, that there is a way of doing this and I just don't see it.
Does anyone know how to load all the required scripts by just calling one of them? Is there something on the angular app config that can be set to do so maybe?
It just looks too dirty to make so many calls and also bad in terms of performance.
I've checked that there is something called Yeoman but it looks like too big for what I want, then I saw RequireJS but I'm unsure that's what I really want and again not sure I want to install another library just for this. Finally, I've seen that on the app config you can set where to find the views and even a way to load the controller (I think, I'm unsure of anything now my head is so dizzy) so I was thinking that maybe Angular has already something to do this which looks so natural, joining the scripts into one?
Many thanks.
This is because your scripts are being loaded in asynchronously and angular is compiling the document synchronously as the page is loaded (since I'm assumnig angular.js is on the script includes as I don't see it above). You will have to manually bootstrap the app in this scenario, as you would also have to do when using RequireJS. Here is an example... Since you are not using a library like RequireJS, I am also putting a hacky counter to see when all your scripts are loaded to callback and bootstrap angular when all scripts have loaded:
function include(destination) {
var e = window.document.createElement('script');
e.setAttribute('src', destination);
//IE detect when script has fully loaded... Increase number of loaded scripts and
//call bootstrap if all have loaded.
e.onreadystatechange = function () {
if (newjs.readyState === "loaded" || newjs.readyState === "complete") {
e.onreadystatechange = null;
loadedScripts++;
}
if (loadedScripts === numScripts) {
bootstrapAngular("myAppName");
}
}
//Other browsers. Same logic as above - Increase scripts loaded and call bootstrap when complete
e.onload = function () {
loadedScripts++;
if (loadedScripts === numScripts) {
bootstrapAngular("myAppName");
}
}
window.document.body.appendChild(e);
}
//5 scripts being loaded
var numScripts = 5;
//0 currently loaded
var loadedScripts = 0;
include('../Scripts/myApp/main.js');
include('../Scripts/myApp/appConfig.js');
include('../Scripts/myApp/MyAppController.js');
include('../Scripts/myApp/Service1.js');
include('../Scripts/myApp/Service2.js');
function bootstrapAngular(appName) {
//Assuming you are putting ng-app at document level... Change to be your specific element.
angular.bootstrap(document, [appName]);
}
The solution above is a little hacky as we are keeping track of all the scripts that have loaded. This is where a library like RequireJS would play very nicely because it would call your callback function when the scripts you want have been loaded. An example would be:
require(['angular', 'jquery', 'appConfig', 'MyAppController', 'Service1', 'Service2'], function (ng, $) {
//bootstrap when all is ready
$(function () {
ng.bootstrap(document, ["myAppName"]);
});
});
If you're using AngularJS (or any similarly advanced framework) then it's definitely time for you to learn about module loading. Injecting <script> tags quickly becomes untenable as you start to build more modules and start dealing with more complex dependency chains.
RequireJS is a great package and can do everything under the sun. Here's a tutorial on using the two together that looks pretty good. Browserify is a similar library that lets you manage dependencies with NPM. It's a little bit simpler than RequireJS, which might be a plus for you. Here's a tutorial that looks decent.
Both RequireJS and Browserify come with command-line build tools, which are great because they can combine all of your source code into a single file when you're ready to deploy your app to a server.
I should warn you, however, that this stuff isn't easy. There's a fairly steep learning curve. In all likelihood, though, this is stuff you're going to need to learn sooner or later anyway.
I was wondering if anyone had experience using require.js with the Adobe CQ5 platform. I'm writing a Chaplin.js(backbone-based) single page app that will be integrated into the rest of CQ5-based site we're working on. Chaplin requires the use of a module system like AMD/Common.js and I want to make sure my compiled javascript file will usable within CQ5's clientlibs. Is it as simple as adding require.js as a dependency in clientlibs prior to loading in my javascript modules? Someone's insight who has experience in doing this would be greatly appreciated.
I've implemented this as a solution of organize in a better way all the modules such as:
//public/js/modules/myModule.js
define('myModule',[ /* dependencies */] ,function( /* stuff here */ ))
and in the components such:
<% //components/component.jsp %>
<div>
<!-- stuff here -->
</div>
componentJS:
//components/component/clientlibs/js/component.js
require(['modules/myModule']);
and finally I've configured require (config.js) and I've stored the JSs modules in a new different design folder. Located the compiled JS after close </body> to guarantee the JS is always located at the bottom after the HTML.
<!-- page/body.jsp -->
...
<cq:includeClientLib js="specialclientlibs.footer"/>
</body>
solving with this the issue of have "ready" all the content before the JS is executed. I've had some problems to resolve with this async stuff managed for the clienlibs compilation tool, in production the problem was different, however, in development, the order in what CQ compiles the JS has produced me some lacks in terms of order of the JS. The problem really was a little bit more complex than the explanation because the number of JS was really big and the team too, but in simple terms it was the best way I've discovered so far..
The Idea
I think you can compile your Chaplin.js with one of the AMDShims to make it self contained, wraps every dependencies it needs inside a function scope, expose an entry point as global variable (which is a bad practise, but CQ do it all the time...) and then use the compiled.js inside a normal clientlib:
AMD Shims
https://github.com/jrburke/requirejs/wiki/AMD-API-Shims
Example
Here is an example of how we make the one of our libs self-contained:
https://github.com/normanzb/chuanr/blob/master/gruntfile.js
Basically, in source code level the lib "require"s the other modules just as usual. However after compiled, the generated chuanr.js file contains everything its needs, even wrapped a piece of lightweight AMD compatible implementation.
check out compiled file here: https://github.com/normanzb/chuanr/blob/master/Chuanr.js
and the source code: https://github.com/normanzb/chuanr/tree/master/src
Alternative
Alternatively rather than compile every lib you are using to be independent/self-contained, what we do in our project is simply use below amdshim instead of the real require.js. so on cq component level you can call into require() function as usual:
require(['foo/bar'], function(){});
The amd shim will not send the http request to the module immediately, instead it will wait until someone else loads the module.
and then at the bottom of the page, we collect all the dependencies, send the requirements to server side handler (scriptmananger) for dynamic merging (by internally calling into r.js):
var paths = [];
for (var path in amdShim.waiting){
paths.push(path);
}
document.write('/scriptmananger?' + paths.join(','));
The amdShim we are using: https://github.com/normanzb/amdshim/tree/exp/defer
I'm using RequireJS in my app, but don't quite well understand all aspects of it's work.
I have main.js file, where dependencies are described. I have Backbone.Router component up and running, which triggers different application classes (class which is responsible to create main view). Some code you can see here.
What I can see with requireJS: even if some view has not been yet 'required' (mean explicitly call to require('./subviews/view)), it's still loaded and inside it loads all templates (I use requireJS text plugin). If I'm adding new application but it's subviews are not ready, but I never used the application - nonexisting subviews are still loaded and I'm getting 404 errors.
Not sure I explained everything clearly, but hope you got point.
Looks like you are using the CommonJS sugared form of define(). Note that this is just a convenience for wrapping Node/CommonJS code, but AMD modules do not operate like Node modules.
An AMD loader will scan for require('') calls in the factory function of the module, and make sure to load all the modules. Otherwise, synchronous access to the that module, by doing a require('./apps/DashboardApp');, would fail in the browser, since file IO is by default async network IO.
If you want to delay loading of some scripts, then you need to use the callback-form of require:
require(['./apps/DashboardApp'], function (DashboardApp) {
});
but this call is an async call, so you would have to adjust your module's public API accordingly.
So basically, if you want to do on-demand loading of a dependency, the callback form of require is needed, given the async nature of file IO in the browser.
Because RequireJS loads all required dependencies. By taking quick look at your code I see that you load routing module and routing has:
var ViewManager = require('ViewManager');
That means it will load ViewManager, dependecies that are specified by ViewManager and other dependencies that those modules need. Essentially when you include require(...), it's the same as specifying dependency. This will be transformed by RequireJS into
define(['ViewManager'], ...)
I'm currently using underscore.js for templating in a project, templates are stored in script tags with a type of text/template and loaded by id. I'm wondering if it's possible to continue to use the same system, but move the templates to a separate file?
The only way I can think about doing this is declaring the templates as global vars in a separate file, but that seems ugly.
Note: I don't want to use Jammit or some other build system for mashing everything together into a single file at deployment time, wondering if there's another solution.
I personally use RequireJS to load my templates into a module but if you're looking for something else you can use Ajax.
As long as your templates are located on the same domain you can get them through an ajax
request. I fail back to the following code if whomever using my widget isn't using an AMD compatible library:
$.ajax({
url: root.WIDGET.BaseUrl + 'templates/widget.html',
asynx: false, // synchonous call in case code tries to use template before it's loaded
success: function (response) {
widgetTemplate = response;
}
});
This assumes you're using jQuery as well but the principle is the same if you're using something else.