How to wire a Backbone View to a meteor handlebars template? - backbone.js

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.

Related

Routing in Marionette using a Marionette Object — Can't make it work

I am trying Marionette for the first time and am very much confused about its routing.
Here's what I have so far: a Backbone model, a Backbone collection, a Marionette Layout view, a Marionette Collection View, a couple of Marionette Item views. The views are hooked together in a Marionette object like so:
var Orchestrator = Marionette.Object.extend({
initialize: function(layout) {
this.layout = layout;
// adding a collection view
// showing the collection view in the layout view
// all is honky-dory
},
// And here is a test function I want to call when I go to localhost:8080/#test:
test: function() {
console.log('HELLOOO!!!');
}
});
And here comes the confusion. I am trying to set up the router:
var Router = Marionette.AppRouter.extend({
initialize: function(controller) {
// the 'controller' is an instance of my Orchestrator object defined above
this.controller = controller;
console.log(this.controller.test); // the method shows up correctly
this.appRoutes = {
'test': 'test'
};
}
});
Then I initialize the Router: var router = new Router(orchestrator); and start the Marionette app.
The console.log statement in the router shows that 1) it is being initialized, 2) the 'controller' is the correct Marionette object, and 3) the 'controller' has the method test on it.
However, when I navigate to localhost:8080/#test (my app is served at localhost:8080), I do not see any evidence that the test method of the Orchestrator object has been called — my 'HELLO' message does not appear in the console. There are no errors in the console either.
Could you please suggest what the matter might be and how to fix this? I've been searching through docs, but have not found any answers so far.
D'oh! Forgot to call Backbone.history.start();. Stupid me!

Routing Cakephp with BACKBONE JS

<script>
var HomeView = Backbone.View.extend({
template: '<h1>Home</h1>',
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
var AboutView = Backbone.View.extend({
template: '<h1>About</h1>',
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
var AppRouter = Backbone.Router.extend({
routes: {
'': 'homeRoute',
'home': 'homeRoute',
'about': 'aboutRoute',
},
homeRoute: function () {
var homeView = new HomeView();
$(".content").html(homeView.el);
},
aboutRoute: function () {
var aboutView = new AboutView();
$(".content").html(aboutView.el);
}
});
var appRouter = new AppRouter();
Backbone.history.start();
</script>
<ul>
<li><?php echo $this->Html->link('Home',array('controller' =>'pages','action' => 'home')); ?></li>
<li><?php echo $this->Html->link('About',array('controller' =>'pages','action' => 'about')); ?></li>
</ul>
How to convert the code above to make backbone.js like this in manual coding I seen in the NET.
<div id="navigation">
Home
About
</div>
<div class="content">
</div>
Im just new to this one guys please help me. Im reading Backbone js now, can anybody help me with this problem. If you have experience with cakephp backbone js.. I also wanted using it on CRUD cakephp.
Just a friendly reminder: CakePHP is a server-side library whose responsibility is to send HTML or some serialized data (JSON, XML, binary, etc.) to your browser.
backbone.js is a client-side library that provides building blocks for client-side content and event-handling. The only relationship between CakePHP and backbone.js is that CakePHP is perhaps responsible for delivering whatever scripts and assets your client needs. There is probably nothing that you can learn about backbone.js that is specific to CakePHP. So try to think in terms of "client" and "server" rather than backbone and Cake.
Now, almost every web site will have some links. When a user clicks a link the browser treats it as an event and responds accordingly.
JavaScript clients can extend the browser functionality by getting in between the browser and the event - that is, by telling the browser, "I will handle this event, not you."
In backbone.js, the Backbone.history.start method is what tells the browser to back off and let backbone take care of certain events:
window.onhashchange: This is supported by all browsers and is called whenever the "hash" part of the URL changes; for instance, if you click an anchor link Go somewhere the event will fire and the listener will be called. If the listener returns something other than true then the browser will not handle the event as usual, but otherwise, your browser URL will change to /path/to/page#somewhere
history.pushState and window.onpopstate: This is a new feature in HTML5 so it is not supported by older browsers, but at this point, it is fairly widely-adopted and available. Basically, this is a powerful API that gives client developers a way to manipulate and rewrite the history of a browser's navigation - not just the hash part of the URL, the whole thing - using syntax (see link) like history.pushState(undefined, undefined, 'not/a/real/path');. Correspondingly, there is an event handler window.onpopstate that will be called whenever the browser moves forward or backward in the history chain, but !NOT! when we call history.pushState or history.replaceState. This is a good behavior and by design.
You need to decide what you want to use - hash-style URLs and/or "real" URLs - and then configure Backbone.history.start arguments accordingly. You can use both but I don't recommend it.
Finally, please make sure **you are setting everything up in your JavaScript wrapped in a $(document).ready(function() { ... });. Right now you are not.

how can I use external templates with Marionette Backbone

How exactly do I use an external template for my Marionette ItemView?
I read so many 'methods' I can't get any of them working...
JST Method ... I'm not on rails so can't use this
Backbone.Marionette.Renderer.render = (template, data) ->
path = JST["backbone/apps/" + template]
unless path
throw "Template #{template} not found!"
path(data)
RequireJS Method ... I'm not using require js
var tpl = require(inject[this.templateName]);
this.template = _.template(tpl);
What method should I be using?
I use Handlebars for my templating engine and hbs to retrieve the precompiled resource via require.js, that way all I have to do is define the template and set it as the ItemView's template; marionette does the rest.
Here's an example
View - welcome.js
define([
'app',
'jquery',
'backbone',
'marionette',
'hbs!templates/welcome'
],
function(App, $, Backbone, Marionette, template) {
return Backbone.Marionette.ItemView.extend({
template: template,
});
});
Template - welcome.html
<div>
<h1>Hello World</h1>
</div>
If you're not using handlebars, or can't use hbs, then something similar can be done with text.js - https://github.com/requirejs/text

How to use jasmine-require with Backbone?

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.

Template not loading in a Backbone.js app - built using Yeoman

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.

Resources