I would like to use my require.js for Backbone within a Jasmine test suite; and I am working on the setup.
I've seen the use of https://github.com/scottburch/jasmine-require in this test setup: https://github.com/Patternslib/Patterns/blob/master/tests/index.html
Now, I would like to inject Backbone into the game:
describe("Basic view test", function() {
var view;
requireDependencies(["underscore", "backbone"], function(_, Backbone) {
view = Backbone.View.extend({el: "li" });
});
it("has el property", function() {
expect(view.el).toBe("li");
});
});
But my Backbone setup is not loaded correctly, see screenshot:
What's missing?
My current setup is here: https://github.com/mulderp/backbone-require-test/tree/master/spec
The problem here was the load orders of the dependencies in the global scope that is not seen in the question.
Make sure underscore is loaded before backbone.
Related
Doing my first backbone app and I'm using a structure somewhat like this tutorial
I'm wondering where the correct place for me to put my onload code, such as setting up onclick listeners etc would be?
I have:
A simple Bootstrap
require.config({
paths: {
jquery: 'libs/jquery/jquery',
underscore: 'libs/underscore/underscore',
backbone: 'libs/backbone/backbone'
}
});
require([
// Load our app module and pass it to our definition function
'app',
], function(App){
// The "app" dependency is passed in as "App"
App.initialize();
});
The App.js
define(['routers/search'], function(router){
var initialize = function(){
this.router = new router();
}
return { initialize: initialize};
});
And then a simple router that calls the relevenent function in the router also defined as a module that calls the relevent function on the router depending on the page.
My feeling is that this function in the router is where I should be putting my onload code. Is that correct?
One possibility is to use the RequireJS domReady plugin (it's available for download from their short plugins list): http://requirejs.org/docs/api.html#pageload
Here's the example they give:
require(['domReady'], function (domReady) {
domReady(function () {
//This function is called once the DOM is ready.
//It will be safe to query the DOM and manipulate
//DOM nodes in this function.
});
});
So then you can just incorporate it into your normal RequireJS structure, knowing that both the DOM is loaded plus any additional dependencies you might have listed alongside it.
I am using an .ejs template in my view. But for some reason the view does not load the given template. It returns undefined. Here's the code:
sandplate2.applicationView = Backbone.View.extend({
el: 'div',
template: _.template($("appl.ejs").html()),
initialize: function(){
console.log("Application view initialize");
_.bindAll(this, "render");
this.render();
},
render: function(){
console.log("Application view rendering");
this.$el.html(this.template());
return this;
}
});
Do I have to configure something else in order to load a template?
I structured my app using Yeoman. I used the init and backbone generators.
FYI - The template I am trying to load is loaded in the index.html using a script element.
If you built it using Yeoman, take a look at app.js to see if you are using Backbone.LayoutManager. You might have to change the configuration there for EJS to work. By default, I think it should be expecting Underscore templates.
I'm using Handlebars and I updated my app.js to look like this:
Backbone.LayoutManager.configure({
manage: true,
paths: {
layout: "templates/layouts/",
template: "templates/"
},
fetch: function(path) {
path = path + ".html";
if (!JST[path]) {
$.ajax({ url: app.root + path, async: false }).then(function(contents) {
JST[path] = Handlebars.compile(contents);
});
}
return JST[path];
}
});
I also added Handlebars to the module's define() call, passing in 'Handlebars' as a reference. You might need to do something similar for EJS.
Please try with latest backbone genearator with yeoman 1.0beta.
We have made lot of improvements on it including Precompiling ejs templates. You don't want to worry about templates, yeoman will precompile and load it for you.
A sample generated code for input-view is provided below.
Todo.Views.InputView = Backbone.View.extend({
template: JST['app/scripts/templates/input.ejs'],
render: function(){
$(this.el).html(this.template());
}
});
Conventions aside, it looks like the problem is simply your jQuery lookup.
_.template($("appl.ejs")...
$('appl.ejs') isn't a valid DOM selector unless you have an element like this in your index.html
<appl class="ejs"></appl>
If you're trying to target your template with jQuery, give it an ID or something that jQuery can find like so:
<script type="text/template" id="appl">
<div></div><!-- template html here -->
</script>
// later...
$('#appl').html()
// will get your template html
However, as others have mentioned, in a yeoman and require.js workflow, you can ask require.js to fetch the template for you as text and throw it around as a variable before creating an underscore template.
Looks like Backbone.view, meteor and handlebars have overlap functionality when it comes to manipulating a section of the DOM. I looked at the ToDo app which is suppose to use Backbone, but in reality, they only use the Router.
Backbone views also deal with templates... but they sound so different from meteor templates. Besides, it looks like both backbone & meteor can update the ui on a model update.
ok, I am lost!? Who does what?
Is Backbone really useful for a Meteor App? Can Backbone & Handle bars coexist?
and if they can, in the Meteor context, how to wire a Backbone view to a handlebars template?
EDIT: found the todo-backbone example. it seems to confirm that you can go either:
meteor + backbone + underscore templates
or... meteor + handlebars
meteor+backbone+handlebars doesn't seem like a viable option...
Thank you
It's very easy and no more work than using the Underscore template. Here's an example .html file:
<template name="user_list">
<ul>
{{#each users}}
<li>{{name}}</li>
{{/each}}
</ul>
</template>
And here's an example .js file:
Users = new Meteor.collection("users");
if (Meteor.is_client) {
Template.user_list.users = function() {
return Users.find();
}
window.UserView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
},
template: function() {
Meteor.ui.render(function() {
return Template.user_list();
});
},
render: function() {
$(el).empty().append(this.template());
}
});
}
You can then use a Router or another View to manage when you want to display the UserView just like you would in any other Backbone.js app.
The key is to use the Meteor.ui.render or other Meteor.ui method to render the HTML so that it's reactive.
I have a Backbone view as a requirejs module. The problem is that requirejs load the http://localhost/remote/script/here.js before the view is even initialized. Is it because the script isn't inside a requirejs module?
define([
'jquery',
'undescore',
'backbone',
'http://localhost/remote/script/here'
], function($, _, Backbone, Luajs){
var View = Backbone.View.extend({
initialize : function(options) {
},
render : function() {
this.$el.html('<p>my view</p>')
return this;
}
});
return View;
});
The array you have as the first argument to define is the depedencies of your view. So yes it is loaded and parsed before the View.
Also note that unless you use modified versions of backbone and underscore, they ar not AMD compliant. You will need to wrap them with a plugin to load them properly.
you try to define the view Backbone after the load the module.
You can do this, in the define () method of RequireJS. The array of this function contains parameters that defines module dependencies.
In the router.js. The firebug console alerts Backbone is null there. Why???
app.js
define([
'order!jQuery',
'order!Underscore',
'order!Backbone',
'order!router' // Request router.js
],
function($, _, Backbone, Router){
App = {
initialize: function() {
console.log("app.js initalize");
Router.initialize();
}
};
return App;
});
router.js
define([
'order!Underscore',
'order!Backbone'
],
function(_, Backbone){
var AppRouter = Backbone.Router.extend({
// Console shows Backbone is null here, why?
// I'm sure the config is correct.
routes: {
'*actions': "defaultAction"
},
defaultAction: function(actions){
// We have no matching route, lets just log what the URL was
console.log('No route:', actions);
}
});
var initialize = function(){
console.log("Router initialize");
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
Backbone does not support the AMD and it doesn't register as module. When required it registers normally as a global Backbone object, also since 1.3 Underscore doesn't support AMD neither and if you will require Backbone and Underscore under Backbone and _ namespaces they will overwrite its values in this modules scope to undefined cause of that.
jQuery supports AMD but it also registers itself as a global instance. Basically it means that you don't need to require jquery, underscore and backbone multiple times - it's enough if you do it once in your requirejs main script
The alternative is to hack the backbone.js library.
Note: This will allow you to reference Backbone.js and underscore.js library within your require.js defines, but it will not stop them from being added to the global namespace/window object. This requires a little more hacking.
Find:
(function(){var l=this,y=
Replace it with:
define('backbone',['underscore','jquery'],function(_,$){
var l = this;
(function(){var y=
Add this to the bottom of the page:
return l.Backbone;
});
Then do the same for underscore.js
Prefix the beginning with:
define('underscore',function(){
Add to the bottom of the page:
return this._;
});