Backbone.js and Handlebars - Using grunt-contrib-handlebars - backbone.js

I'm just wondering if anyone has had experience using this plugin in a backbone project.
Instead of having all my script template tags in a single index file, I wanted to house my templates in the same directory as my views that required them.
So I was hoping i could use the node option to require the local template and render to it and then append to an #id on my index file (which I'll sort out laster).
So basically I have my handlebars template (template.hbs) and its compiled js (template.js) alongside my backbone view, index.coffee.
public
|_ coffee
|_views
|_card
|_list
index.coffee
template.hbs
template.js
Just as a reference, my grunt file looks like this:
handlebars: {
compile: {
options: {
namespace: 'MyApp.Templates',
node: true
},
files: {
"public/coffee/views/card/list/template.js": "public/coffee/views/card/list/template.hbs"
}
}
},
In my backbone view (index.coffee) I was hoping to require the handlebars template like so:
class CardList extends Backbone.View
template: require('./template')
…
do some other shiz
…
render: =>
template = Handlebars.compile($(this.template).html())
html = template
model: this.model
$(this.el).html(html)
Rendering this is spitting out this error in the inspector:
Uncaught [object Object]
> template = handlebars.compile($(this.template).html());
I obviously dont know what I'm doing, so I'm hoping someone could shed some light on how I can use this plugin properly.
I'm using grunt-contrib-handlebars v0.3.5
Any help is appreciated.
Thanks

You might be able to achieve that by building the files object dynamically.
Maybe something like this, although I'm not sure if cwd supports globbing patterns. I'm also not sure if dest is relative to cwd. If this is not the case, this will not work, but it's worth a shot.
handlebars: {
dist: {
options: {
namespace: 'MyApp.Templates',
node: true
},
// Glob for a directory of files, builds the files object, then map each
// one to a new destination file.
expand: true,
cwd: './public/coffee/views/*',
src: '**/*.hbs',
dest: './',
ext: '.js'
}
},

Look inside your template.js file that you're including. grunt-comtrib-handlbars should have precompiled it into a Javascript function for you, so there's no need to call Handlebars.compile anymore. You could just remove that template = Handlebars.compile line.

Related

r.js cannot resolve dependencies mentioned in shim

I've recently joined a project which is built using Backbonejs, (uses Marionette for view rendering) + nodejs. They also use requirejs to load the backbonejs files. Would like to add at this stage, that I've never worked with backbonejs or requirejs before and hence I'm struggling with the issue I describe later.
Some code that will help explain the issue that I run into (All this code was already written by previous dev's)
Folder Structure:
/public
/js
/collection (consists all Backbone.js collections files)
/lib
/bower_components
/backone
/marionette
/etc
/models (consists all Backbone.js models files)
/views (consists all Backbone.js view files)
/main.js
/main.build.js
/app.js
/controller.js
/router.js
Code from files that I think relate to issue:
main.js
requirejs.config({
paths: {
'async': 'lib/bower_components/requirejs-plugins/src/async',
'jquery': 'lib/bower_components/jquery/dist/jquery.min',
'underscore': 'lib/bower_components/underscore/underscore-min',
'lodash': 'lib/bower_components/lodash/dist/lodash.min',
'backbone': 'lib/bower_components/backbone/backbone',
'marionette': 'lib/bower_components/marionette/lib/backbone.marionette.min',
'markercluster':'lib/markercluster',
'jquerymobile': 'lib/jquery.mobile-1.4.0.min',
'hogan': 'lib/template-2.0.0.min',
'templates': '/templates',
'real': 'lib/mainjs',
'touch': 'lib/jquery.touchSwipe.min',
'mouse': 'lib/jquery.mousewheel',
'moment': 'lib/moment-2.5.1.min',
'humanize': 'lib/bower_components/humanize-plus/public/dist/humanize.min',
'validator': 'lib/bower_components/validator-js/validator.min',
'real': 'lib/mainfile'
},
shim: {
backbone: {
deps: ["underscore"]
},
marionette: {
deps: ["backbone"]
},
templates: {
deps: ["hogan", "jquery"]
},
real: {
deps: ["jquery", "jquerymobile", "touch", "mouse"]
},
markercluster: {
exports: "MarkerClusterer"
},
humanize: {
exports: "humanize"
}
},
waitSeconds: 0
});
define('gmaps', ['async!http://maps.google.com/maps/api/js?v=3&key=AIzaSyBiV8f88yLWJ_IMSdP1fVNO1-gt3eLVSgg&sensor=true&callback=gMapsCallback'], function(){
// define('gmaps', ['http://maps.google.com/maps/api/js?v=3&sensor=false'], function(){
return window.google.maps;
});
require(['app', 'templates', 'real'], function(app) {
app.start({
version: "0.9.9"
});
});
main.build.js
({
baseUrl: ".",
name: "main",
wrapShim: true,
out: "main-built.js"
})
app.js
define(['underscore', 'controller', 'router', 'models/Cache', 'views/RootView'], function(_, Controller, Router, Cache, RootView) {
var Application = Marionette.Application.extend({
propertyListPageSize: 3,
initialize: function() {
_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };
},
onStart: function(options){
new RootView();
this.controller = new Controller();
this.router = new Router({controller: this.controller});
this.cache = new Cache();
this.context = {};
//this.evHistory = [];//#todo remove once BB/marionette navigation is in place
if(Backbone.history) Backbone.history.start({ pushState: false });
if(Backbone.history.fragment === "") this.navigate('home');
},
navigate: function(fragment, trigger, replace){
this.router.navigate(fragment, {trigger:trigger, replace:replace});
},
back: function() {
window.history.back();
}
});
app = new Application();
return app;
});
rootView.js
define(['marionette', 'views/HomeView', 'views/HeaderView', 'views/FooterView', 'views/MenuView', 'views/VideoView', 'views/LocationSearchView', 'views/LoginView', 'views/FindView', 'views/ServicesView', 'views/ValueView', 'views/PropertyListView', 'views/SideBySideView', 'views/ConfirmRegistrationView', 'views/ForgotPasswordView', 'views/CreateAccountView', 'views/UserHomeView', 'views/MyBrokerView', 'views/GiveFeedbackView', 'views/SeeFeedbackView', 'views/ViewingScheduleView', 'views/MyViewingsSummaryView', 'views/MyAccountView', 'views/ViewingConfirmView', 'views/ValueAddressPropertyListView'],
function(Marionette, HomeView, HeaderView, FooterView, MenuView, VideoView, LocationView, LoginView, FindView, ServicesView, ValueView, PropertyListView, SideBySideView, ConfirmRegistrationView, ForgotPasswordView, CreateAccountView, UserHomeView, MyBrokerView, GiveFeedbackView, SeeFeedbackView, ViewingScheduleView, MyViewingsSummaryView, MyAccountView, ViewingConfirmView, ValueAddressPropertyListView) {
var RootView = Marionette.LayoutView.extend({
...some view code
});
Use case I'm trying to solve:
So when I access the site in the browser, I notice in the debugger that it loads all the js files right at the beginning. During the load process my site is blank and user has to wait a while before he can use the site.
So what I've been able to understand is that when app is 'started' in main.js, app.js creates an instance of rootView.js , which in turn has all the views listed as dependencies. This triggers a download request for all the other views which in turn would solve their own dependencies and download all the relevant models and collections. Hence all files being downloaded when the user accessed the site.
Solution I've been trying:
Since requirejs is being used, I'm trying to use r.js to optimize and combine all the js files to reduce the number of downloads.
Issue I'm running into:
When i run r.js. i get the following error
Tracing dependencies for: main
Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
Error: Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
at Error (native)
If I add the underscore.js files directly to the specified path in the error, then I get the same error for marionette.js. What I think is happening is that app.js is not recognizing the shim'ed dependencies and hence its trying to find the files directly at specified path in the error.
Things I've tried:
- I've added wrapShim: true in the main.build.js file but that did not help
Honestly, I've been sitting on this for a couple of days and I'm not sure what I can do next and hence this post.
Any help/direction would be appreciated.
You need to include the same shim configuration in your build file, as wrapShim is not sufficient.
If shim config is used in the app during runtime, duplicate the config here. Necessary if shim config is used, so that the shim's dependencies are included in the build. Using "mainConfigFile" is a better way to pass this information though, so that it is only listed in one place. However, if mainConfigFile is not an option, the shim config can be inlined in the build config.
https://github.com/jrburke/r.js/blob/master/build/example.build.js

Compile all angular templates to one js file

I am trying to compile all angulara templates into a single js file.
Something like what ember does with ember-cli.
So I successfully managed to minify and concat all the javascript files.
I have just 2 files now vendor.js and application.js and whole lot of template files which I want to cram into templates.js.
How do I go about it? If some one could give step by step explanation, please. Any links would be appreciated too.
Surprisingly there is no information about this anywhere.
I am using mimosa as build tool, it seemed to me the easiest.
Here is my mimosa config:
exports.config = {
modules: [
"copy",
"stylus",
"minify-css",
"minify-js",
"combine",
"htmlclean",
"html-templates"
],
watch: {
sourceDir: "app",
compiledDir: "public",
javascriptDir: "js",
exclude: [/[/\\](\.|~)[^/\\]+$/]
},
vendor: {
javascripts: "vendor/js"
},
stylus: {
sourceMap: false
},
combine: {
folders: [
{
folder:"vendor/js",
output:"vendor.js",
order: [
"angular.js"
]
},
{
folder:"js",
output:"main.js",
order: [
"application/main.js"
]
}
]
},
htmlclean: {
extensions:["html"]
},
htmlTemplates: {
extensions: ["tpl"]
},
template: {
outputFileName: "templates"
}
}
It does generate templates.js file without any errors. But when I link it, angular spits a bunch of errors.
Once compiled, how do I actually call those templates from ng-include and from the route provider?
I assume that it is the same as I would call a script template using the id which in my case is derived from template original file name, right?
Maybe I am missing some important steps.
The build tool is not important here although desirable. If some one could show how to do it manually without a build tool I would figure out the rest.
Thanks.
I'm using Gulp as my build tool, and in that, there's a plugin gulp-angular-templatecache which pre-compiles and registers all templates for your module in the angular $templateCache - no changes are required to any of the calling code to use these. EDIT: The Angular documentation for $templateCache explains how the templateCache works.
It might be worth reading through the documentation for gulp-angular-templatecache to see how that pre-populates the $templateCache to see if you can crib something that would work with your build process.
Here's my gulp task that does the job:
var templateCache = require('gulp-angular-templatecache');
gulp.task('buildjstemplates', function () {
return gulp.src(['public/javascripts/app/**/*.html'])
.pipe(templateCache({module: 'app'}))
.pipe(gulp.dest('public/javascripts/app/'));
});

Ext JS 5.1 won't load use appfolder path to find js

I am trying to clean up and restructure my javascript in my app, but once I change it it stops working. I am using ext scheduler in my app so that might be the problem. Here is how I want to set up
/ext/scheduler-3.0.0(all core code for schedule components go here)
/ext-all.js
/myscheduler(custom code for schedule components go here)
--/global/
----/view/
------globalscheduling.js
/model/
And This is how I start my app
ExtLatest.Loader.setConfig({enabled: true});
ExtLatest.define("scheduler.Application", {
extend: "Ext.app.Application",
requires: ["myscheduler.global.view.globalschedulegrid"],
name: "scheduler",
appFolder: "",
launch: function () {
getData()
}
});
However extjs still trying to go to https://c.na17.visual.force.com/apex/myscheduler/global/view/globalschedulegrid.js to find my view file what I doing wrong here?
Ext.Loader.setPath('myscheduler.global.view.globalschedulegrid', 'path to my scheduler');
OR
Ext.Loader.setConfig({
paths: {
'myscheduler': 'myscheduler',
}
});
Link to 5.0 Doc (Not sure which version you are using but it's the same)
5.1 as noted in the title. I didn't have to set the path for globalschedulegrid. Just specifying the paths in setConfig like this
ExtLatest.Loader.setConfig({enabled: true,
paths: {
'scheduler': "{!URLFOR($Resource.ConnectWeb, 'scripts/libs/scheduler')}"
}
});

Grunt Locales- Manually Translation Required?

I need my app internationalized and have setting grunt-locales. Simply by adding the data-localize directive the text inside should be translated. My initConfig is configured as such-
locales: {
options: {
locales: ['en_US', 'de_DE']
},
update: {
src: 'app/views/*.html',
dest: 'app/js/locales/{locale}/i18n.json'
},
build: {
src: 'app/js/locales/**/i18n.json',
dest: 'app/js/locales/{locale}/i18n.js'
},
'export': {
src: 'app/js/locales/**/i18n.json',
dest: 'app/js/locales/{locale}/i18n.csv'
},
'import': {
src: 'app/js/locales/**/i18n.csv',
dest: 'app/js/locales/{locale}/i18n.json'
}
},
And data-localized is used in one case in the app here-
<a href="#" data-localize>Forgot your username or password?</a>
What that's done when grunt locales are built is create this file under app/js/locales/de_DE/i18n.json
{
"Forgot your username or password?": "Forgot your username or password?"
}
Which I don't have to tell you is not translated. So will I need to go through and translate this for every language or am I missing something?
Yes, grunt-locales is a tool used for extracting the necessary information from your templates and js files to build the above json objects for all locales that the developer specifies. You will need a translator to go through and translate the strings within the json objects.
Once they have done so, you simply re-build the i18n.json files to create i18n.js files using grunt locales:build. The i18n.js files are used by the localize directive service and filters.
Very late answer, but if you are still interested I hope this helps.

RequireJs text plugin doesn't appear to be loading template into DOM

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

Resources