I am trying to trigger an event in the marionette itemview (List.SendQuestion), however, I was unable to register the trigger in the controller (as seen below)
Essentially, after clicking on the 'a.send', a trigger was supposed to happen and the 'send_qn_view' should capture the event and print out the message 'triggered'. But that was not happening.
Can someone advise me what might be going wrong here?
#Dailymuses.module "SidebarModule.List", (List, App, Backbone, Marionette, $, _) ->
List.Controller =
showSidebar: ->
send_qn_view = new List.SendQuestions
collection: Onethingaday.Public.friends
send_qn_view.on "itemview:ask:user", (itemview, question) ->
console.log('triggered') #THIS IS NOT EXECUTED
class List.SendQuestion extends Marionette.ItemView
template: "sidebar/list/templates/send_question"
className: 'qn_askee'
tagName: 'li'
events:
"click a.send" : "sendQuestion"
sendQuestion: (e) ->
e.preventDefault()
debugger #this debugger was triggered
#trigger "ask:user", #model
class List.SendQuestions extends Marionette.CompositeView
template: "sidebar/list/templates/send_questions"
itemView: List.SendQuestion
itemViewContainer: "ul.friends"
Edit: Corrected my answer and updated my fiddle
Sorry about that. Your syntax for event bubbling is correct.
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.collectionview.md#childview-event-bubbling-from-child-views
"itemview:*" event bubbling from child views
When an item view within a collection view triggers an event, that
event will bubble up through the parent collection view with
"itemview:" prepended to the event name.
That is, if a child view triggers "do:something", the parent
collection view will then trigger "itemview:do:something".
Something to note. Marionette supports view triggers. If your callback is just triggering a view event you can remove the event hash and callback and shorten your code to this:
triggers: {
"click a.send": "ask:user"
}
http://lostechies.com/derickbailey/2012/05/15/workflow-in-backbone-apps-triggering-view-events-from-dom-events/
Fiddle:
http://jsfiddle.net/FRHkt/1/
For anyone new coming to this example, with Marionette 2.x, itemview:* for children views have been replaced with childview: triggers in the parent view, and the collection and composite views have also had their parameters changed from itemView and itemViewContainer to childView and childViewContainer
Related
Using Kendo UI's MVVM framework, I am getting confused about how bindings that are wired in javascript actually work. Given a view model, I have tried the following;
var viewModel = new kendo.observable({
Items: [],
onUpdateItems: function(e){
console.log('updating items');
}
});
viewModel.Items.bind('change', function(e){
viewModel.onUpdateItems(e);
});
viewModel.trigger("change", { field: "Items" });
This does not cause the function to trigger. Though if I actually change items in the view, like interacting with it, it causes the function to fire. This doesn't make a lot of sense to me.
You're binding the change event for viewModel.Items, so you need to trigger the event there. If you change your call to
viewModel.Items.trigger("change");
it will call viewModel.onUpdateItems().
Change events will bubble upwards (from an inner ObservableArray to the outer ViewModel, for example), but not the other way. So if you trigger the change event for the view model, it will not fire the event for the nested ObservableArray Items.
Say I have the following structure -
Model: Folder
Collection: Folders
From within my collection view, I want to trigger an event on a folder with a specific name -
So,
FolderCollectionView = Backbone.View.extend({
...
...
editFolder: function() {
this.collection.findWhere({ name: "abcd" }).trigger("editThisFolder");
}
});
FolderModelView = Backbone.View.extend({
...
...
editThisFolder: function() {
//This should get called
}
});
Is this possible? I'm using event aggregators, however, i haven't found a way wherein, I can trigger an event on a specific folder, I can make the folder view subscribe to a collection view event, but then all folder views respond to that event, I haven't found a way to make only a specific folder view respond to a collection event. Or somehow trigger an event on a specific folder view from the collection view.
I'm new to this, so let me know if i'm missing something important.
Event aggregator reference - http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/
Backbone has underscore.js as a dependency. One of the reasons for this is the fact that Backbone.js Collections implement a whole lot of underscore methods. So your solution should work
this.collection.findWhere({ name: "abcd" }).trigger("editThisFolder");
Then of course you should make your FolderView listen to that event and call the function
var FolderView = Backbone.View.extend({
initialize: function() {
// assuming a folder model assigned to each view
this.listenTo(this.model, 'editThisFolder', this.editThisFolder);
}
});
Did this answer your question?
Using Backbone i'm starting to build an App where i have everything cleanly separated. But now i have the following question. Where should i put the App main logic, in the views or in the model.
For example i have a view and a model, which are binded to a button and when i click that button i have to make
$.ajax(params)
do i put that in the view or the view calls a method with :
this.model.doAction(params)
which do you think is the best approach?
You can define an events property in the view which is of the format {"event selector": "callback"} for eg. {"click .collapse": "collapse"} where collapse would be a function defined as a property of the view. Then write your ajax request code in this callback function.
Also, unless I am missing something, "binding a view and model to a button" doesn't sound correct Backbone way to me. Instead you should think of one instance of model associated with one instance of the view. Whenever an attribute of the the model instance changes, a model change event will be triggered. You can bind a view function to this event so that change in the model is reflected in the view. Here is a quick example
var Book = Backbone.Model.extend({
// ...
});
var BookView = Backbone.View.extend({
initialize: function () {
this.model.bind('change', this.render, this);
},
render: function () {
// here, make changes to the dom as per changes in model
}
});
To associate a model with a view instance, you can pass the it while instantiating a new
view object..
var book = new Book({
title: "A great book"
});
var view = new BookView({model: book});
view.model.set('author', 'AGreatAuthor');
The set function call will fire change event and will result in render function of view
to be called.
Refer to the annotated source of Todos app example for a complete example.
I've been struggling through my first Backbone app and have started to get the hang of things. I now have a few successful reports that load JSON data from my server (NodeJS), populate a template (Handlebars), and render relatively nicely on the frontend.
The issue I'm running into is that I'm trying to add event handlers to a <select> object coming in from one of my templates and I'm not having much luck.
Here's the template:
// Report - New Clients
script(type='text/x-handlebars-template', id='newClients-template')
// Start Listing
{{#each report}}
.listingName
.youSearched
| Stylist:
.srchResult
select.chzn-select(id='stylists', style='width:350px')
option(value='null') All Stylists
{{#each stylists}}
option(value='{{uid}}') {{name}}
{{/each}}
.clear
And here's the Backbone View:
note: I'm calling the view from within a $ -> so it shouldn't be loading until DocumentReady
# View
class window.NewClientsView extends Backbone.View
events:
'click #stylists': 'selectStylist'
initialize: ->
#el = $('.containerOuter')
_.bindAll this, 'render'
#collection.bind 'reset', #render
# Compile Handlebars template at init (Not sure if we have to do this each time or not)
source = $('#newClients-template').html()
#template = Handlebars.compile source
# Get the latest collections
#collection.fetch()
render: ->
# Render Handlebars template
renderedContent = #template { report: #collection.toJSON() }
$(#el).html renderedContent
return this
selectStylist: (e) ->
console.log 'hit it!'
console.log e
I'm expecting that any time the dropdown is clicked or changed, I'll see the selectStylist function fired. Unfortunately that hasn't happened yet :( I also have inspected the element in Chrome Dev Tools and there are no event listeners set on the object.
I've been stuck on this for a bunch of hours now and have reviewed all the other suggestions for Backbone event listeners (i.e. pass in your this.el as a parameter when instantiating your view), but haven't had any success.
Any help or ideas would be appreciated!
In initialize, you write
#el = $('.containerOuter')
But Backbone.js processes events before initialize—meaning that it's already bound those events to a different #el (which you should see in the DOM as a lonely div).
You can hack this by overriding make, the function that's used to create #el:
make: ->
$('.containerOuter')[0]
I have a main app view, with a filter menu in the header. When it's clicked, I want to filter content in a seperate news-feed view. But I don't know how to bind events (and pass class data) from clicks in one view to a function in another.
How can I accomplish this?
There are a number of ways to accomplish this, but probably you want to create a model object, which is shared between the two views. Then on 'click' in view one, update the model object, and bind 'on change' in view two to the model object.
Basically, you can set up both views to stay in sync with the model object, and any changes to the object will result in changes to the view.
Everything in Backbone inherits from Backbone.Events, so you can trigger and bind events from anywhere (docs for Backbone.Events):
var View1 = Backbone.View.extend();
var View2 = Backbone.View.extend({
eventHandler: function(data) {alert(data)}
});
var v1 = new View1;
var v2 = new View2;
v1.bind('hello-world-event', v2.eventHandler)
v1.trigger('hello-world-event', 'Hello World!')
Note that in this example, when v2.eventHandler is called, 'this' will refer to v1. See the backbone docs for more.