I'm trying to add an event dynamically but the event doesn't fire. jQuery is not available.
Backbone Code
var OptimizelyTeacherClick = Backbone.View.extend({
events: {
"click button.start-class-creation" : "optimizelyEvent"
},
optimizelyEvent: function(e){
e.preventDefault();
console.log('Optimizely Event');
alert('Optimizely Event');
}
});
var optimizelyTeacherClick = new OptimizelyTeacherClick({ el: $('.start-class-creation') }).render();
HTML
<form class="start-class-creation-form" action="http://site.dev/classes/online/new?type=online&via=teach&button=page_top" method="post">
<input value="My Class" name="title" class="initialized" type="hidden">
<button type="submit" class="btn submit-btn large primary start-class-creation">Get Started</button>
</form>
The root el element for the OptimizelyTeacherClick view is the button itself. So the event key string should just be click. Events are bound on the el.
var OptimizelyTeacherClick = Backbone.View.extend({
events: {
"click": "optimizelyEvent"
},
optimizelyEvent: function(e) {
e.preventDefault();
console.log('Optimizely Event');
}
});
You could also just instantiate the view with the selector string, Backbone will fetch it with jQuery.
var optimizelyTeacherClick = new OptimizelyTeacherClick({
el: '.start-class-creation'
}).render();
That being said, I don't feel like you're going in the right direction with this design, but I don't know enough on your project to help more.
Related
I am using the same structure as explained in the answers for this question -- > How to handle nested CompositeView using Backbone.Marionette?
to render my composite views and Item view. My Item view consists of LI tag. I want to handle click event for this Itemview. I am trying my code as below :which is not working The same events code snippet If I write in my first composite view, It get's triggered. But events do not get triggered in ItemView. Please help.
var topNavMenuView = Backbone.Marionette.ItemView.extend({
tagName :'li',
className:'dropdown',
template : _.template(topNavMenuItemTemplate) ,
initialize:function(options){
console.log("initialize");
this.id=options.menuCode;
},
events: {
'click li' : function(event){
alert('click');
},
'click ' : function(event){
alert('click');
}
}
});
View events should return events hash like this
events: {
'click': 'onClick'
},
onClick: function(){
alert('click');
}
I have a Backbone view for a search form. The form includes several elements, including a slider implemented in jSlider. I want to capture all changes to the form, and update the results shown accordingly.
I can capture the click events fine, but I don't know how to capture the slide event on jSlider - I know how to bind custom events in Backbone, but jSlider seems not to have a way to bind it directly.
Here's my HTML:
<form id="searchform">
<input type="text" id="city" />
<input type="text" id="type" />
<input id="price-jslider" type="slider" name="price" value="1;500" />
</form>
And here's my Backbone code:
var SearchFormView = Backbone.View.extend({
el: $('#searchForm'),
events: {
"click input": "updateResults",
// how to capture slide event on jslider?
},
updateResults: function(e) {
// do stuff
}
});
Does anyone have any ideas on how to capture this sort of event?
You can pass an onstatechange function when initializing the jQuery slider:
var SearchFormView = Backbone.View.extend({
el: $('#searchForm'),
render: function() {
var self = this;
$("#price-jslider").slider({
onstatechange: function( value ) {
self.updateResults();
}
});
},
events: {
"click input": "updateResults"
},
updateResults: function(e) {
// do stuff
}
});
from jslider page
onstatechange function(value)
Function fires while slider change state.
callback function(value)
Function fires on "mouseup" event.
when you instantiate jslider, you define those function in option
$(whatever).jslider({
onstatechange : function(v) {
//set your input value to v
$('#searchForm').trigger('click')
}
})
I want to create a generic event handler that I can reuse on dom elements so I don't have to write boiler plate over and over again. I thought I had it figured out but I am getting errors.
The problem I am having is that I think the event handlers are bound at a different time than I need. Maybe at document.ready? Where I think I need to attach them with the .live() method? Though I may have no idea what I am talking about here.
Here is what I am trying to do:
Multi page application.
Multiple collections where data needs to be inserted.
Button code to show the insert form.
<button id="btnShowInsert" class="btn btn-success" rel="tooltip" title="add group">
<i id="btnIcon" class="icon-plus-sign icon-white"></i>
</button>
Template that shows the form based on the page (controller)
{{> groups_insert}}
Here is the form.
<template name="groups_insert">
{{#if acl_check}}
{{> alert}}
< p>
< form class="form-horizontal well hide" id="insert">
<fieldset>
< div class="control-group">
< label class="control-label" for="name">Name</label>
< div class="controls">
< input type="text" class="input-xlarge" id="name" name="name">
< /div>
< /div>
< div class="form-actions well">
< button id="btnReset" type="reset" class="btn btn-large">Reset</button>
< button id="btnSubmit" type="button" class="btn btn-primary btn-large">Submit</button>
< /div>
< /fieldset>
< /form>
< /p>
{{/if}}
< /template>
Here is the client code to implement the button that shows the form on the page.
Template.groups.events[ Meteor.eventhandler.btn_events('#btnShowInsert') ] = Meteor.eventhandler.make_btn_show_insert_form_click_handler();
Here is my generic event handler:
var EventHandler = Base.extend({
btn_events: function(selector) {
return 'click ' + selector; //, keydown '+selector+', focusout '+selector;
},
make_btn_show_insert_form_click_handler: function(){
//var click = options.click || function () {};
return function (event) {
if (event.type === "click") {
event.stopPropagation();
event.preventDefault;
try{
if ($('#btnIcon').hasClass('icon-plus-sign') ) {
$('#btnIcon').removeClass('icon-plus-sign');
$('#btnIcon').addClass('icon-minus-sign');
} else {
$('#btnIcon').removeClass('icon-minus-sign');
$('#btnIcon').addClass('icon-plus-sign');
}
$('#insert').slideToggle('slow', 'swing');
} catch(error) {
Alert.setAlert('Error', 'Critical Error: ' + error, 'alert-error');
}
}
}
},
});
Meteor.eventhandler = new EventHandler;
THE ERROR
Uncaught TypeError: Cannot call method 'btn_events' of undefined
BUT, if I define the event handler this way and call it this way it works.
Template.groups.events[ btn_events('#btnShowInsert') ] = make_btn_show_insert_form_click_handler();
var btn_events = function (selector) {
return 'click ' + selector; //, keydown '+selector+', focusout '+selector;
};
var make_btn_show_insert_form_click_handler =
function () {
//var click = options.click || function () {};
console.log( Meteor.request.controller );
return function (event) {
if (event.type === "click") {
event.stopPropagation();
event.preventDefault;
try{
if ($('#btnIcon').hasClass('icon-plus-sign') ) {
$('#btnIcon').removeClass('icon-plus-sign');
$('#btnIcon').addClass('icon-minus-sign');
} else {
$('#btnIcon').removeClass('icon-minus-sign');
$('#btnIcon').addClass('icon-plus-sign');
}
$('#insert').slideToggle('slow', 'swing');
} catch(error) {
Alert.setAlert('Error', 'Critical Error: ' + error, 'alert-error');
}
}
}
};
The Problem
I don't want to have to replicate code all over my site in order to implement a nice button that can slideToggle and form on any page. If I could get it abstracted then I should be able to have a Show Form type of button on all pages for any collection that I am rendering that allows data entry. As well, this leads into being able to create one form handler for all forms as well and then tying them to the controller through an action to the model.
Any ideas?
You can bind a high-level template to elements created with child templates. Then you only have to do the binding once. For example
HTML:
<template name="settings">
{{> login_settings }}
{{> account_settings }}
{{> data_settings }}
</template>
<template name="login_settings">
<btn class="slideToggle">Slide me for login!</btn>
</template>
<template name="account_settings">
<btn class="slideToggle">Slide me for account!</btn>
</template>
<template name="data_settings">
<btn class="slideToggle">Slide me for data!</btn>
</template>
JavaScript:
Template.settings.events {
'click .slideToggle': function() {
var clickedElement = event.target;
// add/remove CSS classes to clicked element
}
};
So if you end up creating 10 different template definitions under settings so you still only have to bind the handler to a single template.
I feel like you're overcomplicating things. Why not do this?
Template.someTemplate.events({
'click .button': buttonClicked
});
function buttonClicked(evt) {
// DRY code to handle a button being clicked
}
This has the right balance of separation: your event handler is defined once, but you can tell each template that you want its buttons to listen to some event. And if that's not good enough, you can further abstract it:
Template.someTemplate.events(genericEvents);
And possibly even merge genericEvents with specific events for that Template if you wanted.
Here is what I ended up doing. The example only shows the generic insert handler.
var EventHandler = Base.extend({
btnClickHandler: function(){
return function (event) {
event.preventDefault();
Meteor.eventhandler[event.currentTarget.id](event);
}
},
insert: function(event){
event.preventDefault();
var params = $('#insert-form').toJSON();
try{
window[Meteor.request.controller.capitalise()]['validateParams'](params);
var ts = new Date();
params.client_updated = ts;
var has_popup = params.has_popup;
delete params.has_popup;
window[Meteor.request.controller.capitalise()]['insert'](params, function(error, _id){
if(error){
Alert.setAlert('Error', error, 'alert-error', true, has_popup);
} else {
Alert.setAlert('Success', 'Record successfully created.', 'alert-success', true, has_popup);
$("#insert-form").reset();
Meteor.flush();
}
});
} catch(error) {
Alert.setAlert('Error', error, 'alert-error', true, params.has_popup);
}
}
});
Meteor.eventhandler = new EventHandler;
Now, I merely have to create handlebars templates without any significant javascript coding to handle generic events and wire them up as follows.
$(document).on("click", '#print', Meteor.eventhandler.btnClickHandler());
$(document).on("click", '#insert', Meteor.eventhandler.btnClickHandler());
$(document).on("click", '#remove', Meteor.eventhandler.btnClickHandler());
$(document).on("click", '#removeSubField', Meteor.eventhandler.btnClickHandler());
$(document).on("click", '#insertSubField', Meteor.eventhandler.btnClickHandler())
$(document).on("click", '#update', Meteor.eventhandler.btnClickHandler());
$(document).on("click", '#updateSubField', Meteor.eventhandler.btnClickHandler());
$(document).on("click", "#toggleActive", Meteor.eventhandler.btnClickHandler());
$(document).on("click", "#toggleChild", Meteor.eventhandler.btnClickHandler());
Now, I don't have to write any template event maps to handle basic CRUD. I can create any number of handlebars templates as long as the /route corresponds to the collection name. Although I do some tricky conversions from time to time. Basically, the generic event handler wires up the events, based on the route aka request.controller, to a collection and abstracts it through a client/server shared data model for validation and even access control alongside what is existing in Meteor.
It seems to work well and has reduced my code base significantly. I have dozens of collections where I haven't had to write any event maps handlers because basic CRUD is handled but abstracted enough that I can customize validation, security and other sanity checks on the client/server shared data model.
The approach I've taken to this problem in Meteor 1.0.2 is to use dynamic templates. See Dan Dascalescu's canonical answer and the docs.
Let's say you have a set of generic events attached to template "A" and you want to take advantage of them in templates "B", "C", and "D."
HTML:
<template name="A">
{{> Template.dynamic template=myTemplate}}
</template>
JS:
Template.A.events({
... your event code
})
You define a helper function for "A" that dynamically picks which of B, C, or D (...) you want to include:
Template.A.helpers({ // dynamically insert a template
myTemplate: function(){
if (...) return 'B'; // return a string with the name of the template to embed
if (...) return 'C';
if (...) return 'D';
}
})
The events defined in "A" will now be available in "B", "C", and "D."
Note that template "A" need not contain any HTML whatsoever.
I have a simple html form like this
<table>
<tr><td >Topic: </td> <td> <input type="text" name="Topic"></td></tr>
<tr><td ><button id ="submit">submit</button></td></tr>
</table>
For this form i have implemented backbone.js, part of the code for backbone view is given below.
AppView = Backbone.View.extend({
events: {
"click #submit": "SubmitForm",
},
SubmitForm: function(){
topic = $("#Topic").val();
var subject_model = new App_Form();
subject_model.save();
}
My question is, when i include the form tag in the html form above. On click of the 'submit' button the SubmitForm function is not called. Whereas on exclusion of the form tag the SubmitForm function is called on click of submit button.
Could somebody help with this please!
You should return false; in SubmitForm to prevent actual submitting.
var AppView = Backbone.View.extend({
events: {
"click #submit": "SubmitForm",
},
SubmitForm: function(){
topic = $("#Topic").val();
var subject_model = new App_Form();
subject_model.save();
return false;
}
});
Check to make sure that you've defined an el in your Backbone.View and that the selector in your event map is with the scope of that el.
If the event isn't bound often times its because you've failed to provide a scoped selector.
I am learning backbone.js and am quite new. I have a view that acts as a button:
simpleButton = Backbone.View.extend({
template: "<button class='${classes}'>${text}</button>",
el: $("body"),
events: {
"click": "onClick",
"focus": "onFocus",
"blur": "onBlur"
},
initialize: function (args) {
_.bindAll(this, 'render');
this.rendered = false;
this.text = args.text || 'button';
this.classes = args.classes || [];
this.classes.push('ui-button');
//console.debug("Wh.views.simpleButton.initialize classes ",this.classes);
if (args.autoRender === true) this.render();
},
render: function () {
//console.debug("Wh.views.simpleButton.render classes ",this.classes);
if (this.rendered === false) {
$.tmpl(
this.template, {
classes: this.classes.join(' '),
text: this.text
}
).appendTo(this.el);
this.rendered = true;
}
},
//event handlers
onClick: function (ev) {
console.debug(this);
alert("click on ", ev, this);
},
onFocus: function (ev) {
////console.debug(ev);
},
onBlur: function (ev) {
}
});
My problem is that if I create two buttons, and click just one of them, I get the alert box two times, and the debug showing me "this" shows the first button first, and the second button next.
Am I missing something?
The events you define are bound to the "el" property of your view. In your case it is "body" so when you fire up click with 2 simpleButton views instantiated, you have 2 of them listening for the same event.
Each view you instantiate should represent one and only one DOM element defined by the el property. So if you want to create a button view (not sure this is 'best practice' in a real program) you could have :
SimpleButton = Backbone.View.extend({
template : "<button class='${classes}'>${text}</button>",
tagName : "div", // defines the html tag that will wrap your template
className: ".buttonbox",
...
});
mybtn = new SimpleButton();
mybtn.render().appendTo('body')
That way your click event will only concern the one div.buttonbox inside of which your button lives.
Notice : Backbone idea of the render function is creating an html string you'll use afterwards to append prepend or whatever in the DOM. That way if you create many you can do it so you only refresh the DOM once (refreshing the DOM is expensive)...
Use this in your View .it will unbind the click events
initialize : function() {
$(this.el).unbind("click");
}
Just a thought that creating a Backbone.View for each and every button in your app could be a performance overkill and you can't leverage the "delegate" feature in jQuery. I'd instead create a Backbone.View for the parent element of those buttons instead.
Of course, if you have a few special buttons with complicated logic then they probably do deserve their own View classes. :)
Give your buttons unique ids, for example <button id="button1"> and <button id="button2">, then in your events hash, you need to specify the click event and the id of the button you want to handle that event for, e.g:
events : {
"click #button1" : "onClick",
"click #button2" : "doSomethingElse"
}
Now this will call onClick() only when you click on the button with id=button1 and call doSomethingElse() when you click on the button with id=button2