How to print the 2 different value using one template - backbone.js

In my json data, i am getting projectName and assignedTo values(it accrues in all data),
example data:
[ {projectName:'project1',assignedTo:'some1'},{projectName:'project2',assignedTo:'some2'}]
my template is: (including my confused stuff)
<script id="listTemplate" type="text/template">
<%= projectName === projectName ? projectName : taskStatus%>//not works how can i mange?
</script>
In my view, i am converting the model "toJSON()", but i am getting the project name alone in the 2 links what i am use.
my request, Is it possible to use a single template to print two different values? - as a first time i need to print project name, later taskStatus with some condition, if so any suggestion please

The reason why it is constantly printing project name is because, in the template, you have...
<%= projectName === projectName ? projectName : taskStatus%>
projectName would always equal to projectName. What you'll want to do in this case is, in your Backbone.View, when you serialize the Model to be consumed by the template, you can do this...
Backbone.View.extend({
events: {
// An example on how to change the display
'click button.change-display': 'onChangeDisplayClicked'
},
template: _.template(...),
// Controls whether project name should be shown or not.
showProjectName: true,
onChangeDisplayClicked: function() {
// Flip the switch
this.showProjectName = !this.showProjectName;
// Re-render the View
this.render();
},
serialize: function() {
// Grab the data from model
var data = this.model.toJSON();
// Pass this data to the template to control what to be displayed.
data.showProjectName = this.showProjectName;
return data;
},
render: function() {
this.$el.html(this.template(this.serialize()));
}
});
... and in your template, you would...
<script id="listTemplate" type="text/template">
<%= showProjectName ? projectName : taskStatu s%>
</script>

Related

underscore template does NOT compile the model I pass in argument

I am doing internationalisation of an app I built. Basically, on clic of a flag an event is triggered and a callback function will fetch a json file containing the translated labels of the app. Then I set the json data to a model. That just works fine. Finally I compile the template with the model and I render it. But I just get a blank page, no error in the console, just a blank page. I console.log the model and the string that template() returns and they do NOT match.
here a simplified sample (the app is originally built in french *):
var app_multi_lang_model = Backbone.Model.extend({
MODE_AVANCE : "mode: AVANCE",
MODE_OPERATEUR: "mode : OPERATEUR",
MENU: "Menu",
LANGUE: "langue",
FRANCAIS: "Français",
ANGLAIS: "Anglais",
CHINOIS:"Chinois",
ANNULER: "annuler",
VALIDER: "valider"
});
var app_view= Backbone.View.extend({
el:"#app_template_placeholder" ,
template: _.template($('#app_template').html()),
events:{
"click #us_flag":"us_flag_clicked"
},
us_flag_clicked: function(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
app_multi_lang_model_instance.set(JSON.parse(xhttp.responseText));
app_view_instance.render();
}
};
xhttp.open("GET", "../languages/english.js", true); //loading the json file
xhttp.send();
},
render: function(){
console.log(this.model);
this.$el.html(this.template(this.model));
console.log(this.template(this.model));
}
});
var app_multi_lang_model_instance= new app_multi_lang_model();
var app_view_instance= new app_view({model:app_multi_lang_model_instance});
and this is how english.js looks like:
{
"MODE_AVANCE": "mode: AVANCED",
"MODE_OPERATEUR": "mode : OPERATOR",
"MENU": "Menu",
"LANGUE": "language",
"FRANCAIS": "French",
"ANGLAIS": "English",
"CHINOIS":"Chinese",
"ANNULER": "cancel"
}
and here are the screen of this.model just before .template(this.model) call:
and here a sample of the string that .template() returns
we see that the model attributes are now translated in english but the html which is supposed to be rendered still in french.
what am I doing wrong? why does template() is still returning the template with the former model whereas I passed it the updated one. And why the browser doesn't display the string returned by template()?
Okay I found out what was wrong! the problem is the way I've declared the model.
var app_multi_lang_model = Backbone.Model.extend({
MODE_AVANCE : "mode: AVANCE",
MODE_OPERATEUR: "mode : OPERATEUR",
MENU: "Menu",
LANGUE: "langue",
FRANCAIS: "Français",
ANGLAIS: "Anglais",
CHINOIS:"Chinois",
ANNULER: "annuler",
VALIDER: "valider"
});
the attributes are not wrapped in "default{...}" so my attributes are not considered as backbone attributes (I cannot access them using model.get). But they exist because I can do "app_multi_lang_model_instance.VALIDER" for example. When I fetch the JSON file and set data to my model using model.set I am not changing my attributes, I'm creating new one wrapped in "defaults:{...}" so my model looks like this:
var app_multi_lang_model = Backbone.Model.extend({
defaults:{
MODE_AVANCE : "mode: AVANCED",
MODE_OPERATEUR: "mode : OPERATOR",
MENU: "Menu",
LANGUE: "language",
FRANCAIS: "French",
ANGLAIS: "English",
CHINOIS:"Chinese",
ANNULER: "cancel",
VALIDER: "ok"
}
MODE_AVANCE : "mode: AVANCE",
MODE_OPERATEUR: "mode : OPERATEUR",
MENU: "Menu",
LANGUE: "langue",
FRANCAIS: "Français",
ANGLAIS: "Anglais",
CHINOIS:"Chinois",
ANNULER: "annuler",
VALIDER: "valider"
});
So when I pass it to _.template it will populate my html with the attributes it finds first, the unwrapped attributes.
I fixed that wrapping my attributes in "defaults:{...}" and replace this line:this.$el.html(this.template(this.model)); by this one: this.$el.html(this.template(this.model.toJSON())); otherwise the attributes won't be accessible.
But still, event if the returned html is now correct the browser doesn't display anything. why?

Backgrid formatter adding values from other columns

Is it possible, with BackGrid, to build a custom Cell vía formatter, composing values from hidden columns?
var grid = new Backgrid.Grid({
columns: [
{
name:"half_value_1",
cell:"string",
rendered: false
},
{
name:"half_value_2",
cell:"string",
rendered: false
},
{
name: "composite",
cell: "string",
formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
fromRaw: function (half_value_1, half_value_2) {
return half_value_1 + '/' + half_value_2;
}
})
}],
collection: col
});
Can I get the half_value_1 and half_value_2 inside the fromRaw function?
I think the best way to get the result you want is to use a custom cell rather than a custom formatter. You could do something like this for that particular column:
{
name: "composite",
cell: Backgrid.Cell.extend({
render: function(){
// You have access to the whole model - get values like normal in Backbone
var half_value_1 = this.model.get("half_value_1");
var half_value_2 = this.model.get("half_value_2");
// Put what you want inside the cell (Can use .html if HTML formatting is needed)
this.$el.text( half_value_1 + '/' + half_value_2 );
// MUST do this for the grid to not error out
return this;
}
})
}
That should work perfectly for you - I use it for a handful of grids in my projects. I didn't test this code though, so I may have typos :)
Key

backbone.js a url property must be defined

I'm trying to use Backbone.localStorage as a backend to an app.
I wrote a Model called Era, and a corresponding EraView. Pressing enter in the EraView causes the model to be saved, and this is where I get the error:
Uncaught Error: A "url" property or function must be specified
urlError backbone.js:1509
_.extend.url backbone.js:515
_.result underscore-min.js:1060
Backbone.sync backbone.js:1410
Backbone.sync backbone.localStorage.js:188
_.extend.sync backbone.js:276
_.extend.save backbone.js:476
karass.EraView.Backbone.View.extend.close era.js:61
karass.EraView.Backbone.View.extend.updateOnEnter era.js:75
Here is the code to the EraView
var karass = karass || {};
// Era Item View
// the DOM element for an era item
karass.EraView = Backbone.View.extend({
tagName: 'li',
className: 'era',
template: _.template( $('#era-template').html() ),
// The DOM events specified to an item
events: {
'dblclick .edit-input': 'edit',
'keypress .edit-input': 'updateOnEnter',
//'blur .edit': 'close',
},
// The EraView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between an era and a EraView in this karass,
// we set a direct reference on the model for convenience.
initialize: function(){
_.bindAll(this);
this.model.on('change', this.render, this);
},
// Re-renders the era item to the current state of the model and
// updates the reference to the era's edit input within the view
render: function(){
this.$el.html( this.template(this.model.attributes));
this.$era_start = this.$('.era-start');
this.$era_end = this.$('.era-end');
this.$era_start.attr('disabled', true);
this.$era_end.attr('disabled', true);
return this;
},
// Switch this view into editing mode, displaying the input field
edit: function(){
this.$('.edit-input').removeAttr('disabled');
this.$el.addClass('editing');
this.$('.edit-input').addClass('editing');
},
// Close the editing mode, saving changes to the era
close: function(){
this.$('.edit-input').attr('disabled', true);
var start = this.$era_start.val().trim();
var end = this.$era_end.val().trim();
if(start && end){
this.model.save({from: start, until: end});
}
this.$el.removeClass('editing');
this.$('.edit-input').removeClass('editing');
this.trigger('close');
},
updateOnEnter: function(e){
if(e.which !== ENTER_KEY && (!this.$era_start.val().trim() || !this.$era_end.val().trim())){
return;
}
this.close();
}
});
And this is the code for the era model:
var karass = karass || {};
karass.Era = Backbone.Model.extend({
defaults: {
from: '',
until: ''
},
});
I thought I didn't need a url while using localStorage.
Edit: I forgot to mention that while this behavior occurs in the Era/EraView itself, it also occurs in the Name model, which extends Era. Name in turn belongs in a Names collection. I don't know if this makes a difference, but I figured I add it.
Edit 2: The Names collection looks like this:
karass.Names = History.extend({
model: karass.Name,
localStorage: new Backbone.LocalStorage('karass-names'),
});
Edit 3: I posted all the code on jsfiddle: http://jsfiddle.net/herrturtur/CRv6h/
You don't need an url while using localStorage. But you need to set the localStorage property on your model or on your collection (if you set the localStorage on a collection the models inside the collection will "inherit" this setting):
karass.Era = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("EraCollection"),
// the EraCollection should be an unique name within your app.
defaults: {
from: '',
until: ''
},
});
If you don't setup the localStorage property the plugin falls back to the default ajax sync so you get the uri missing exception.

How do I use JQuery Datepicker with Backbone-Forms?

var User = Backbone.Model.extend({
schema: {
date: {type: 'Date'}
}
});
var user = new User();
var form = new Backbone.Form({
model: user
}).render();
$('.bootstrap').append(form.el);
What type do I enter to use the included JQuery-UI datepicker? There is no documentation on this other than:
The old jQuery editors are still included but may be moved to another repository:
jqueryui.List
jqueryui.Date (uses the jQuery UI popup datepicker)
jqueryui.DateTime
The schema type is declared in quotes and I can't figure out what the string for jqueryui.Date would be - and that doesn't work for sure.
You want to create a custom editor that you'll name for example: 'DatePicker'.
All the editors are attached to Backbone.Form.editors. Because the DatePicker is rendered exactly like a text field, we can use the text field as a base and only override the behavior specific to the datepicker.
I often use moment.js for some date related work, so this example also includes this. Also it's based on Bootstrap DatePicker and not the jQuery one, but this would be almost 100% the same.
Backbone.Form.editors.DatePicker = Backbone.Form.editors.Text.extend({
render: function() {
// Call the parent's render method
Backbone.Form.editors.Text.prototype.render.call(this);
// Then make the editor's element a datepicker.
this.$el.datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
weekStart: 1
});
return this;
},
// The set value must correctl
setValue: function(value) {
this.$el.val(moment(value).format('YYYY-MM-DD'));
}
});
That's it, you can now use your date picker like this:
schema: {
birthday: { title: 'When were you born', type: 'DatePicker'}
}
Not sure it undestanding right your Question
but I think you can atach the Jquery date pickers in the initialize method of the view

Dynamically add/remove regions to a layout

Is it possible to dynamically add and remove regions to a layout with Marionette? My app needs to be able to push and pop regions from a layout. This is similar to how GitHub pushes and pops views when you drill down in the source code of a project. They have the slide over animation when presenting the next view and then it slides back when you're backing out. The idea is that I need to keep the previous views around. Another analogy would be how UINavigationControllers work on iOS.
Or maybe I should just define a custom layout that is able to handle adding and removing regions on the fly?
I ended up implementing a container view to fit my needs. It cleans up event refs like you'd expect in Marionette.
https://github.com/ayoung/backbone-vs-marionette/blob/marionette/public/js/views/mainContainer.js
I'm not sure but you may be getting confused with the existence of some html and the displaying of that html?
I mean you can make a CompositeView of Items and only show one of the items at a time. Then use jQuery animate or another animation library to move through the CompositeView's Items.
Yes it is possible. Here is the code I use.
The layout:
var Layout = Marionette.LayoutView.extend({
initialize: function(options) {
options = _.extend({ regionTag: 'div' }, options);
this.mergeOptions(options, ['regionTag', 'regionName']);
},
template: false,
regions: {},
append: function(view) {
var viewClass = 'dynamic-layout-' + this.regionName,
viewCount = $('.' + viewClass).length + 1,
viewId = this.regionName + '-view-' + viewCount,
$el = $('<' + this.regionTag + '/>', {
id: viewId,
class: viewClass
});
this.$el.append($el);
var region = Marionette.Region.extend({
el: '#' + viewId
});
this.regionManager.addRegion(viewId, region);
this.regionManager.get(viewId).show(view);
},
appendEmpty: function(id, className, tag) {
tag = tag || 'div';
var data = { id: id, className: className, tag: tag };
var $el = Marionette.Renderer.render('#append-layout-template', data);
this.$el.append($el);
var region = Marionette.Region.extend({
el: '#' + id
});
this.regionManager.addRegion(id, region);
},
customRemove: function(regionId) {
this.regionManager.removeRegion(regionId);
}
});
A helpful template:
<script type="text/template" id="append-layout-template">
<<%= tag %> id='<%= id %>' class='<%= className %>'></<%= tag %>>
</script>
The controller:
var view = new SomeView();
// the region name will be a part of a unique id
var layout = new Layout({ regionName: 'myRegion' });
// add a dynamic region to the layout and a view to that region
layout.append(view);
// same as above (you have to name the id and class yourself)
var regionId = 'myRegionId';
layout.appendEmpty(regionId, 'someClassName', 'span');
layout.getRegion(regionId).show(view);
// remove a region
layout.customRemove(regionId);

Resources