Backbone.js set View attribute - javascript

I'm kind of new to Backbone and I'm having trouble understanding how to set the attributes of a View. I'm using a view without a model.
This is the View:
var OperationErrorView = Backbone.View.extend({
attributes: {},
render: function(){
var html = "<h3>" + this.attributes.get("error") +"</h3>";
$(this.el).html(html);
}
})
Then later on:
if (errors.length > 0){
errors.forEach(function(error){
// var errorView = new OperationErrorView({attributes: {"error": error} }); Doesn't work
var errorView = new OperationErrorView();
errorView.set({attributes: {"error": error}})
errorView.render()
$("#formAdd_errors").append(errorView.$el.html());
});
}
Which is the correct approach to do this? Right now it doesn't work: When I try the method that is not commented out, it gives me the error TypeError: errorView.set is not a function, if I try it the first way, it just doesn't call the render() function.
UPDATE:
var OperationErrorView = Backbone.View.extend({
attributes: {},
initialize: function(attributes){
this.attributes = attributes;
},
render: function(){
var html = "<h3>" + this.attributes.get("error") +"</h3>";
console.log("html");
$(this.el).html(html);
}
})
if (errors.length > 0){
errors.forEach(function(error){
console.log(error);
var errorView = new OperationErrorView({"error": error});
errorView.render()
$("#formAdd_errors").append(errorView.$el.html());
});
}
I tried including this.render() in the initialize function. Doesn't work. Doesn't even call the render function. Why?

A couple things:
set is not a function of a Backbone View. Check the API
In your commented code, calling new OperationErrorView(...) does not automatically evoke the render function. You have to do this manually.
The attributes property of the View does not have a get method. Again, Check the API
So, what should you do?
Research different ways to initialize a View with properties. Then figure out how to get those properties on the HTML that your View controls.
Here's a bit to get you started
var OperationErrorView = Backbone.View.extend({
tagName: 'h3',
initialize: function(attributes) {
this.attributes = attributes;
this.render();
},
render: function(){
// attach attributes to this.$el, or this.el, here
// insert the element into the DOM
$('#formAdd_errors').append(this.$el);
}
});
// later in your code
if ( errors.length > 0 ) {
errors.forEach(function(error) {
new OperationErrorView({ error: error });
});
}

Thanks to chazsolo for the answer, all the info is there. So, I'll write the code that worked just in case someone finds it useful someday:
var OperationErrorView = Backbone.View.extend({
initialize: function(attributes){
this.attributes = attributes;
},
render: function(){
var html = "<h3>" + this.attributes.error +"</h3>";
$(this.el).html(html);
}
});
if (errors.length > 0){
errors.forEach(function(error){
var errorView = new OperationErrorView({'error':error});
errorView.render()
$("#formAdd_errors").append(errorView.$el.html());
});
}

Related

Marionette CollectionView not re-rendering after collection.fetch

I have an 'email' style app that displays messages grouped by the date. When the app loads, a shallow collection of messages are fetched and loaded into a backbone collection. Each model in the collection represents a list of messages within a grouping. The MessageGroup represents a group of messages and the MessagesView displays the groups of messages.
This all works well until the collection is fetched again like after a filter is applied, only the group headers are displayed, not the messages inside. I've tried triggering an event that the MessagesView can listen for, then re-render itself but I get an error: listening.obj.off is not a function.
var MessageModel = Backbone.Model.extend({});
var MessageCollection = Backbone.Collection.extend({
model: MessageModel
});
var GroupModel = Backbone.Model.extend({});
var GroupCollection = Backbone.Collection.extend({
model: GroupModel,
url: '/messages/recipient',
parse: function (response) {
// Create a grouped JSON to render nested views with
var messageArray = [];
var groupedlist = _.groupBy(response.messages, function(model) {
return model.publishDate;
});
_.forEach(groupedlist, function(n, key) {
var grouping = {};
grouping.group = key;
grouping.list = n;
messageArray.push(grouping);
});
return messageArray;
},
fetchMessages: function() {
this.fetch({
data: filtermodel.toJSON(),
success: function() {
var messagecollection = new MessageCollection();
// Loop through each grouping and set sub-collections
groupcollection.each(function(group) {
var list = group.get('list');
messagecollection.reset(list);
group.set('list', messagecollection);
});
}
});
}
});
// Model to track applied filters
var FilterModel = Backbone.Model.extend({
defaults: {
folder: 0
}
});
// ------------------------ VIEWS ------------- //
// View for a single Message
var MessageView = Backbone.Marionette.ItemView.extend({
template: require('../../../templates/activities/message-item.ejs'),
events: { 'click li.item': 'getMessageDetail' },
getMessageDetail: function(e){
this.triggerMethod('showDetail', this.model);
//initMessageDetail(this.model);
}
});
// Grouped container view for a list of Messages within a group
var MessageGroup = Backbone.Marionette.CompositeView.extend({
template: require('../../../templates/activities/message-list.ejs'),
className: "list-view-group-container",
childView: MessageView,
childViewContainer: "ul.viewcontainer",
initialize: function() {
this.collection = this.model.get('list');
}
});
// Top level view for all grouped messages
var MessagesView = Backbone.Marionette.CollectionView.extend({
childView: MessageGroup,
initialize: function() {
this.collection.on('change', this.log, this);
},
log: function() {
console.log('triggered log');
}
});
// View for selected message detail
var MessageDetailView = Backbone.Marionette.ItemView.extend({
template: require('../../../templates/activities/message-detail.ejs'),
className: "message-content-wrapper"
});
// View for filter selection bar
var MessageFilterView = Backbone.Marionette.ItemView.extend({
template: require('../../../templates/activities/message-filter-bar.ejs'),
events: {
'click #search-btn': function() {
filtermodel.set('search', $('#search-input').val());
groupcollection.fetchMessages();
}
}
});
var filtermodel = new FilterModel();
var groupcollection = new GroupCollection();
// Fetch messages first run
groupcollection.fetchMessages();
// LayoutView to display in center panel of application
module.exports = ViewMessages = Marionette.LayoutView.extend({
template: require('../../../templates/activities/viewmessages.ejs'),
className: 'content full-height',
regions: {
'messagelistregion': '#messageList',
'messagedetailregion': '.message-detail',
'messagefilterregion': '.filter-bar'
},
childEvents: { 'showDetail': 'onMessageSelected' },
onMessageSelected: function (childView, childViewModel) {
var that = this;
var detailModel = childViewModel.clone();
var messageDetailView = new MessageDetailView({model:detailModel});
that.messagedetailregion.show(messageDetailView);
},
onShow: function(){
var that = this;
var messagesview = new MessagesView({
collection: groupcollection
});
var messageFilterView = new MessageFilterView();
that.messagelistregion.show(messagesview);
$("#messageList").ioslist();
that.messagefilterregion.show(messageFilterView);
this.messagedetailregion.on('show', function() {
console.log('message detail region shown:' + that.messagedetailregion.currentView);
})
}
});
I'm thinking its because the work that is done to build out the groupings of messages inside the success callback doesn't finish before the reset event is triggered and the view is refreshed. How can I get the MessagesView to update after subsequent fetches?
UPDATE:
I moved the post-success logic of grouping the collection into its hierarchical tree/leaf structure to a custom event (fetchSuccess) in the top level collectionview (MessagesView):
var MessagesView = Backbone.Marionette.CollectionView.extend({
childView: MessageGroup,
initialize: function() {
this.collection.on('fetch:success', this.fetchSuccess, this);
},
fetchSuccess: function() {
var messagecollection = new MessageCollection();
groupcollection.each(function(group) {
var list = group.get('list');
messagecollection.reset(list);
group.set('list', messagecollection);
});
}
});
It is being triggered in the success callback of the fetch. I'm pretty sure this is a good way of rendering the collection, but I cant seem to get around the error in Marionette:
**Uncaught TypeError: listening.obj.off is not a function**
Anyone have any ideas why this collectionview will not re-render??
I was able to determine that the creation of the models in the collection occurred after the reset event, but before the structure of the nested models was built out:
success: function() {
var messagecollection = new MessageCollection();
// Loop through each grouping and set sub-collections
groupcollection.each(function(group) {
var list = group.get('list');
messagecollection.reset(list);
group.set('list', messagecollection);
});
};
After any filter event, grouping, sorting etc, the collection structure needs to be modified into this nested hierarchy each time. The view was picking up the reset event before the structure was built out so the child views had no data to render. I fixed this by cloning the original collection after the changes and having the views render the cloned collection:
groupcollection.fetch({
reset: true,
data: filtermodel.toJSON(),
success: function() {
groupcollection.each(function(group) {
var list = group.get('list');
var messagecollection = new MessageCollection(list);
group.set('list', messagecollection);
});
filteredcollection.reset(groupcollection.toJSON());
}
});

Use custom helpers in Handlebars which are loaded from an api

With ember-cli I use some handlebars which are loaded from an api. I'm using variables in the Handlebars templates, but now it would be nice to get render, bind-attr and a custom-helper working.
// app/helpers/view-helper.js
var ViewTemplateHelper = Ember.Handlebars.makeBoundHelper(function(template, context) {
if (Ember.isEmpty(template)) {
return;
}
else if (Ember.isArray(template)) {
template = template.get('firstObject.value');
}
else {
template = template.get('value');
}
context = context || this;
var dummy = Ember.View.extend({
classNames: ['view-template'],
context: context,
template: Ember.Handlebars.compile(template)
});
var view = dummy.create();
var $elem = null;
Ember.run(function() {
$elem = $('<div>');
view.appendTo($elem);
});
return new Ember.Handlebars.SafeString($elem.html());
});
export default ViewTemplateHelper;
Just calling the helper like this
// app/templates/blog-list.hbs
{{view-template blog}}
When I use this Handlebar this code works fine
<h1>{{blog.title}}</h1>
But when using render, bind-attr, custom-helper,
{{render 'blog/cover' blog.image}}
the console logs an error:
Uncaught TypeError: Cannot read property 'container' of null
Does anyone know how to use custom helpers in the Handlebars loaded from the api?
The solution was to use this code
options.hash.content = template;
return Ember.Handlebars.helpers.view.call(this, view, options);
The full helper
var ViewTemplateHelper = Ember.Handlebars.makeBoundHelper(function(template, options) {
if (Ember.isEmpty(template)) {
return;
}
else if (Ember.isArray(template)) {
template = template.get('firstObject.value');
}
else {
template = template.get('value');
}
var dummy = Ember.View.extend({
classNames: ['view-template'],
template: Ember.Handlebars.compile(template)
});
var view = dummy.create();
options.hash.content = template;
return Ember.Handlebars.helpers.view.call(this, view, options);
});
export default ViewTemplateHelper;
Thanks to http://discuss.emberjs.com/t/how-do-you-render-a-view-from-within-a-handlebars-helper-that-takes-dynamic-content/984/3?u=harianus

Backbone Marionette Layout Region not closing when returning to module

I have a Backbone Marionette application whose layout's regions are not working properly. My app is structured using Require modules and some of these modules' regions are failing to close themselves when the module has been returned to a second time. The first time through the regions are closing as expected but upon return the layout object no longer contains the region objects it did during the first visit: I am using the browser debugger to ascertain this difference.
Here is my Module code:-
define(["marionette", "shell/shellapp", "interaction/videoreveal/model", "interaction/videoreveal/controller", "utils/loadingview", "utils/imagepreloader"], function(Marionette, shellApp, Model, Controller, LoadingView, imagePreloader){
var currentModuleModel = shellApp.model.get("currentInteractionModel"); // get module name from menu model
var Module = shellApp.module(currentModuleModel.get("moduleName")); // set application module name from menu model
Module.init = function() { // init function called by shell
//trace("Module.init()");
Module.model = new Model(shellApp.interactionModuleData); // pass in loaded data
Module.controller = new Controller({model: Module.model, mainRegion:shellApp.mainRegion}); // pass in loaded data and region for layout
Module.initMain();
};
Module.initMain = function() {
trace("Module.initMain()");
shellApp.mainRegion.show(new LoadingView());
// do some preloading
var imageURLs = this.model.get('imagesToLoad');
imagePreloader.preload(imageURLs, this.show, this);
};
Module.show = function() {
Module.controller.show();
};
Module.addInitializer(function(){
//trace("Module.addInitializer()");
});
Module.addFinalizer(function(){
//trace("Module.addFinalizer()");
});
return Module;
});
Here is the Controller class which is handling the Layout and Views:-
define(["marionette", "shell/vent", "shell/shellapp", "interaction/videoreveal/layout", "interaction/videoreveal/views/mainview", "ui/feedbackview", "ui/videoview"], function(Marionette, vent, shellApp, Layout, MainView, FeedbackView, VideoView){
return Marionette.Controller.extend({
initialize: function(options){
trace("controller.initialize()");
// store a region that will be used to show the stuff rendered by this component
this.mainRegion = options.mainRegion;
this.model = options.model;
this.model.on("model:updated", this.onModelUpdate, this);
this.layout = new Layout();
this.layout.render();
this.mainView = new MainView({model:this.model, controller:this});
this.feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"});
this.videoView = new VideoView({videoContainerID:"vrVideoPlayer"});
vent.on("feedbackview:buttonclicked", this.onFeedbackClick, this);
vent.on("videoview:buttonclicked", this.onVideoClick, this);
},
// call the "show" method to get this thing on screen
show: function(){
// get the layout and show it
this.mainRegion.show(this.layout);
this.model.initInteraction();
},
initFeedback: function (index) {
this.model.set("currentItem", this.model.itemCollection.models[index]);
this.model.set("itemIndex", index);
this.model.initFeedback();
},
initVideo: function (index) {
this.model.set("currentItem", this.model.itemCollection.models[index]);
this.model.set("itemIndex", index);
this.model.initVideo();
},
finalizer: function() {
this.layout.close();
},
// events
onFeedbackClick: function(e) {
this.layout.overlayRegion.close();
},
onVideoClick: function(e) {
this.layout.overlayRegion.close();
},
onFinishClick: function() {
this.model.endInteraction();
},
onFeedbackClosed: function() {
this.layout.overlayRegion.off("close", this.onFeedbackClosed, this);
if (this.model.get("currentItem").get("correct") === true) {
this.model.initThumb();
}
},
onModelUpdate: function() {
trace("controller onModelUpdate()");
switch (this.model.get("mode")) {
case "initInteraction":
this.layout.mainRegion.show(this.mainView);
break;
case "initFeedback":
this.layout.overlayRegion.on("close", this.onFeedbackClosed, this);
this.feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"})
this.feedbackView.setContent(this.model.get("currentItem").get("feedback"));
this.layout.overlayRegion.show(this.feedbackView );
break;
case "initVideo":
this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
break;
case "interactionComplete":
vent.trigger('interactionmodule:completed', this);
vent.trigger('interactionmodule:ended', this);
break;
}
}
});
});
Here is the FeedbackView class:-
define(['marionette', 'tweenmax', 'text!templates/ui/feedbackWithScrim.html', 'shell/vent'], function (Marionette, TweenMax, text, vent) {
return Marionette.ItemView.extend({
template: text,
initialize: function (options) {
this.model = options.model;
this.content = options.content; // content to add to box
this.feedbackBoxID = options.feedbackBoxID; // id to add to feedback box
this.hideScrim = options.hideScrim; // option to fully hide scrim
},
ui: {
feedbackBox: '.feedbackBox',
scrimBackground: '.scrimBackground'
},
events : {
'click button': 'onButtonClick' // any button events within scope will be caught and then relayed out using the vent
},
setContent: function(content) {
this.content = content;
},
// events
onRender: function () {
this.ui.feedbackBox.attr("id", this.feedbackBoxID);
this.ui.feedbackBox.html(this.content);
if (this.hideScrim) this.ui.scrimBackground.css("display", "none");
this.$el.css('visibility', 'hidden');
var tween;
tween = new TweenMax.to(this.$el,0.5,{autoAlpha:1});
},
onButtonClick: function(e) {
trace("onButtonClick(): "+ e.target.id);
vent.trigger("feedbackview:buttonclicked", e.target.id) // listen to this to catch any button events you want
},
onShow : function(evt) {
this.delegateEvents(); // when rerendering an existing view the events get lost in this instance. This fixes it.
}
});
});
Any idea why the region is not being retained in the layout when the module is restarted or what I can do to correct this?
Much thanks,
Sam
Okay.... I got there in the end after much debugging. I wouldn't have got there at all if it wasn't for the generous help of the others on this thread so THANKYOU!
Chris Camaratta's solutions definitely pushed me in the right direction. I was getting a Zombie layout view in my Controller class. I decided to switch a lot of my on listeners to listenTo listeners to make their decoupling and unbinding simpler and hopefully more effective. The key change though was to fire the Controller class's close method. I should have had this happening all along but honestly it's my first time getting into this mess and it had always worked before without needing to do this. in any case, lesson hopefully learned. Marionette does a great job of closing, unbinding and handling all of that stuff for you BUT it doesn't do everything, the rest is your responsibility. Here is the key modification to the Module class:-
Module.addFinalizer(function(){
trace("Module.addFinalizer()");
Module.controller.close();
});
And here is my updated Controller class:-
define(["marionette", "shell/vent", "shell/shellapp", "interaction/videoreveal/layout", "interaction/videoreveal/views/mainview", "ui/feedbackview", "ui/videoview"], function(Marionette, vent, shellApp, Layout, MainView, FeedbackView, VideoView){
return Marionette.Controller.extend({
initialize: function(options){
trace("controller.initialize()");
// store a region that will be used to show the stuff rendered by this component
this.mainRegion = options.mainRegion;
this.model = options.model;
this.listenTo(this.model, "model:updated", this.onModelUpdate);
this.listenTo(vent, "feedbackview:buttonclicked", this.onFeedbackClick);
this.listenTo(vent, "videoview:buttonclicked", this.onVideoClick);
},
// call the "show" method to get this thing on screen
show: function(){
// get the layout and show it
// defensive measure - ensure we have a layout before axing it
if (this.layout) {
this.layout.close();
}
this.layout = new Layout();
this.mainRegion.show(this.layout);
this.model.initInteraction();
},
initFeedback: function (index) {
this.model.set("currentItem", this.model.itemCollection.models[index]);
this.model.set("itemIndex", index);
this.model.initFeedback();
},
initVideo: function (index) {
this.model.set("currentItem", this.model.itemCollection.models[index]);
this.model.set("itemIndex", index);
this.model.initVideo();
},
onClose: function() {
trace("controller onClose()");
if (this.layout) {
this.layout.close();
}
},
// events
onFeedbackClick: function(e) {
this.layout.overlayRegion.close();
},
onVideoClick: function(e) {
this.layout.overlayRegion.close();
},
onFinishClick: function() {
this.model.endInteraction();
},
onFeedbackClosed: function() {
if (this.model.get("currentItem").get("correct") === true) {
this.model.initThumb();
}
},
onModelUpdate: function() {
trace("controller onModelUpdate()");
switch (this.model.get("mode")) {
case "initInteraction":
this.layout.mainRegion.show(new MainView({model:this.model, controller:this}));
break;
case "initFeedback":
var feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox", controller:this});
feedbackView.setContent(this.model.get("currentItem").get("feedback"));
this.layout.overlayRegion.show(feedbackView);
this.listenTo(this.layout.overlayRegion, "close", this.onFeedbackClosed);
break;
case "initVideo":
this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
break;
case "interactionComplete":
vent.trigger('interactionmodule:completed', this);
vent.trigger('interactionmodule:ended', this);
break;
}
}
});
});
If I understand your question correctly your views do not work well after they are closed and re-opened.
It looks like you are using your layout/views after they are closed, and keeping them for future use with these references:
this.feedbackView = new FeedbackView();
Marionette does not like this, once you close a view, it should not be used again. Check out these issues:
https://github.com/marionettejs/backbone.marionette/pull/654
https://github.com/marionettejs/backbone.marionette/issues/622
I would advise you not to store these views and just recreate them when you show them
layout.overlayRegion.show(new FeedbackView());
#ekeren's answer is essentially right; I'm just expanding on it. Here's some specific recommendations that I believe will resolve your issue.
Since you're utilizing regions you probably don't need to create your views ahead of time:
initialize: function(options) {
this.mainRegion = options.mainRegion;
this.model = options.model;
this.model.on("model:updated", this.onModelUpdate, this);
vent.on("feedbackview:buttonclicked", this.onFeedbackClick, this);
vent.on("videoview:buttonclicked", this.onVideoClick, this);
},
Instead, just create them dynamically as needed:
onModelUpdate: function() {
switch (this.model.get("mode")) {
case "initInteraction":
this.layout.mainRegion.show(new MainView({model:this.model, controller:this}));
break;
case "initFeedback":
var feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"})
feedbackView.setContent(this.model.get("currentItem").get("feedback"));
this.layout.overlayRegion.show(feedbackView);
this.layout.overlayRegion.on("close", this.onFeedbackClosed, this);
break;
case "initVideo":
this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
break;
case "interactionComplete":
vent.trigger('interactionmodule:completed', this);
vent.trigger('interactionmodule:ended', this);
break;
}
}
The layout is a bit of a special case since it can be closed in several places, but the principle applies:
show: function(){
// defensive measure - ensure we have a layout before axing it
if (this.layout) {
this.layout.close();
}
this.layout = new Layout();
this.mainRegion.show(this.layout);
this.model.initInteraction();
},
Conditionally cleanup the layout:
finalizer: function() {
if (this.layout) {
this.layout.close();
}
},

Change Views Content based on different modules event

My content box module enumerates a collection and creates a container view for each item ( passing the model to the view). It sets the initial content to the content property of its model. Base on a layout property in the model the container view is attached to the DOM. This is kicked off by the “_contentBoxCreate” method.
The content box module responds to clicks to sub items in a sidemenu. The sidemenu is implemented in a different module. The sidemenu sub click event passes an object along as well that contains a sub_id and some text content. I want to take the content from this object and use it to update container view(s).
Currently I’m doing this via the “_sideMenuClick” method. In backbonejs is there a best practice for updating a views content, given that no data was changed on its model?
thanks,
W.L.
APP.module("contentbox", function(contentbox) {
//Model
var Contentbox = Backbone.Model.extend({});
//Collection
var Contentboxes = Backbone.Collection.extend({
model: Contentbox,
url: 'ajax/contentboxResponse/tojson'
});
/*
* View:
*/
var Container = Backbone.View.extend({
initialize: function() {
contentbox.on('update', jQuery.proxy(this.update, this));
contentbox.on('refresh', jQuery.proxy(this.render, this));
var TemplateCache = Backbone.Marionette.TemplateCache;
this.template = TemplateCache.get("#contentbox-container");
},
render: function() {
var content = this.model.get('content').toString();
var html = this.template({content: content});
this.$el.html(html);//backbone element
return this;
},
update: function(fn) {
var content = fn.apply(this);
if (content !== null) {
var html = this.template({content: content});
this.$el.html(html);
}
}
});
//collection
var contentboxes = new Contentboxes();
var _sideMenuToggle = function() {
contentbox.trigger('refresh');
};
var _sideMenuClick = function(sideMenu) { //view contex
var fn = function() {
// this fn will have the context of the view!!
var linksub = this.model.get('linksub').toString();
if (linksub === sideMenu.id.toString()) {
return sideMenu.content.toString();
}
return null;
};
contentbox.trigger('update', fn);
};
var _contentBoxCreate = function() {
var create = function(cboxes) {
cboxes.each(function(model) {
var layout = "#body-" + model.get('layout');
var $el = jQuery(layout);
var container = new Container({model: model});
$el.append(container.render().$el);
});
};
contentboxes.fetch({
success: create
});
};
this.on("start", function() {
_contentBoxCreate();
});
this.addInitializer(function() {
APP.vent.on('sidemenu:toggle', _sideMenuToggle);
APP.reqres.setHandler('sidemenu:submenu', _sideMenuClick);//event and content...
//from another module
});
});
UPDATE:
Changed the view...
/*
* View
*/
var Container = Backbone.View.extend({
initialize: function() {
this.renderableModel = this.model; // Define renderableModel & set its initial value
contentbox.on('update', this.update, this);
contentbox.on('refresh', this.reset, this); // 3rd param gives context of view
var TemplateCache = Backbone.Marionette.TemplateCache;
this.template = TemplateCache.get("#contentbox-container");
},
render: function() {
var content = this.renderableModel.get('content').toString();
var html = this.template({content: content});
this.$el.html(html);//backbone element
return this;
},
update: function(fn) {
/**
* The "update" event is broadcasted to all Container views on the page.
* We need a way to determine if this is the container we want to update.
* Our criteria is in the fn
*/
var content = fn.apply(this); //criteria match return content, else null.
/*
* The render method knows how to render a contentbox model
*/
if (content !== null) {
this.renderableModel = new Contentbox();
this.renderableModel.set({content: content}); //add content to new contentbox model
this.render(); //Rerender the view
}
},
reset: function() {
this.renderableModel = this.model;
this.render(); // restore view to reflect original model
}
});

backbone js , binding model to view

I'm trying out backbonejs but got stuck on how to bind the model to the view.
yepnope({
load : ["/static/js/lib/jquery-1.6.2.min.js", "/static/js/lib/underscore-min.js", "/static/js/lib/backbone-min.js"],
complete: nameList
});
function nameList() {
var PageItem = Backbone.Model.extend({
defaults: {name: "default name" }
});
var Page = Backbone.Collection.extend({
model: PageItem
});
var page = new Page;
var AppView = Backbone.View.extend({
el: $("#names"),
$artistList: $('#names_list'),
$inputField: $('input#new_name'),
events: {
"keypress input": "processKeyPress"
},
processKeyPress: function(event){
if(event.charCode == 13) {
event.preventDefault();
this.addName();
}
},
addName: function(event) {
var newName = this.$inputField.val();
this.$artistList.prepend('<li>' + newName + '</li>');
page.push(new PageItem({name: newName}));
// I've also tried page.push({name: newName});
} });
var app = new AppView;}
When I press enter on the input field, it runs processKeyPress which calls addName, the new name is added the the html list but not pushed onto the model. I keep getting:
Uncaught TypeError: Object function (a){return new l(a)} has no method 'isObject'
ok, please test this out for yourself, but it appears to work with the Github version of underscore so maybe there was a bug which has been fixed.. http://jsfiddle.net/yjZVd/6/

Categories

Resources