Backbone.Js events from subview doesn't fire - javascript

I'm new to BackboneJs. And I really don't know what hell I'm doing wrong, but the events from my subview doesn't fire. I've been stuck with this problem now a long time, and I've used the google and stackoverflow to find a solution to my problem but I'm still confused. Could someone help\guide me?
var ExerciseList = Backbone.Collection.extend();
var ExerciseView = Backbone.View.extend({
events : {
'click' : "clickFun"
},
clickFun : function(e) {
alert("yamaha");
},
render : function() {
var exerciseList = new ExerciseList();
exerciseList.url = '/api/exercise_bank/' + this.id + '/';
var that = this;
var element = this.$el;
exerciseList.fetch({
success : function() {
var template = _.template($('#exercise-bank-template-exercises').html(), {exercises : exerciseList.models});
$(element).html(template);
}
});
return this;
},
});
// Collection
var MuscleGroupList = Backbone.Collection.extend({
url : '/api/exercise_bank/'
});
// View
var MuscleGroupView = Backbone.View.extend({
el : '#exercise-bank-component',
initialize : function() {
this.exerciseList = new ExerciseList();
this.exerciseView = new ExerciseView();
},
render : function() {
var muscleGroupList = new MuscleGroupList();
that = this;
console.log('MuscleGroup.render');
muscleGroupList.fetch({
success : function(muscleGroupList) {
template = _.template($('#exercise-bank-template-musclegroups').html(), {
musclegroups : muscleGroupList.models
});
that.$el.html(template);
_.each(muscleGroupList.models, function(musclegroup) {
var element = "#eb-exercises-" + musclegroup.get('id');
that.exerciseList.id = musclegroup.get('id');
that.exerciseView.id = musclegroup.get('id');
that.exerciseView.setElement(element).render();
return that;
});
}
});
return this;
},
});
muscleGroupView = new MuscleGroupView();
exerciseView = new ExerciseView();
muscleGroupView.render();
Backbone.history.start();

Questions:
1) Is your view getting rendered on the Dom.
2) Is this element really present inside the dom var element = "#eb-exercises-" + musclegroup.get('id');
3) Why do need the setElement here
that.exerciseView.setElement(element).render();

Solved it!
I just moved:
initialize : function() {
this.exerciseList = new ExerciseList();
this.exerciseView = new ExerciseView();
},
down to:
_.each(muscleGroupList.models, function(musclegroup) {
var element = "#eb-exercises-" + musclegroup.get('id');
var exerciseList = new ExerciseList();
var exerciseView = new ExerciseView();
exerciseList.id = musclegroup.get('id');
exerciseView.setElement(element).render();
return that;
});
and now it works as I want it to do! :)

Related

JS "... is not a constructor" mystery

I'm confused at this point and I did searched for a solution but couldn't quite find the one answers my situation. Here is the code block I'm having trouble with
this.render = function() {
this.$el.html(Handlebars.compile($("#home-tpl").html()));
itemListView = new ItemListView();
itemListView.setItems(items);
$('.content', this.$el).html(itemListView.$el);
return this;
};
It runs flawlessly the first time. But when I navigate back to this from other page
itemListView = new ItemListView();
section gives me a "ItemListView is not a constructor" error!
This is what ItemListView looks like
var ItemListView = function () {
var service = new DataService();
var items = [];
this.initialize = function() {
this.$el = $('<div/>');
this.render();
};
this.viewAll = function() {
items = service.viewall();
this.render();
}
this.setItems = function(list) {
items = list;
this.render();
}
this.render = function() {
this.$el.html(Handlebars.compile($("#item-list-tpl").html())(items));
return this;
};
this.initialize();
}
Now why would it run the first time but not the next time?

backbone filtering a collection

I am trying to filter a collection based on an attribute called status. Once filtered I want to re-render the view to reflect the filtered results. So far I have come up with this function in my collection.
var ProjectCollection = Backbone.Collection.extend({
url: '/projects',
model: app.Project,
status: function( status ) {
return this.filter(function(project){
return project.get('status') == status;
});
},
});
In my view I the run the following,
filterStatus: function(e) {
e.preventDefault();
var elm = $(e.currentTarget),
status = elm.data('statusid');
this.collection.reset( this.collection.status( status ) );
}
The render function is below along with it's functions that also get called,
render: function() {
this.$el.empty();
console.log("ProjectDashboardView render");
if(this.collection.length < 1) {
var noProjects = new app.noProjectsDashboard;
} else {
this.addAll();
}
$(".month-column").height($(".project-holder").height() + 50);
},
addAll: function() {
console.log("allAdd");
this.collection.each(function(model){
this.addOne(model)
}, this);
},
addOne: function(model) {
var view = new app.ProjectTimelineEntry({
model: model
});
this.$el.append( view.render() );
var number_of_days_in_calendar = 0;
$('.month-column').each(function(){
number_of_days_in_calendar = number_of_days_in_calendar + parseInt($(this).data('days'));
});
var day_width = 1/(number_of_days_in_calendar) * 100;
//Is the start after the end of Feb?
var start_date = new Date(model.get('start_date'));
var march_date = new Date("03/01/2014");
var current_month = start_date.getMonth() + 1;
var march_month = march_date.getMonth() + 1;
console.log(current_month, march_month);
if(current_month <= march_month) {
var start_date_offset = model.get('num_days_from_year_start') * day_width;
var duration_of_project = model.get('run_number_days') * day_width;
//view.$('.duration-bar').css("background-color", model.get('color'));
view.$el.find('.duration-bar').css({
width : duration_of_project + "%",
"margin-left" : start_date_offset + "%"
}, 500);
} else {
var start_date_offset = (model.get('num_days_from_year_start') + 2) * day_width;
var duration_of_project = model.get('run_number_days') * day_width;
//view.$('.duration-bar').css("background-color", model.get('color'));
view.$el.find('.duration-bar').css({
width : duration_of_project + "%",
"margin-left" : start_date_offset + "%"
}, 500);
}
// if(Date.parse(start_date) < new Date("01/03")) {
// console.log("before march");
// }
},
Now this filters the collection, however what happens is that when I try and filter the collection again, it filters the collection that I have just reset too, how can I filter the collection, run the views render() function once a filter is complete, but not keep resetting the collection?
As hindmost mentionned, you should add a visible field to app.Project model.
Then in ProjectView attach a listener to this field:
this.listenTo(this.model, "change:visible", this.onVisibleChange)
and the method definition:
onVisibleChange: function(){
$(this.el).css('display', (this.get('visible')) ? 'block': 'none')
}
In your filter method you run over the collection and change the visible field of each model accordingly to if it should or should not being rendered.
var ProjectCollection = Backbone.Collection.extend({
url: '/projects',
model: app.Project,
status: function( status ) {
return this.each(function(project){
project.set('visible', project.get('status') == status)
});
},
});
You have to add extra attribute to collection's model (app.Project) which will store the flag indicating if a project has to be displayed or not.
var app.Project = Backbone.Model.extend({
defaults: {
...
status: '',
display: true
}
};
Then you have to add the code to model View's render which will show/hide View's element depending on value of display attribute:
var ProjectView = Backbone.View.extend({
...
render: function() {
...
if (this.model.get('display'))
this.$el.show();
else
this.$el.hide();
...
return this;
},
...
};
And, finally you have to modify ProjectCollection's status method to set display attribute on each model:
var ProjectCollection = Backbone.Collection.extend({
url: '/projects',
model: app.Project,
status: function( status ) {
this.each(function(project){
project.set('display', project.get('status') == status);
});
},
...
});

TypeError: param.toElement is undefined

Hey all I have the following code that's a mix of Prototype JS and Jquery:
var oldZindex = 0;
var theClickID = null;
UIProgressButton.prototype._initEvents = function() {
var self = this;
this.button.addEventListener( 'click', function(param) {
$('#overlay').css({'visibility':'visible'});
theClickID = $('button[data-id="' + param.toElement.attributes[1].value + '"]').parent().attr('id');
oldZindex = $('#' + theClickID).css('z-index');
});
}
This code works just fine in a Chrome Browser but seems to throw the error of:
typeerror param.toelement is undefined
In FireFox.... How would I go about fixing this issue since I know for a fact that it does work since its working in Chrome just fine?
Fixed it by adding the following:
var Element = ((window.event)?(event.srcElement):(evt.currentTarget));
Then using that within my code:
UIProgressButton.prototype._initEvents = function() {
var self = this;
this.button.addEventListener( 'click', function(param) {
$('#overlay').css({'visibility':'visible'});
var Element = ((window.event)?(event.srcElement):(param.currentTarget));
theClickID = $('button[data-id="' + Element.attributes[0].value + '"]').parent().attr('id');
oldZindex = $('#' + theClickID).css('z-index');
});
}
Since you already have jQuery you could use it to bind your event listeners so you don't have the IE trouble in getting the event:
var oldZindex = 0;
var theClickID = null;
UIProgressButton.prototype._initEvents = function() {
var self = this;
$(this.button).on( 'click', function(param) {
$('#overlay').css({'visibility':'visible'});
theClickID = $('button[data-id="'
//you may prefer to get a named attribute
//instead of by index:
//param.getAttribute('attributeName')
+ param.target.attributes[1].value
+ '"]').parent().attr('id');
oldZindex = $('#' + theClickID).css('z-index');
});
}

Trigger invokes the called function multiple time when fired from layout view in marionitte

I am using marionitte framework and i am trying to fire a trigger from one layout view to any other view but its function on the other view is invoked multiple times even though the trigger is fired only once.
Does anyone has got encountered the same issue and has resolved it please let me know the resolution so that i can use in my code.
w.src.SourceTabView = Backbone.Marionette.Layout.extend({
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template: "#src-template-source-tab-view",
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
regions: {
"change results" : "#src-template-change-results-region",
"change pagination" : "#src-template-change-pagination-region",
"document layout" : "#src-template-document-layout-region",
"workflow detail" : "#src-template-workflow-detail-region",
"job log" : "#src-template-job-log-region",
"source job" : "#src-template-job-region"
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
changeInfo: null,
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mostRecentChange: null,
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
screenCapture: null,
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
regionSelector: null,
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tab: "details",
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
controls: {
institution: new w.EntitySelector(null,[]),
contentSetGroup: new w.EntitySelector(null,["cs"]),
pageTypes: new w.EntitySelector(null,["page_type"]),
prevTypes: new w.EntitySelector(null,["previous_type"])
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
initialize: function(){
this.listenTo( this.model , 'sync' , this.render );
var instTypes = [] ;
for( var instTypeEntityId in w.INST_TYPES ){
instTypes.push(instTypeEntityId);
}
this.controls.institution = new w.EntitySelector(null,instTypes);
this.controls.institution.minValueLength = 1;
this.controls.contentSetGroup.maxValueLength = 1;
this.controls.contentSetGroup.minValueLength = 1;
this.controls.prevTypes.minValueLength = 1;
this.controls.prevTypes.maxValueLength = 1;
this.fetchMostRecentChange();
this.regionSelector = null;
this.changeInfo = new w.src.ChangeInfo(this.model);
this.changeInfo.vent.on("change-selected", this.render);
this.changeInfo.vent.on("change-refreshed", this.render);
this.changeInfo.vent.on("workflow-selected", this.render);
this.changeInfo.vent.on("workflow-refreshed", this.render);
this.changeInfo.vent.on("jobLog-view", this.render);
this.changeInfo.vent.on("workflow-created", w.refreshWorkflows);
this.changeInfo.vent.on('save-source', this.fromJobRun, this);
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fetchMostRecentChange: function(){
var self = this;
$.ajax({
url: "/source/" + self.model.id + "/most-recent-change",
dataType: "json",
success: function(mostRecentChange){
if( mostRecentChange ){
$.ajax({
url: "/document?change=" + mostRecentChange.id + "&rendering=screen-capture",
dataType: "json",
success: function(response){
self.mostRecentChange = mostRecentChange;
if( response.results.length > 0 ){
self.screenCapture = response.results[0];
}
self.render.call(self);
}
});
}
}
});
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
serializeData: function(){
var verificationDate = this.model.get('verificationDate');
// Converting timestamp into readable format
if(!verificationDate) {
verificationDate = '';
} else {
verificationDate = moment(verificationDate).format('MM/DD/YYYY');
}
return {
model: this.model,
tab: this.tab,
minimumChangeInterval: this.humanizeDate(this.model.get("minimumChangeIntervalMs")),
verificationDate: verificationDate
}
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
onRender: function(){
var tabRegion = localStorage.getItem("tabRegion"),
rememberTabs = localStorage.getItem("rememberTabs");
if( tabRegion && rememberTabs === "true"){
this.tab = tabRegion;
} else if( this.tab == null ) {
this.tab = "details";
}
localStorage.setItem("tabRegion", this.tab);
// content set group
var contentSetIds = [];
for( var contentSetId in w.CS ){
if( this.model.get("entityIds").indexOf(contentSetId) >= 0 ){
contentSetIds.push(contentSetId);
}
}
this.controls.contentSetGroup.values = contentSetIds;
this.controls.contentSetGroup.bind(this.$("#src-tab-details-group"));
// page types
var pageTypeIds = [];
for( var pageTypeId in w.PAGE_TYPES ){
if( this.model.get("entityIds").indexOf(pageTypeId) >= 0 ){
pageTypeIds.push(pageTypeId);
}
}
this.controls.pageTypes.values = pageTypeIds;
this.controls.pageTypes.bind(this.$("#src-tab-details-page"));
// prev types
var prevTypeIds=[];
for(var prevTypeId in w.PREV_TYPES){
if(this.model.get("entityIds").indexOf(prevTypeId)>=0){
prevTypeIds.push(prevTypeId);
}
}
this.controls.prevTypes.values = prevTypeIds;
this.controls.prevTypes.bind(this.$("#src-tab-details-previousType"));
// changes
var changePaginationView = new w.PaginationView({collection:this.changeInfo.changes});
changePaginationView.autoHide = true;
this["change results"].show(new w.src.ChangeCollectionView({collection:this.changeInfo.changes, changeInfo:this.changeInfo}));
this["change pagination"].show(changePaginationView);
// institution
var institutionIds = [];
_.each(this.model.get("entityIds"),function(entityId){
if( !w.INST_TYPES[entityId] && !w.PAGE_TYPES[entityId] && !w.CS[entityId] && !w.PREV_TYPES[entityId]){
institutionIds.push(entityId);
}
});
this.controls.institution.values = institutionIds;
this.controls.institution.bind(this.$("#src-tab-details-institution"));
// job
this["source job"].show(new w.src.SourceJobView({changeInfo:this.changeInfo}));
// documents
if( this.changeInfo.selectedChange ){
var documentLayoutView = new w.src.DocumentLayoutView({collection:this.changeInfo.documents, changeInfo:this.changeInfo});
this["document layout"].show(documentLayoutView);
var documentPaginationView = new w.PaginationView({collection:this.changeInfo.documents});
documentPaginationView.autoHide = true;
documentLayoutView["document results"].show(new w.src.DocumentCollectionView({collection:this.changeInfo.documents, changeInfo:this.changeInfo}));
documentLayoutView["document pagination"].show(documentPaginationView);
} else {
this["document layout"].close();
}
//view job logs
if(this.changeInfo.viewDetails) {
var self = this;
var jobId = this.changeInfo.selectedChange.get('jobId');
var selectedJobModel = new w.Job({id: jobId});
selectedJobModel.fetch({ success: function(model) {
self["job log"].show(new w.src.JobLogView({logger: model.get('logger')}));
}});
}
// workflow detail
this["workflow detail"].show(new w.src.WorkflowDetailView({changeInfo:this.changeInfo}));
// other setup
this.onUrlType();
this.setTab();
if(!this.model.isNew()) {
this.validate();
}
if(this.model.get("includedRegions").length) {
this.$(".delete-all-selected-region").removeClass("disabled");
}
// Adding datepicker
this.$("#verificationDate").datepicker({
todayHighlight: true,
autoclose: true
});
},
fromJobRun: function() {
var self = this;
this.onUseMyAccountClick();
this.populateDateToModel();
// save the model
this.model.save().success(function() {
self.onSuccess();
self.changeInfo.vent.trigger("job-selected");
}).error(function(response) {
self.onError(response)
});
},
});
sourceJobView.js
w.src.SourceJobView = Backbone.Marionette.Layout.extend({
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template: "#src-template-source-job-view",
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
initialize: function(){
this.options.changeInfo.vent.on("job-selected", this.runJob );
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
onRender : function(){
// decide whether to show the job details link
if( w.IS_ADMIN ){
this.$(".jobDetailsMessage").show();
} else {
this.$(".jobDetailsMessage").hide();
}
} ,
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
serializeData: function(){
return this.options.changeInfo;
},
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
runJob: function() {
console.log('here');
},
onRunJobClick: function() {
this.options.changeInfo.vent.trigger("save-source");
}
});
According to the code here
Onrunjobclick in sourceJobview triggered is fired to sourceTabview which is listened by fromrunJob which on success fires a trigger back to sourceJobview which is listened by runJob which is invoked multiple times
The most common cause of this issue is zombie views - they have been removed from the DOM, but their event handlers remain bound.
You should use listenTo instead of on to bind events. This allows Marionette's View.close method to clean up old event handlers when views are closed.
It's a simple fix:
initialize: function(){
this.listenTo(options.changeInfo.vent, "job-selected", this.runJob);
},

loop through array in each()

I have a backbone chat web app , i have a javascript array which holds names and other stuff, what i want is to look in this array if this.name() exists and if so read the element before the name wich is a image url and use that in option_h3 in the code below.
hope im clear enough.
thanks i.a.
var PeopleView = Backbone.View.extend({
className: 'peopleView',
initialize: function() {
this.people = Hula.user.get("people");
this.people.on('add', this.addPerson, this);
this.people.on('remove', this.render, this);
$(".nav_item").removeClass("nav_item_s");
$("#people_nav").addClass("nav_item_s");
},
render: function(){
this.$el.empty();
var header = $('<div id="people_header">');
var title = $('<div class="t34 title">');
title.html("Friends");
header.append(title);
this.$el.append(header);
var addOption = $('<div id="people_add_option">');
var addInput = '<div id="addp"><div id="people_add_input_h"><form id="add_person_input_form" ><input id="add_person_input" name="s" type="text" value="Enter Hularing ID..." ></input></form></div></div>';
addOption.html(addInput);
this.$el.append(addOption);
var list = $('<div id="people_list" >');
this.$el.append(list);
this.people.each(this.addPerson, this);
return this;
},
addPerson: function(person){
var view = new PeopleViewPerson({model: person});
this.$("#people_list").prepend(view.render().$el);
},
events: {
'keypress #add_person_input': 'addNewPerson',
},
addNewPerson: function(e){
var ID = $('#add_person_input').val();
if(ID !=="Enter Hularing ID..."){
if(e.which == 13) {
if(validate(ID)){
Hula.subscribe(ID);
this.$('#add_person_input').val("")
$("#add_person_input_form")[0].reset();
$('#add_person_input').blur().focus();
e.preventDefault();
}
}
}
}
});
var PeopleViewPerson = Backbone.View.extend({
className: 'friend_holder',
initialize: function() {
$(this.el).attr('id', jid_to_id(this.model.get("jid")));
this.model.on('all', this.render, this);
this.model.get('conversation').get('messages').on('add', this.onNewMessage, this);
},
render: function() {
var img = $('<div class="friend_img_h">');
if(this.pic() == null){
img.html('<img src="farax/img/default.png" />');
} else {
var img_src = 'data:'+this.picType()+';base64,'+this.pic();
img.html('<img src="'+img_src+'" />')
}
var info_h = $('<div class="friend_info_h">');
var person_name = $('<div class="friend_name">');
person_name.html(this.name());
var line2 = $('<div class="friend_line2">');
var status = this.status();
line2.html(status);
var option_h = $('<div class="friend_option_h">');
option_h.html('<div class="msg_person_icon" ></div>');
// CONTACT INFO DIV.
var option_h1 = $('<div class="friend_option_h1">');
// CONTACT INFO IMAGE IN CSS FILE.
option_h1.html('<div class="msg_person_icon1"></div>');
// CONTACT FACEBOOK TIJDLIJN.
var option_h2 = $('<div class="friend_option_h2">');
// CONTACT FACEBOOK TIJDLIJN IMAGE IN CSS FILE.
option_h2.html('<div class="msg_person_icon2"></div>');
var option_h3 = $('<div class="friend_option_h3">');
option_h3.html('<div class="msg_person_icon3"></div>');
for(var t = 0; t < javaScript_array.length; t++) {
// this one stays the same
var naam = this.name();
var res = javaScript_array[t];
// ignore this it is done to get the name from the database the same as in the collection
var naam1 = naam.replace(" ","0");
// see if it exists
if(naam1 === res){
option_h3.append('<div class="msg_person_icon3"><img src="'+ javaScript_array[t-1] +'" width="35" height="35"/></div>');
} else {
option_h3.html('<div class="msg_person_icon3"></div>');
}
}
if(this.ask()== "subscribe"){
line2.prepend('<span class="pending_out">Request pending.</span>');
}
if(this.pending()){
line2.prepend('<span class="pending_in">Pending authorisation!</span>');
}
info_h.append(person_name).append(line2);
this.$el.html(img);
this.$el.append(info_h);
this.$el.append(option_h);
this.$el.append(option_h1);
this.$el.append(option_h2);
this.$el.append(option_h3);
return this;
},
jid: function() { return this.model.get('jid');},
name: function() { return this.model.get('name'); },
status: function() { return this.model.get('status'); },
pic: function() { return this.model.get('picture').pic; },
picType: function() { return this.model.get('picture').picType; },
ask: function() { return this.model.get('ask'); },
subscription: function() { return this.model.get('subscription'); },
pending: function() { return this.model.get('pending'); },
online: function() { return this.model.get('online'); },
events: {
'click .friend_img_h': 'loadPerson',
'click .friend_info_h': 'loadPerson',
'click .msg_person_icon': 'messagePerson'
},
loadPerson: function(){
Hula.screen.person.render(this.model);
},
messagePerson: function(){
Hula.screen.conversation.render(this.model);
},
onAll: function(person){
},
onNewMessage: function(message){
$('#people_list #'+jid_to_id(this.jid())+' .friend_line2').html(message.get("message"));
},
OnStatusChange: function(change){
$("#people_list #"+id_to_jid(this.ji())).html(this.status().toString());
}
});
it only works on the last element in the array while there are three elements.
I'm looking at your code and to be completely honest, it makes absolutely no sense to me.
I can see that you have option1 option2 and option3 which I assume are the 3 elements you are referring to only, in your array at the bottom, you only ever seem to change option 3.

Categories

Resources