How can I assign a specific itemView to a certain model in a CompositeView collection? - marionette

I am using backbone.marionette and want to use different views for some Items from a collection passed to a CompositeView. Is this possible?
var vText = Backbone.Marionette.ItemView.extend({
template : "forms/form-element",
className : "control-group"
});
var vCheckbox = Backbone.Marionette.ItemView.extend({
template : "forms/form-element-checkbox",
className : "control-group"
});
var vForm = Backbone.Marionette.CompositeView.extend({
tagName : 'form',
template : 'forms/form',
className : 'crud-form form-horizontal',
itemView : // use different view for specific items based on collection modelData
})
I tried:
itemView : function(model) {
switch(model.get('inputType')) {
case "checkbox":
return vCheckbox;
break;
default:
return vText;
break;
}
}
But doesn't work. How can I achieve this?

this isn't directly supported right now, but wouldn't be too hard to add for your project's needs
https://github.com/derickbailey/backbone.marionette/blob/master/src/backbone.marionette.collectionview.js#L105-115
this is the function that retrieves the value of the itemView setting for a view. You could override it in your specific view:
Backbone.Marionette.CompositeView.extend({
// ...
getItemView: function(){
return this.itemView();
}
});
This seems like something that should be added to marionette directly, as well. Can you add an issue to the github issues list? https://github.com/derickbailey/backbone.marionette/issues link back to this SO post from the ticket.

Related

Which is the best way to add events handler dynamically in Backbone.View?

In my previous project , i use Backbone.js of version 1.1.2 , and the Backbone.View is defined as
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
options || (options = {});
_.extend(this, _.pick(options, viewOptions));
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
As i update my Backbone to the latest version(1.2.3) , now the Backbone.View is defined as
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
_.extend(this, _.pick(options, viewOptions));
this._ensureElement();
this.initialize.apply(this, arguments);
};
The difference is the delegateEvents now is moved to the setElement method , thus i can't dynamically change the
events property of Backbone.View. But in some situation , i think dynamically change events is needed , for example,
i have a FileRowView which extends Backbone.View to represent file information , its template html string is :
<li>
<h3 class="title">File name</h3>
<p>File description </p>
</li
The problem the title dom can be clicked and do something , but in other situation it can't be clicked , so in the events hash , i can
control this behavior in the initialize method just like :
var FileRowView = Backbone.View.extend({
tagName:'li',
tmpl:
'<h3 class="title">File name</h3>'+
'<p>File description </p>',
events:function(){
var events = {};
if(this.options.canClickTitle){
events['clcik .title'] = this._titleClickHandler
}
return events ;
},
//
// options = {
// canClickTitle:false
// ...
// ...
// }
//
initialize:function(options){
options = _.extend({}, {
canClickTitle:true
} , options);
this.options = options ;
this.render();
},
_titleClickHandler:function(){
console.log('some action');
},
render:function(){
var html = _.template(this.tmpl)();
this.$el.append(html);
}
});
But in the new version Backbone.View , as the delegateEvents has been added to the setElement method , how can i dynamically add event handler in the events property ?
In the version 1.2.0 the change document say:
Views now always delegate their events in setElement. You can no longer modify the events hash or your view's el property in initialize.
I don't know why ?
I'd turn the problem around a bit and use the tools you already have rather than trying to force them to work the way you think they should.
For example, you could make your template look like this:
<h3 class="title <%= is_clickable ? 'clickable' : '' %>">File name</h3>
<p>File description</p>
Then include an is_clickable flag in the data you feed the template and your events would be simple and static:
events: {
'click .clickable': '_titleClickHandler'
}
Or if you need more specificity:
events: {
'click .title.clickable': '_titleClickHandler'
}
Now you don't have to do anything weird or version dependent and you don't have to fight the framework.

Kendo UI custom validation rules not validation properly in MVVM

Here I have a Kendo view model.
$(document).ready(function () {
var viewModel = kendo.observable({
addData : function (e) {
if (val.validate()) {
// this will send data to server if only view is valid.
}
}
});
kendo.bind($("#my-form"), viewModel);
var val = $("#my-form").kendoValidator({
messages : {
my custom messages
},
rules: {
my custom rules
}
}).data("kendoValidator");
});
What happen here, when I try to submit the form it did not validate the form. simply it gets true for val.validate(). Then I remove data("kendoValidator") because this is now not a HTML5 validations. So after remove that it is like this...
var val = $("#my-form").kendoValidator({
messages : {
my custom messages
},
rules: {
my custom rules
}
});
Then I try to submit the form, it refresh the page. Where I have been wrong in this ??
Your setup should work fine. Perhaps it is really matter of validation rules.
By all means you should not remove the
data("kendoValidator")
part, because this is how you get reference to the Validator object.
Have a look at this example I have prepared for your reference. Let me know if you have further questions.
http://dojo.telerik.com/ovUCA

Is there a way to separate the two cases for rendering the emptyView?

Is there a way to separate the two cases for rendering the emptyView?
1. When the CollectionView is just created. The collection is still empty
2. After collection is fetched but the data is empty.
_CollectionView = Backbone.Marionette.CollectionView.extend({
emptyView: _EmptyView,
itemView: _ItemView,
initialize: function () {
this.collection = new Backbone.Collection ();
this.collection.fetch();
},//initialize
});
this is the way I have done this in the past. Set your 'emptyView' to be your loading view and then after the collection has synced, set the 'emptyView' to your actual EmptyView if required. I have also used this in 'onBeforeRender' as in the example below you may need to re-render your view if it has already been rendered with the 'EmptyView':
emptyView: LoadingView,
collectionEvents: {
'sync': 'onSync'
},
onSync: function () {
if(this.collection.length === 0) {
this.emptyView = EmptyView;
//may need to call 'this.render();' here if already rendered
}
}

Backbone click event fires events for all collection rather than model

Can't figure out what's wrong. When I click on a model title, it fetches all models in collection at once rather than fetch one model. If I move this event from logView to logsView it works properly but doesn't have access to model, well I can find this model using index or ant other model's ID but don't think this is a nice way.
var Log = Backbone.Model.extend({});
window.LogsList = Backbone.Collection.extend({
model:Log,
url:function (tag) {
this.url = '/logs/' + tag;
return this;
}
});
window.colList = new LogsList();
window.logView = Backbone.View.extend({
el:$('.accordion'),
template:_.template($('#log').html()),
initialize:function () {
this.model.bind('add', this.render, this);
},
events:{
"click .accordion-toggle" :"getLogBody"
},
render:function () {
return this.template(this.model.toJSON());
},
getLogBody:function () {
this.model.fetch();
}
});
window.LogsView = Backbone.View.extend({
el:$("#content"),
initialize:function (options) {
colList.bind('reset', this.addAll, this);
colList.url(options.data).fetch();
},
addOne:function (model) {
var view = new logView({model:model});
$("#accordion").append(view.render());
},
addAll:function () {
colList.each(this.addOne);
}
});
window.listView = new LogsView({data:"Visa_Cl"});
The problem is caused by your el in the LogView: el:$('.accordion')
Backbone's view events are scope to the view's el. In this case, you've specified the view's el as ALL HTML elements with a class of "accordion". Therefore, when you click on any of your HTML elements with this class, the code runs for all of them, which is why you are seeing this behavior.
This article will show you a few options for doing what you want, correctly:
Backbone.js: Getting The Model For A Clicked Element
I would also recommend reading this one, to better understand the use of el in Backbone, and a few of the tricks and traps of it:
Backbone.js: Object Literals, Views Events, jQuery, and el

how to load a partial view on button click in mvc3

I have a DDL, on its change I load a partial view _GetX. Right now I am using it like this
#Html.DropDownListFor(model => model.XId, Model.XList, "Select", new { GetUrl = Url.Action("GetX", "Route") })
I need to load this partial view on clicking a button. How do I do this?
Assuming your GetX controller action already returns the partial view:
$(function() {
$('#someButton').click(function() {
// get the DDL. It would be better to add an id to it
var ddl = $('#XId');
// get the url from the ddl
var url = ddl.attr('GetUrl');
// get the selected value of the ddl to send it to the action as well
var selectedValue = ddl.val();
// send an AJAX request to the controller action passing the currently
// selected value of the DDL and store the results into
// some content placeholder
$('#somePlaceholder').load(url, { value: selectedValue });
return false;
});
});

Resources