Backbone view 1 pass value to another view 2 - backbone.js

This is the first view content ,where the second view is loaded to first view as a child.
define([ 'jquery', 'underscore', 'backbone',
'text!../../../../school-admin/classManagement.html',
'views/schoolModule/stdManagementView'],
function($, _, Backbone, hrManagementTemplate,StdManagementView) {
var ClassManagementView = Backbone.View
.extend({
// target item.
el : $("#schoolContentOuterPnl"),
render : function() {
var data = {};
// template
var compiledTemplate = _.template(hrManagementTemplate, data);
// append the item to the view's target
this.$el.html(compiledTemplate);
},
// Event Handlers
events : {
"click #btnStdInClassManagement" : "loadStdInClassManagement",
},
loadStdInClassManagement : function(){
//Here i want to pass value to another view
new StdManagementView({
el : $("#classManagementContenTtabContent")
});
},
});
return new ClassManagementView;
});
This is my second view ,when the event on the first view is triggered.
define([ 'jquery', 'underscore', 'backbone', 'datatables',
'text!../../../../school-admin/stdManagement.html' ],
function($, _, Backbone, datatables, stdManagementTemplate) {
var StdManagementView = Backbone.View.extend({
initialize: function(){
this.render();
},
render : function() {
var data = {};
// template
var compiledTemplate = _.template(
stdManagementTemplate, data);
// append the item to the view's target
this.$el.html(compiledTemplate);
},
// Event Handlers
events : {},
});
return StdManagementView;
});
From the above code how can i pass a dynamic value from view 1 to view 2.

From your code it looks like you only want to pass in a value once when you create your second view. As such you can just pass it in to the constructor of your second view and it will be part of the options object passed in.
For example
//view 1
loadStdInClassManagement : function(){
//Here i want to pass value to another view
new StdManagementView({
el : $("#classManagementContenTtabContent"),
someValue: 'something'
});
}
//view 2
var StdManagementView = Backbone.View.extend({
initialize: function(options){
this.someValue = options.someValue;
this.render();
},

Related

Multiple Models using same View in Backbonejs

I'm a beginner in Backbone trying to get a sample page up and running with multiple instances of a Carousel. Here's how my HTML looks like
<div class="en-carousel" id="one" data-href="/js/models/json/carousel.json" data-content-id=""></div>
<div class="en-carousel" id="two" data-href="/js/models/json/carousel2.json" data-content-id=""></div>
<div class="en-carousel" id="three" data-href="/js/models/json/carousel3.json" data-content-id=""></div>
The data is fetched using the Model and the render method of view iterates over the 'el' object of my View object. The model fetch is associated with a success handler which initiates the carousel. The controller code looks like
define([
'jquery',
'backbone',
'handlebars',
'text!view/carousel.html',
'plugin/jquery.bxslider.min',
'TPL'
], function($, Backbone, Handlebars, carouselTemplate, $bx, TPL){
var CarouselModel = Backbone.Model.extend({
setURL: function(url){
this.url = url;
}
});
var CarouselView = Backbone.View.extend({
el: '.en-carousel',
model: new CarouselModel(),
initialize: function () {
},
render: function () {
var that = this;
this.$el.each(function(){
//that.model.url = $(this).data('href');
that.model.setURL($(this).data('href'))
that.listenTo(that.model,'change', that.onSuccess);
that.model.fetch();
});
},
onSuccess: function(response, status, xhr){
var tpl = window["ENX"]["templates/handlebars/carousel.html"](this.model.get('response'));
$(this.el).append(tpl);
$(this.el).find('ul.carousel').bxSlider();
},
ajaxFail: function(){
alert(2)
}
});
return CarouselView;
});
I'm using Handlebars for templating. The problem here is the listenTo event is fired n*n times for n instances. So for 3 carousel I'm getting 9 carousels in total and all of them are being appended to div#one
The solution is simple: replace the entire content of el when the model changes.
initialize: function () {
this.model.fetch().done(function () {
this.render();
this.listenTo(this.model,'change', this.render.bind(this));
}.bind(this));
},
render: function () {
var tpl = window["ENX"]["templates/handlebars/carousel.html"](this.model.get('response'));
this.$el.html(tpl);
this.$el.find('ul.carousel').bxSlider();
}
There is also no need to iterate over el because it is just a single element.

backbone + requirejs: scope issue

I have 2 views, one is a list of "timetracks" and the other is a form to create a timetrack/s
The first one has a collection attached.
The second one, the timetraks form, it defines a "create" function that makes reference to the first one to rerender timetraks view once a new timetrack is created.
timetracks code:
define(['backbone','collections/timetracks', 'views/timetracks/item'], function(Backbone, instanceTimeTracksCollection, TimeTrackView){
var TimeTrackGrid = Backbone.View.extend({
//......
});
return TimeTrackGrid;
});
The form code:
define(['backbone', 'collections/timetracks'], function(Backbone, instanceTimeTracksCollection){
//...............
//next comes my issue:
create: function(){
instanceTimeTracksCollection.create(indexed_array,{
success: function(model, response) {
console.info('model created, response = ',response);
// timeTracksGrid is out of scope, timeTracksGrid would be an instance of timetracks.
timeTracksGrid.render();
},
error: function(error){
console.info('error=',error);
},
wait:true
});
}
});
... and finally I have app.js where the instances of both views are defined:
requirejs(['backbone','views/timetracks/new','views/timetracks/list'],
function(Backbone, newTimeTrackForm, timeTracksGrid) {
var grid = new timeTracksGrid();
var formView = new newTimeTrackForm();
});
How could I render the timetracks view once a new timetrack is created?
**************************** UPDATE *************************************
This is my new version of the code. The issue now is that "this.listenTo(this.collection, "add", this.render);" is overlapping with "this.collection.fetch". As a result the timetracks records are rendered multiple times.
// timetracks view
define(['backbone','collections/timetracks', 'views/timetracks/item'], function(Backbone, timeTracksCollection, TimeTrackView){
var TimeTrackGrid = Backbone.View.extend({
//....
initialize: function(){
_.bindAll(this, 'render', 'generateTimeTracks', 'appendTimeTrack');
this.listenTo(this.collection, "add", this.render);
this.render();
}
render: function(){
$(this.el).html("<table border='1'></table>");
this.collection.fetch({
success: this.generateTimeTracks
});
},
generateTimeTracks : function(){
var self = this;
_(this.collection.models).each(function(item){
self.appendTimeTrack(item);
}, this);
},
appendTimeTrack: function(item){
var timeTrackView = new TimeTrackView({
model: item
});
$('table', this.el).append(timeTrackView.render().el);
}
}
Some other changes:
on app.js instead doing {model:myCollection} as you suggested I'm doing {collection: myCollection}
my form code creates a new model by calling this.collection.create
Thanks again !
A different solution would be to create the views and your collection seperately.
Then in your app.js you could pass the collection to both views. In the initialize function of the TimeTrackGrid you should listen to the "add" event of models on the collections. When such an event is fired you should render the view.
In the create method of your form view you could add the data to your collection. This way your views don't have to know anything about each other which better conforms the Model and View separation.
Thus:
//the grid view
define(['backbone', 'collections/timetracks', 'views/timetracks/item'], function (Backbone, instanceTimeTracksCollection, TimeTrackView) {
var TimeTrackGrid = Backbone.View.extend({
initialize: function () {
//start listening to models being added
this.listenTo(instanceTimeTracksCollection, "add", this.render)
},
render: function () {
//render your view
return this;
}
});
return TimeTrackGrid;
});
//and the form view
define(['backbone', 'collections/timetracks'], function (Backbone, instanceTimeTracksCollection) {
//...............
//next comes my issue:
create: function () {
var data = //get the data from the form
instanceTimeTracksCollection.add(data) //if you have defined a model on your collection, backbone will automatically instantiate the model
}
});
//and you app -> notice the reference to the collection definition
requirejs(['backbone','views/timetracks/new','views/timetracks/list', 'collections/timetrackcollection'],
function(Backbone, newTimeTrackForm, timeTracksGrid) {
var instanceTimeTracksCollection = new TimeTracksCollection();
var grid = new timeTracksGrid({model : instanceTimeTracksCollection});
var formView = new newTimeTrackForm(model : instanceTimeTracksCollection);
});
EDIT=========================================================
fetch the config here
requirejs(['backbone','views/timetracks/new','views/timetracks/list'],
function(Backbone, newTimeTrackForm, timeTracksGrid) {
var grid = new timeTracksGrid();
var formView = new newTimeTrackForm();
var collection = new Collection();
collection.fetch()
});
change your view to:
define(['backbone','collections/timetracks', 'views/timetracks/item'], function(Backbone, timeTracksCollection, TimeTrackView){
var TimeTrackGrid = Backbone.View.extend({
//....
initialize: function(){
_.bindAll(this, 'render', 'generateTimeTracks', 'appendTimeTrack');
// maybe backbone does not fire the add event after fetch
// I believe it does, but I might be mistaken. You will have to look that up
this.listenTo(this.collection, "add", this.render);
this.render();
}
//model is passed to the render method by backbone
render: function(model){
$(this.el).html("<table border='1'></table>");
$('table', this.el).append(new TimeTrackView({model : model}).render().el);
},
//unused now
generateTimeTracks : function(){
var self = this;
// backbone has underscore build in
// so use this instead
this.collection.each(function(item){
//do something with item
}
_(this.collection.models).each(function(item){
self.appendTimeTrack(item);
}, this);
},
//unused now
appendTimeTrack: function(item){
var timeTrackView = new TimeTrackView({
model: item
});
$('table', this.el).append(timeTrackView.render().el);
}
}

Backbone collection length always set to one with nested views

When the view is loaded, the collection length always return 1 while there is currently 3 members shows up in the parse function. I wondered if the problem was the nested item view, but it seems to behave the same without ! I don't understand why the self.push(member) does not add the model to the collection ! Bit stuck here, any help please ?
The model
define([
'backbone'
], function(Backbone) {
'use strict';
var MemberModel = Backbone.Model.extend({
id: "_id"
});
return MemberModel;
});
The collection
define([
'backbone',
'models/MemberModel'
], function(Backbone, MemberModel) {
'use strict';
var Members = Backbone.Collection.extend({
model: MemberModel,
url: '/api/v1/users',
parse: function(response, options) {
var self = this;
_.each(response.users, function(item){
var member = new self.model();
member.set('email', item.email);
member.set('firstname', item.firstname);
member.set('lastname', item.lastname);
member.set('group', item.group);
member.set('city', item.city);
// shows every member's emails
console.log('member.email='+member.get('email'));
self.push(member);
});
console.log('this.length='+this.length); // this is always equal to 1
return this.models;
}
});
return Members;
});
The view
define([
'collections/members',
'views/membersItem',
'text!templates/members.html'
], function(MembersCollection, MembersItem, membersTpl) {
'use strict';
var Members = Backbone.View.extend({
el: '#settings-content',
template: _.template(membersTpl),
events: {
'click #edit-member': 'editMember'
},
initialize: function() {
this.collection = new MembersCollection();
this.render();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
var self = this;
this.collection.fetch({
success: function() {
self.renderMember();
}
});
return this;
},
renderMember: function() {
// same here collection length = 1
console.log('collection.length:'+this.collection.length);
_.each(this.collection.models, function (item) {
var memberView = new MembersItem({model: item});
$('.list-item', this.el).append(memberView.render().el);
}, this);
}
});
return Members;
});
The nested item view
define([
'text!templates/members_item.html'
], function(membersItemTpl) {
'use strict';
var MembersItem = Backbone.View.extend({
tagName: "tr",
className: '.item',
template: _.template(membersItemTpl),
initialize: function() {
this.model.bind("change", this.render, this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
return MembersItem;
});
Looks like the problem is with duplicated id's in the collection. It is supposed to be a unique Identifier.
Set the idAttribute inside the model and make sure, the id's for the 3 objects in question are different.
var MemberModel = Backbone.Model.extend({
idAttribute: "_id"
});
If your id is duplicated then the model will not be added to the model.
If that does not work try setting the id Attribute explicitly
_.each(response.users, function(item, index){
var member = new self.model();
member.set('_id', index);

Backbone Keypress not triggering after adding template

I've created my own version of what is basically: todomvc dependency-example, but I built it from looking at this Modular Backbone Example. I'm trying to move hardcoded html in the base template to it's own template file. I plan on creating multiple pages and want a minimal base template. However, when I load and insert the template in the view, the keypress event for createOnEnter stops working. Every other feature still works, which includes the other event listed in events (clearCompleted).
See: this.$el.append(notesTemplate);.
The browser never makes it to the function createOnEnter().
My app view:
define([
'jquery',
'underscore',
'backbone',
'models/notes/NoteModel',
'collections/notes/NoteCollection',
'views/notes/NotesListView',
'text!templates/notes/statsTemplate.html',
'text!templates/notes/notesTemplate.html'
], function($, _, Backbone, NoteModel, NoteCollection, NotesListView, statsTemplate, notesTemplate){
'use strict';
var NotesView = Backbone.View.extend({
el: $("#page"),
events: {
"keypress #new-note": "createOnEnter",
"click #clear-completed": "clearCompleted"
},
initialize: function() {
var onDataHandler = function(collection) {
this.render();
}
this.$el.append(notesTemplate);
this.model = new NoteCollection();
this.model.fetch({ success : onDataHandler, dataType: "jsonp"});
this.input = this.$("#new-note");
this.allCheckbox = 0;
this.listenTo(this.model, 'add', this.addOne);
this.listenTo(this.model, 'reset', this.addAll);
this.listenTo(this.model, 'all', this.render);
this.footer = this.$('footer');
this.main = $('#main');
this.model.fetch();
},
render: function() {
var done = this.model.done().length;
var remaining = this.model.remaining().length;
if (this.model.length) {
this.main.show();
this.footer.show();
this.$('footer').show();
this.footer.html(_.template(statsTemplate, {done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
this.allCheckbox.checked = !remaining;
},
addOne: function(note) {
var view = new NotesListView({model: note});
$("#notes-list").append(view.render().el);
},
addAll: function() {
this.model.each(this.addOne);
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
this.model.create({title: this.input.val()});
this.input.val('');
},
clearCompleted: function() {
_.invoke(this.model.done(), 'destroy');
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
this.model.each(function (note) { note.save({'done': done}); });
}
});
return NotesView;
});
Solved!
I didn't provide enough information for anyone to find the problem. It was a typo in the element with the ID #new-note. The above code works just fine.
I accomplished loading templates like this by setting the template option in the view like this:
template: _.template(notesTemplate),
and then in my render function calling:
this.$el.html(this.template());
to actually render it. This ensures the events get delegated properly.

Handeling response from backbone.js collection using fetch

am pretty new to backbone.js and managed recently to finish my first application. I made a collection that is responsible for fetching data through a API but am not able to loop through the result and use it.
Here is my model file
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var VehicleLookupModel = Backbone.Model.extend({
//data will contain one of the items returned from the collection's 'parse' function.
parse: function(data){
return data;
}
})
return VehicleLookupModel;
});
collection file
define([
'jquery',
'underscore',
'backbone',
'l/models/VehicleLookupModel'
], function($, _, Backbone, VehicleLookupModel){
var VehicleLookupModelSet = Backbone.Collection.extend({
model : VehicleLookupModel,
url : function() {
return '/en/car/api/model-lookup-model.json/'+this.make+'/';
},
parse : function(response) {
return response;
},
initialize: function(options) {
options || (options = {});
this.make = options.make;
}
})
return VehicleLookupModelSet;
});
and finally the view file
define([
'jquery',
'underscore',
'backbone',
'l/collections/VehicleLookupMakeSet',
'l/collections/VehicleLookupModelSet',
'l/collections/VehicleLookupTrimSet'
], function($, _, Backbone, VehicleLookupMakeSet, VehicleLookupModelSet, VehicleLookupTrimSet){
var BrowseVehicleView = Backbone.View.extend({
el: $('#vehicle-browse-form'),
initialize: function(){
// Extend JQuery example
// This would extend JQuery function for resetting elements on the form
$.fn.extend({
resetElement: function(){
$(this).attr('disabled', 'disabled');
$(this).html('');
return $(this);
}
});
// define array of elements to be used in DOM manipulations
this.elements = {
"make" : $('#id_make', this.el),
"model" : $('#id_model', this.el),
"trim" : $('#id_trim', this.el),
"year" : $('#id_year', this.el)
}
},
events: {
"change #id_make" : "onMakeChange",
"change #id_model" : "onModelChange",
"change #id_trim" : "onTrimChange"
},
render: function(){
// Using Underscore we can compile our template with data
},
onMakeChange: function(event) {
this.elements.model.resetElement();
this.elements.trim.resetElement();
this.collection = new VehicleLookupModelSet({make: this.elements.make.val()})
this.collection.fetch();
console.log(this.collection);
},
onModelChange: function(event) {
var VehicleLookupTrimInstance = new VehicleLookupTrimSet({make: this.elements.make.val(), model: this.elements.model.val()})
VehicleLookupTrimInstance.fetch();
},
onTrimChange: function(event) {
},
renderItem: function(object, item) {
console.log(item);
}
});
// Our module now returns our view
return new BrowseVehicleView;
});
The above is console.log(this.collection) is returning an object with many property which am not sure how to use. But, I noticed that there is a method "models" and inside models there is many number of objects, each represent the value of the json.
Any ideas how i can loop through the object?
this.collection.fetch({
success: function(collection, response) {
_.each(collection.models, function(model) {
console.log(model.toJSON());
})
}
});

Resources