How can I change ItemView to CompositeView in Marionette application? - marionette

I'm trying to make in-row editing in Marionette application. What is the best approach to do this?
I have table which is Marionette.CompositeView and rows of this table are Marionettes ItemViews. Now I'm trying to change clicked table row (ItemView) to a CompositeView which will contain inputs and selects with ajax fetched data. Is this a good approach?

You can use CollectionView.getChildView to render different view for edited items, but this can cause performance problems if you need to render large collections.
I modified Derick Bailey's Tree View example to show how this can be done - http://jsfiddle.net/msamujlo/8g3abfg2/
// The recursive tree view
var TreeView = Backbone.Marionette.CompositeView.extend({
template: "#node-template",
tagName: "ul",
getChildView: function(item){
return item.get('isEditable')? EditorView : TreeView;
},
// ... more methods
};
var EditorView = TreeView.extend({
template: "#editor-template",
});
...

Related

Backbone routing and view relations

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();

how do I show just one item of my marionette collection and navigate between them

I have a route
routes: { "pages/:id" : "page"
},
page: function (id) {
var pageView = new contentCollectionView({
collection: collection,
tagName: "div",
className: "pages"
});
pageView.close();
PAGE.content.show(pageView);
},
However what I want to do is to only show the individual page in my collection that matches the id I am passing - I also want to be able to be navigate from that page to the preceding and next page in the collection.
So - how do I pass the identifier in to my pageView at the time of rendering so it only does the one item in the collection ( I believe I know how to get the item index in the collection as it loops through, but if you want to show an example of that as well it would be nice)
According to your requirement i would recommend to render ItemView instead of CollectionView.
If you have CollectionView you have ItemView for sure :)
and pass special by ID model from you collection.
So you code may look like this:
page: function (id) {
var pageView = new contentItemView({
model: collection.get(id)
});
pageView.close();
PAGE.content.show(pageView);
}
About navigation - you can check with at method index of model in collection to actualize navigation logic. If you also need to keep browser navigation(back and forward buttons) you have to checkout Backbone.history

Backbone.Marionette and Backbone.Paginator. CompositeView rerendering

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});

Is it ok to use App.__container__.lookup to access instances?

I need access to the ApplicationView from another view and I'm able to do so with App.__container__.lookup('view:application').
Is __container__ intended to be used this way?
Is there a better way to access instances of views?
(update)
My usecase:
My ApplicationView has a template with 2 columns.
The CSS is responsive so the size of the columns changes to accommodate the
width of the page.
I'm using Ember List View which requires height and width to be specified during initialization
I want to get the instance so I can access the DOM object to figure out its size
I can't use Ember.View.views because at that point, the ApplicationView has not been inserted into the DOM
Don't use that. One of the core developers said that whenever someone tries to use App.__container__, he would add another underscore.
If you really want to access an Ember.View intance use Ember.View.views['foo']. Where foo is the elementId of the view instance.
So if for example you want the App.ApplicationView instance:
App.ApplicationView = Ember.View.extend({
elementId: 'application'
});
// somewhere else in your code
var applicationViewInstance = Ember.View.views['application'];
Having said that, I never came across a situation where I needed to access view instances like that. If you can post your use case, I may be able to suggest alternative ways.
UPDATE: You want to access some properties of a view instance, from some other view instance (view height and width). You can pass those properties to the controller and let other controllers access them for using them in other views (source view -> source controller -> some other controller -> some other view):
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
var controller = this.get('controller'),
height = this.$().height(),
width = this.$().width();
controller.setProperties({
height: height,
width: width
});
}
});
App.SomeotherController = Ember.Controller.extend({
needs: ['application'],
applicationViewWidthBinding: 'controllers.application.width',
applicationViewHeightBinding: 'controllers.application.height'
});
App.SomeOtherView = Ember.View.extend({
// assuming its controller is an instance of App.SomeotherController
applicationViewWidthBinding: 'controller.applicationViewWidth',
applicationViewHeightBinding: 'controller.applicationViewHeight'
});

Backbone.js - Binding from one view to another?

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.

Resources