How to create DOM-dependent element in Backbone Marionette? - javascript

In my backbone+marionette application I have used morris.js Line Chart.
This chart takes an array of data from the model. And must be created after DOM creating (DOM dependent).
Сode to create the chart:
_createChartData: function () {
var trends = this.model.get("trends"),
chartData = [];
$.each(trends, function (x, y) {
// some actions with data
});
return chartData;
},
_createChart: function () {
Morris.Line({
element: 'promo-chart',
data: this._createChartData(),
xkey: 'date',
ykeys: ['y', 'g'],
});
}
View and Controller:
define(['utils/hbs', 'modules/model/promo-mdl'], function( Hbs, Promo){
var PromoView = Marionette.ItemView.extend({
initialize: function () {
this._events();
},
template: Hbs.tf('modules/promo'),
templateHelpers: {
// handlebars helpers
},
events: {
'click #submitBtn' : '_savePromo',
},
_events: function () {
this.listenTo(this.model, 'change', this.render);
},
_savePromo: function () {
// save data
}
});
return Marionette.Controller.extend({
initialize: function (config) {
this.region = config.region;
this.model = new Promo({});
this.model.fetch();
this._events();
},
_events: function () {
App.vent.on('show:promo', this._show, this);
},
_show: function () {
this.view = new PromoView({ model: this.model });
this.region.show(this.view);
}
});
});
I tried to create this chart in View, but got an errors - empty data or no chart element in DOM.
Where to create this chart, in View or Controller? And which event to use? initialize, onShow or onRender?

It seems like you're creating the chart when the element it uses isn't in the DOM yet. Try creating the chart in the view's onShow function:
var PromoView = Marionette.ItemView.extend({
// code...
onShow: function(){
// create chart here
}
});

onDomRefresh function in the view could be what you are looking for.

Related

How can I prevent Backbones save method from trying to update every model?

I am creating a crud web app with backbone. I am writing the functionality to update a resource (PUT). I am trying to achieve this by fetching a models properties from the server (see the SubscriberView) and on successfully fetching the resource to instantiate a SubscriberEditView whereby the newly fetched model is passed.
So far this works as expected; SubscriberEditView renders an html form which is populated with the model instance properties.
When I enter a new login value into the form I can trigger the update function which successfully makes a PUT request to the server resource and updates the model instance as expected.
However, the problem is that when I then repeat this process with another model instance the PUT request is made against the curent model AND the previously instantiated model.
Is the reason for this because I now have two instances of SubscriberEditView? Or is it something else that I have missed/misunderstood.
Please see below the described code.
// The view for a single subscriber
var SubscriberView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#subscribers-tmpl').html()),
initialize: function() {
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
var html = this.template(this.model.toJSON());
this.$el.html(html);
return this;
},
events: {
'click .remove': 'onRemove',
'click .edit-subscriber': 'editSubscriber',
},
editSubscriber: function() {
var getSubscriberModel = this.model.set('id', this.model.attributes.id, {silent:true})
getSubscriberModel.fetch({
success: function (model, response) {
$('#addSubscriber').fadeOut();
new SubscriberEditView({model:model});
},
error: function (response) {
console.log('There was an error');
}
});
},
onRemove: function() {
this.model.destroy();
}
});
// The edit view
var SubscriberEditView = Backbone.View.extend({
tagName: 'div',
el: '#updateSubscriber',
template: _.template($('#subscriberEdit-tmpl').html()),
initialize: function() {
this.model.on('sync', this.render, this);
},
events: {
'click #close': 'cancel',
'click .save-subscriber': 'update'
},
update: function() {
var $login = this.$('#login');
this.model.save({
login: $login.val(),
},
{
dataType: 'text',
success: function (model, response, options) {
console.log('success');
},
error: function (model, response, options) {
console.log('error');
}
});
},
cancel: function() {
$('#addSubscriber').fadeIn();
$('#editInner').fadeOut();
},
render: function() {
var html = this.template(this.model.toJSON());
this.$el.html(html);
},
});
If anyone could help then that would be greatly appreciated.
Cheers
The issue is el: '#updateSubscriber',. All your view instances are pointing to same element to which events are delegated. So clicking on any of the .save-subscriber will trigger update for all the view instances. You should not specify el for a view that is going to have more than one instance.

Collection not firing listen events

In my backbone collection I am working with a collection view at the moment, the view is generated "on the fly".
I am struggling to get the view to update when something is added to the collection, on debugging I have noticed that the collection does not have a _listenid which in my limited knowledge I assume means it cannot listen for the events bound to it?
What would this be happening?
Here is my view,
Pops.Views.ProjectManagers = Backbone.View.extend({
className: 'ui-user-list ui-user-list--single',
template: _.template($("#tpl-project-managers").html()),
events: {
"click .js-add-user": "addUser"
},
initialize: function () {
// this.collection.on('all', function() {
// console.log(arguments);
// });
// this.collection.on('reset', this.render, this);
console.log(this.collection);
this.collection.on('add', this.addOneProjectManager, this);
},
render: function () {
//alert("!!!!!");
this.$el.html(this.template());
this.addAllProjectManagers();
return this;
},
addAllProjectManagers: function () {
this.collection.each(this.addOneProjectManager, this);
},
addOneProjectManager: function (model) {
console.log(model);
if(this.model.get('is_admin') == true) {
model.set('admin', true);
}
var teamMember = new App.Views.SingleTeamMember({
model: model,
project: this.model
});
this.$('.ui-member-list').prepend(teamMember.render().el);
},
});
If I physically refresh the page, the collection then has a _listenid
I initialise this view like this,
var projectManagerList = new Pops.Views.ProjectManagers({
model : this.model,
collection : this.model.get('project_manager')
});
and this is the model I pass through,

Create new Backbone Views in click event of Backbone View

in the click event of the PodcastView I would like to create multiple new PodcastItemView objects. The jquery $.get works flawlessly, btw.
If I do console.debug(this.pinfo) in the start-function, I receive the JSON array of my podcast items (title, desc, url,...), so this is not the problem. Also it iterates x times through this array, so this works, too.
PodcastView = Backbone.View.extend({
tagName: "li",
itemTpl: _.template($("#podcast-item").html()),
events: {
"click .p-click" : "start"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
},
render: function() {
this.$el.html(this.itemTpl(this.model.toJSON()));
return this;
},
start: function() {
$.get(restUri + "podcast/" + this.model.get("title") + "/items", _.bind(function(data) {
this.pinfo = data;
_.each(this.pinfo, function(o) {
var v = new PodcastItemView({model: o});
$("#podcast-items").append(v.render().el);
}, this);
}));
}
});
What does not work, however, is the the creation of new PodcastItemView and to append them to #podcast-items. I get the following error:
TypeError: obj[implementation] is not a function (Backbone.js:225)
This is my PodcastItemView.
PodcastItemView = Backbone.View.extend({
tagName: "div",
itemTpl: _.template($("#podcast-item-list").html()),
initialize: function() {
this.listenTo(this.model, "change", this.render);
},
render: function() {
this.$el.html(this.itemTpl(this.model.toJSON()));
return this;
}
});
I am thankful for every tip or response.
Rewrite the start function to:
start: function() {
$.get(restUri + "podcast/" + this.model.get("title") + "/items", _.bind(function(data) {
this.pinfo = data;
_.each(this.pinfo, function(o) {
var model = new Backbone.Model(o)
var v = new PodcastItemView({model: model});
$("#podcast-items").append(v.render().el);
}, this);
}));
As mentioned in comments your code fails because you are trying to bind native object to the view instead of Backbone.Model.
Hope this helps.

How listen to other model events from your model in backbone js?

I have a problem with backbone. I have two models: A and B
I need to listen to events in one model (for instance A), and then after the event has happened make changes in the view of model B and its view.
Does anyone have a fairly simple example how such functionality can be implemented in backbone?
var Model_A_View= Backbone.View.extend({
events: {
// some events;
"click" : "ok",
},
initialize: function () {
this.Model_A = new Model_A({ // });
}
var Model_B_View= Backbone.View.extend({
events: {
// some events;
},
initialize: function () {
this.Model_B = new Model_B({ // });
this.listenTo( model_A , "change: ok", this.dosomethingFunction());
}
dosomethingFunction: function () {
//dosomething
}
Your code hard to read, no code style, and some synax error.
However, you'd better create model outside of you view's initialize function, and pass the model as a para when new a view:
var modelA = new Model_A();
var viewA = new Model_A_View({
model: modelA
});
var modelB = new Model_B();
var viewB = new Model_B_View({
model: modelB
});
viewB.listenTo(modelA, "change:ok", viewB.dosomethingFunction);
As variant you can use The Backbone Events. Few simple steps, how you can do this.
1.Specify events global object:
window._vent = _.extend({}, Backbone.Events);
2.Specify event triggers in your view/model/collection. For example for your view:
var Model_A_View = Backbone.View.extend({
events: {
// some events;
"click" : "ok",
},
initialize: function() {
this.Model_A = new Model_A({});
},
ok: function() {
vent.trigger('model_A:update', this.model); //you can also send your model
}
});
3.Specify event listening in your Model_B:
var Model_B_View = Backbone.View.extend({
initialize: function() {
this.Model_B = new Model_B({});
vent.on('model_A:update', this.updateModel, this);
},
updateModel: function() {
//this function will be call after `model_A:update` event triggered
//do something with Model_B
}
});

How to pass a model(data) from one view to another in Backbone and edit/delete it?

I have a web application using BackboneJS. In this application, I have a LayoutView.js file in which there is a Backbone View (called LayoutView). LayoutView has other functions (methods) that call other views. I am fetching some data in the initialize function of LayoutView, and I need to get this same data (model) in another view and work (update/delete) on it. Below is how I am passing data from LayoutView to myView:
var LayoutView = Backbone.View.extend({
el: $("#mi-body"),
initialize: function () {
var that = this;
this.ConfigData = new Configurations(); //Configurations is a collection
this.ConfigData.fetch({
success: function () {
alert("success");
},
error: function () {
alert("error");
}
});
this.render();
Session.on('change:auth', function (session) {
var self = that;
that.render();
});
},
render: function () {
// other code
},
events: {
'click #logout': 'logout',
'click #divheadernav .nav li a': 'highlightSelected'
},
myView: function () {
if (Session.get('auth')) {
this.$el.find('#mi-content').html('');
this.options.navigate('Myview');
return new MyLayout(this.ConfigData);
}
}
});
Still, I do not know how to "get"/access this data as my current data/model/collection (I am not sure which term is correct) in myView and work on it using Backbone's "model.save(), model.destroy()" methods. Also, whenever an edit/delete happens, the data of ConfigData should be modified and the update should reflect in the html displayed to the user.
Below is the code from MyView:
var MyView = Backbone.View.extend({
tagName: 'div',
id: "divConfigurationLayout",
initialize: function (attrs) {
this.render();
},
render: function () {
var that = this;
},
events: {
"click #Update": "update",
"click #delete": "delete"
},
update: function(){
//code for updating the data like model.save...
},
delete: function(){
//code for deleting the data like model.destroy...
}
});
Now the data I passed is in attrs in the initialize function. How to get this done..?
The syntax for instantiating a Backbone view is new View(options) where options is an Object with key-value pairs.
To pass a collection to your view, you'd instantiate it like so:
new MyLayout({
collection : this.configData
});
Within your view, this.collection would refer to your configData collection.

Categories