RequireJs text plugin doesn't appear to be loading template into DOM - backbone.js

I'm building an app with backbone/RequireJs on the front end and node/express on the backend. All written in CoffeeScript. I'm having trouble getting HTML template files to load properly with the text! plugin.
My Config is a s follows:
require.config
paths:
app: 'app'
jquery: 'libs/jquery-1.7.1'
jqueryTmpl: 'libs/jquery.tmpl'
jqueryUI: 'libs/jquery-ui-1.8.18.custom.min'
underscore: 'libs/underscore.amd'
order: 'libs/order'
backbone: 'libs/backbone.amd'
handlebars: 'libs/handlebars'
bootstrap: 'libs/bootstrap/js/bootstrap'
marionette: 'libs/backbone.marionette'
modelbinding: 'libs/backbone.modelbinding'
validation: 'libs/backbone.validation'
jqueryQtip: 'libs/jquery.qtip'
utils: 'utils'
jqueryDatatables: 'libs/datatables/jquery.dataTables'
DT_bootstrap: 'libs/datatables/DT_bootstrap'
user_maintenance: 'templates/tmpl.user.maintenance'
require [
"require",
"jquery",
"underscore",
"backbone",
"order!jqueryTmpl",
"order!marionette",
"order!validation",
"app"
]
This is the start of one of my views :-
define (require) ->
Backbone = require 'backbone'
Backbone.ModelBinding = require 'modelbinding'
require 'jqueryUI'
require 'jqueryQtip'
require '../../scripts/text!user_maintenance.html'
Utils = require '../../scripts/Utils.js'
class UserMaintenanceView extends Backbone.Marionette.ItemView
template: "#tmpl-user-maintenance"
className: "row"
.
.
.
If I look at the network tab in Chrome developer tools, the /scripts/templates/tmpl.user.maintenance.html file has been loaded via a GET. Clicking on it shows the contents that appear like so. (I removed the contents for brevity, its just HTML)
<script type="text/x-jquery-tmpl" id="tmpl-user-maintenance">
</script>
I don't get any script errors, but the template doesn't appear in the DOM and my view is not rendered. If I simply paste the HTML template into the main HTML file and don't try to load with the text plugin everything works fine. But I want to break my templates into separate files.
Any idea what I'm doing wrong?

The text!plugin does not inject the text into the DOM, it only loads the given file and it is up to you to process the content. See http://requirejs.org/docs/api.html#text for more information.

You'll have to assign/append the returned HTML from the template call into the DOM.
Example:
$('.someClass').html(Template, {}); // where {} === the data object for the template

Related

$templatecache and $routeprovider - 404

I am using the grunt task grunt-angular-templates for precompiling angular templates in my app, which has resulted in output like that:
$templateCache.put('src/ng-app/views/story/page.html',
//...html
but this route is throwing a 404 on the template file
.when('/:pageId.aspx', {
templateUrl: 'src/ng-app/views/story/page.html',
I've seen another post about setting the ID of the template and specifying that in the route but I don't know how to do that for external files - the example uses inline templates like that:
<script type="text/ng-template" id="login.html">
login.html
</script>
Simple solution in the end - I had to ensure the view script generated by ngTemplates was included before route declarations (i have those in a separate script routes.js)
Its also key to ensure the module is the same in ngTemplates grunt:
var app = angular.module('MyApp.Web'...
ngtemplates: {
app: {
src: 'src/ng-app/views/**/*.html',
dest: 'dist/src/js/ng-views.js',
options: {
module: 'MyApp.Web'
}
}
}
Adding templates as a dependency made no discernible difference (as I found on another post):
angular.module('templates', []);
var app = angular.module('MyApp.Web', [
'ngRoute',
'youtube-embed',
'templates'
]);

Using RequireJS with AngularJS. Problems loading AngularJS

I am creating a NodeJS app which uses AngularJS for it's front-end. I am Also using RequireJS to load in the JS dependencies and then instantiate the Angular app. Here is what I am trying to do:
Within my HTML file (written in Jade) I include the RequireJS files and then call the RequireJS config using the 'data-main' attribute:
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
script(type="text/javascript" src="/bower_components/requirejs/require.js" data-main="/main.js")
My main.js file looks as follows:
"use strict";
function(require) {
require(['/assets/requiredPathsAndShim.js'], function(requiredPathsAndShim) {
require.config({
maps : {
// Maps
},
paths : requiredPathsAndShim.paths,
shim : {
// Modules and their dependent modules
}
});
angular.bootstrap(document, ['appNameInHere']);
});
})(require);
I have an external file which contains an object with my routes '/assets/requiredPathsAndShim.js' and it looks like follows:
"use strict";
(function(define) {
define([], function() {
return {
paths : {
'angular' : '/bower_components/angular/angular'
}
};
});
})(define);
I will add that my NodeJS/Express app has the 'bower_components' folder set to serve static files and this is working fine.
Whenever I try and instantiate the AngularJS app using the 'angular.bootstrap...' method it tells me Angular is not defined. I can't see why this is happening and haven't been able to figure it out yet. O can't see any problem with my routes to the Angular files. Can anyone see or suggest why this may be happening?
Thanks!
Just managed to crack it! I had to place the 'angular.bootstrap' call in a callback function of the require.config method as the app was trying to call AngularJS before it had been defined.
Hope this helps anyone in the future.

In the ui-router-extras demo, I want to use ng-grid in module1 but it's not working

In my project, I hope the lazy loaded modules can add their own state, so I found the ui-router-extras. It's really useful for me, but when I want to use ng-grid in the lazy loaded module like the module1 in demo, the module1.js file looks like this:
define(['angularAMD', 'ngGrid'], function () {
var app = angular.module("module1", ['ui.router','ngGrid']);
...
and the main.js file looks like this:
require.config({
waitSeconds: 100,
paths: {
"angularAMD": "../../lib/angularAMD",
...
"jQuery": "../../lib/jquery",
"ngGrid": "../../lib/ng-grid-2.0.14.debug"
},
shim: {
"angular": { exports: "angular" },
...
"ngGrid": ["angular", "jQuery"],
},
deps: ["app"]
});
But I got an exception from ng-grid : "Uncaught TypeError: Cannot read property 'factory' of undefined". I found the ng-grid source code where the exception happened:
angular.module('ngGrid.services').factory('$domUtilityService',['$utilityService', '$window', function($utils, $window) {....}
So I found in the lazy loaded module, get module by angular.module('mymodule') returns the undefined. Is there something I forgot to write, or is there another way to use ng-grid or other plugin in the lazy load module with ui-router-extras future?
You need to use the 'ngload' plugin for AngularAMD to load a module on the fly.
Excerpt from the docs:
3rd Party AngularJS Modules
3rd party AngularJS modules, meaning any module created using angular.module syntax, can be loaded as any normal JavaScript file before angularAMD.bootstrap is called. After bootstraping, any AngularJS module must be loaded using the included ngload RequireJS plugin.
define(['app', 'ngload!dataServices'], function (app) {...});
In case you need to load your module using the RequireJS plugin or if you have complex dependecies, you can create a wrapper RequireJS module as below:
define(['angularAMD', 'ui-bootstrap'], function (angularAMD) {
angularAMD.processQueue();
});
In this case, all dependencies will be queued up and when .processQueue() is called, it will go through the queue and copy them into current app using app.register:
https://github.com/marcoslin/angularAMD

Chrome extension using RequireJS, Backbone (Chaplin) conflicts

I am creating a Google Chrome Extension that have to add content on the visited websites (like a toolbox).
I have to use RequireJS and BackboneJS (Chaplin) and everything is ok except when i'm visiting a website using RequireJS (and Backbone, but the problem seems to come from RequireJS conflicts).
(This is when I use content scripts to include a -script- tag that includes RequireJS.)
I suppose it's normal to have conflicts if I add content directly in the page so I tried the solution here : Loading multiple instances of requireJS and Backbone
It seems to work (for now), but the website is trying to reload his own RequireJS file (with his path, but in my extension) before loading mine and I'm afraid it could lead to unexpected behaviour.
Plus, I have to precise my file paths in requirejs.config or it's looking for them in Bitbucket sources (cloudfront). (Maybe it's normal though)
Example with bitbucket :
Denying load of chrome-extension://mgncmiffelpdhlbkkmmaedbodabdchea/https://d3oaxc4q5k2d6q.cloudfront.net/m/7aaf1677069c/amd/build/main.js?8uxr. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
<--------- This file is Bitbucket's RequireJS, Bitbucket is still working fine though
Is there another solution I didn't find yet ? Or am I doing it wrong ? I'm a beginner with RequireJS (and Chrome ext.. and Backbone...) so I might have missed something.
Here is the Content script part in manifest.json
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"]
}],
init-app.js is Rob's script
require.load = function(context, moduleName, url) {
url = chrome.extension.getURL(url);
var x = new XMLHttpRequest();
// Append Math.random()... to bust the cache
x.open('GET', url + '?' + Math.random().toString(36).slice(-4));
x.onload = function() {
var code = x.responseText;
x += '\n//# sourceURL=' + url; // Optional, for debugging.
window.eval(code);
context.completeLoad(moduleName);
};
x.onerror = function() {
// Log error if you wish. This is usually not needed, because
// Chrome's developer tools does already log "404 Not found"
// errors for scripts to the console.
};
x.send();
};
and main.js contain requirejs.config + app
// Configure the AMD module loader
requirejs.config({
skipDataMain: true,
// The path where your JavaScripts are located
baseUrl: 'extension',
// Specify the paths of vendor libraries
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/lodash/dist/lodash',
backbone: '../bower_components/backbone/backbone',
handlebars: '../bower_components/handlebars/handlebars',
text: '../bower_components/requirejs-text/text',
chaplin: '../bower_components/chaplin/chaplin',
application: '/extension/application',
routes: '/extension/routes',
},
// Underscore and Backbone are not AMD-capable per default,
// so we need to use the AMD wrapping of RequireJS
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
handlebars: {
exports: 'Handlebars'
}
}
// For easier development, disable browser caching
// Of course, this should be removed in a production environment
//, urlArgs: 'bust=' + (new Date()).getTime()
});
// Bootstrap the application
require(['application', 'routes'], function(Application, routes) {
new Application({routes: routes, controllerPath: 'scripts/controllers/', controllerSuffix: '-controller'});
});
It works on gooogle.com for instance, but I get
GET chrome-extension://ccgfmmmnebacpnbdpdnphmnmicaooddg/extension/Home.js?9zfr net::ERR_FILE_NOT_FOUND
on https://www.cloud9trader.com (website using RequireJS) because it has
<script data-main="/0.2.59/scripts/Home.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js"></script>
in its source. To summarize I just need the script to ignore the "current" website Require file.
The skipDataMain option is synchronously checked when require.js is loaded. Setting this variable after loading require.js has no effect on the loader any more, because the data-main scan has already run at that point.
The correct way to skip data-main is to declare the configuration before loading require.js, as follows:
// extension/config.js
var require = {
skipDataMain: true
};
manifest.json:
{
...
"content_scripts": [{
"matches": ["https://*/*", "http://*/*"],
"js": [
"extension/config.js",
"bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"
]
}],
...
}

Using dust.js templates with backbone.marionette and require.js

I am building a large scale application using backbone and marionette. Instead of using underscore templating engine, I am planning to use dust.js.
I have found marionette-dust plugin which could do the job but currently I am at a loss in understanding how to use it with require.js. Also, it there a better way of implementing dust besides using this plugin?
Feedback appreciated.
Following is the code in sample application
testView.js
define(["app", "templates/test.dust"], function(app, testTpl){
app.module("test.view", function(view, app, Backbone, Marionette, $, _){
view.list = Marionette.ItemView.extend({
template: testTpl,
});
});
return app.test.view;
});
test.dust
(function() {
dust.register("demo", body_0);
function body_0(chk, ctx) {
return chk.write("This is Dust.js Test");
}
return body_0;
})();
main.js
requirejs.config({
baseUrl: "assets/js",
paths: {
backbone: "vendor/backbone-min",
jquery: "vendor/jquery-min",
marionette: "vendor/backbone.marionette-min",
tpl: "vendor/tpl",
underscore: "vendor/underscore-min",
dust: "vendor/dust",
dustHelpers: 'vendor/dust-helpers',
dustMarionette: "vendor/backbone.marionette.dust",
templates: 'templates/compiled',
},
shim: {
jquery: {
exports: 'jquery'
},
underscore: {
exports: "_"
},
backbone: {
deps: ["jquery", "underscore"],
exports: "Backbone"
},
marionette: {
deps: ["backbone"],
exports: "Marionette"
},
dust: {
exports: 'dust'
},
dustHelpers: ['dust'],
templates: ['dust', 'dustMarionette']
}
});
require(["app"], function(app){
app.start();
});
I'm the author of that plugin. Basically you need to define the three dependencies
'backbone', 'dust' and 'marionette' in your Requirejs config file and then define
the AMD version of this module as a dependency after marionette during the initial application setup.
The plugin is written under the assumption that you have compiled all of your dust templates and they are in the dust cache (you should find details on how to do that in the dust documentation). You may see why if you have a look at the plugin source code. Inside of each your views simply set the template property to the name of the template in the dust cache that you want to use.
The plugin overrides the render function in the Marionette.Renderer object that is used by all views. So basically under the hood, Marionette is calling the render function of this plugin which renders the templates with Dust and then returns the HTML. Marionette's documentation here mentions this is the best way to provide custom rendering.
I've tried to outline all of this in the Readme file for the plugin but if you think it can be improved (which I don't doubt it can) then please let me know which areas are unclear.
I've written a Yeoman generator called generator-maryo to provide scaffolding for a marionette and dust web application. There are still a few todos in there but it provides you with a good foundation on how to use the marionette-dust plugin with marionette. If I can be more explicit in any area then let me know.
EDIT AFTER CODE ADDED TO QUESTION
The marionette-dust plugin is access by template name, not by the compiled template function. So essentially you need to run the compiled template function (which it should do by itself as it's an anonymous function), then it will be placed in the dust cache under the name "demo". So your item view should look like:
define(["app", "templates/test.dust"], function(app, testTpl){
app.module("test.view", function(view, app, Backbone, Marionette, $, _){
view.list = Marionette.ItemView.extend({
template: "demo",
});
});
return app.test.view;
});
Note that all I did was set the template property to "demo". Also, is this all of the code for the demo app? You need to actually show the view using a region or you can just call the render function on the view manually. To get something working quickly, you can do something like this:
myView = new app.test.view;
$('body').append(myView.render().$el);
Also, why are you wrapping the view in a module block? As you're already using RequireJS, Marionette's module system is not really necessary. I am in the process of writing a demo app for Marionette using generator-maryo which will probably explain a lot. Keep an eye out for it in the github repo.

Resources