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.
Related
I'm preparing to build a medium sized website with Backbone for the first time. There are 7 menu items, and I cant figure out whats the best routing/view relationship when it comes to performance. When a route is triggered, do I create a new instance of the "active" view every time it's triggered or do I only create one view instance for each view, when the user loads the page?
... And whats the best way to handle views – adding and removing dom elements and events, without having trouble with performance. e.g.
$('selector').html(my-new-view);
You can create your menu only once, if it doesnt change. You can create some html template like this.
<div>
<nav>render-here-only-one</nav>
<section>render-here-when-page-changes/loads</section>
</div>
I prefer backbone's render method initialize and append itself.
var testView = Backbone.View.extend({
el: $("#section"),
template:_.template("<strong>hello.</strong>")
initialize: function () {
// any change on view will trigger render
_.bindAll(this, "render");
},
render: function (item) {
this.el.html(this.template());
}
});
var myView = new testView();
myView.render();
I'm working on Infinite Pagination
(http://addyosmani.github.io/backbone.paginator/examples/infinite-paging/index.html)
I'm using CompositeView for pagination view.
And I've got the following problem. Each time after I get new portions of data Paginator's collection removes old data and adds new so it makes CompositeView to rerender and erase old results.
How can I resolve this problem? I'm thinking about disabling rerender functionality but how it should be done properly?
Thanks in Advance!
var BaseFeedChronoCompositeView = Backbone.Marionette.CompositeView.extend({
tagName: "div",
template: _.template(ChronoFeedComposite_html),
itemView: Article,
events: {
'click #loadmore-button-manual': function (e) {
e.preventDefault();
this.collection.requestNextPage();
},
appendHtml: function (collectionView, itemView, index) {
collectionView.$("#chronoFeed-content").append(itemView.$el);
}
});
Here is the basic code.
this.collection.requestNextPage() - sends request for data to server. After it gets data this.collection removes old models and adds new models.
Composite View is listening for these events and removes itemViews for old models and append itemViews for new models.
And I need CompositeView not to remove old itemViews.
Im not quite sure how this paginator works, as I've never used it. But I think the easiest way to fix this is to make sure that your Collection doesnt remove old models. I assume that when your data is being returned it is doing a set on the collection. If you look at the backbone docs you can see that you can disable this method from removing models http://backbonejs.org/#Collection-set
If you'd like to customize the behavior, you can disable it with
options: {add: false}, {remove: false}, or {merge: false}.
So when you are updating the collection, instead of just calling
myCollection.set([o1,o2,o3]);
you should be doing
myCollection.set([o1,o2,o3], {remove:false});
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
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'm fiddling with a view and related model that look like that:
App.Views.Addresses = App.Views.Addresses || {};
App.Views.Addresses.Address = Backbone.View.extend({
events: {
"click button#foo" : "clear"
},
initialize: function(model){
this.address = model.model;
this.address.view = this;
_.extend(this, Backbone.Events);
this.render();
},
render: function(){
... rendering stuff
},
clear: function(){
this.address.clear();
}
});
and
var Address = Backbone.Model.extend({
url: function() {
... url stuff
},
clear: function(){
this.destroy();
this.view.remove();
}
});
I'm facing two problems here. The first one:
I have a button with id="foo" in my source and would like the view to catch the 'click' event of this very button and fire the 'clear' event. Problem: This does not work.
Anyway calling 'clear' on my model by hand cleanly removes the data on the server but does not remove the view itself. Thats the second problem. Hopefully someone more experienced can enlighten me.
Thx in advance
Felix
First problem:
Your button must be inside the element rendered by the view.
backbone scope events to inner elements only
You must render your view within this.el element
backbone use that element for delegation
Second problem:
Use events to destroy your view
You should not store the view in the model. This is kind of a "no no" in MVC. Your model already emits a "remove" event when deleted. Your view should listen to it and behave accordingly.
You must remove your view element from the DOM yourself
This is not handled by backbone.
Other general comments:
Views already are extending Backbone.Events
Use this.model instead of this.address