Localizing templates with backbone, underscore on mobile - backbone.js

I want to localize templates in my Phonegap/Backbone mobile App. I want to somehow override underscore render function in a way that would always append extra attribute with languages. Let me show that on example:
lets say I required (require.js) the HomeView template wich looks like:
<div>
<p><%= language.get('someText') %></p>
</div>
In my HomeView.js I have:
var template = _.template(HomeTemplate);
this.$el.html( template({language: LanguageModel}));
This works, but I don't want to always append this language attribute to underscore template. Could I somehow overwrite that render function so It would always include language Model?

You can put any JavaScript expression you like inside <%= ... %>. In particular, you can access globals. So, if you have a global application namespace:
// I'll call it `app` for lack of a better placeholder.
window.app = { ... };
Then you can put language in there:
app.language = your_language_model;
and access it in any template without supplying anything extra to the _.template call or the compiled template function:
var t = _.template('<%= app.language.get('pancakes') %>');
var h = t();
Demo: http://jsfiddle.net/ambiguous/jkmG7/1/

Related

Render view from another controller inside my view

I don't know if this is possible, and i feel like its something i should not do, but i need it, so lets say:
I have a controller "A", this controller has a function that do some calculations and then there is the view that will render a div with some fields inside of it.
ok that is working, i can't touch that.
but now i have also the controller "B", this controller also has a function and a view, this view shows somethings and i need to show what is in the first case inside a div here (among the divs that are already present).
ps: the function on controller "A" needs two parameters.
There are some parts in this project that people used iframe and then .load() to show what is in A inside the iframe in another view, but i want to know if i can do this with cakephp itself using div.
cake version 2.4.9
Controllers aren't meant to call functions in other Controllers in Cake, but Cake has a few features to let you share functions between Views.
From what you imply you are doing here, likely you could stick the function into a View Cell:
namespace App\View\Cell;
use Cake\View\Cell;
class MyCell extends Cell
{
// Pass whatever data you need to, to the Cell
public function display($var1, $var2)
{
// Your calculations previously done in Controller A, do here instead, Ex.
$this->set('unread_count, $var1 - $var2);
}
}
Then put whatever div contents in the Cell template:
<!-- templates/cell/MyCell/display.php -->
<div class="notification-icon">
You have <?= $unread_count ?> unread messages.
</div>
Then plop the output into both Controller A & B's views, ex.:
<!-- In Controller A's & B's template, render where needed, passing the data needed: -->
<?= $this->cell('MyCell', [$var1, $var2]); ?>
This is only one option - depending on what exactly you're doing in that Controller, you could maybe stick the shared function into a Table, as a Custom Finder. You could stick the shared function in a plain old PHP Trait or Library.

Reference to HTML elements in view, a convention?

I'm currently in the progress of learning Backbone.js and I'm using the book Developping Backbone Applications.
I have a questions about the reference to HTML elements and how they are stored. For example:
initialize: function() {
this.$input = this.$('#new-todo');
Here the HTML element with ID to-do is stored in the this.$input, why do we use the $ in front of input, is this merely a convention? If I change this.$input to this.input my code works fine. I find this confusing because the book states:
The view.$el property is equivalent to $(view.el) and view.$(selector) is equivalent to $(view.el).find(selector).
I would think that $(view.el) does something completely different than (view.el).
How is this.$input saved in Backbone.js? If I console.log it, it produces:
Object[input#new-todo property value = "" attribute value = "null"]
Could someone give me some insight? :)
Using $ infront of a variable name is just a naming convention. It helps developer in distinguishing variable holding jQuery objects from others.
view.$el is a helper variable provided by Backbone, so that we can use it directly, instead of explicitly forming the jQuery object. Hence view.$el is equivalent to $(view.el).
view.$el is assigned in setElement method:
setElement: function(element, delegate) {
// Some code
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
// Some code
}
Backbone.$ is reference to $ global variable exported by jQuery.
view.$(selector) is a method defined in View. It's definition does exactly same as $(view.el).find(selector)
$: function(selector) {
return this.$el.find(selector);
}

Updating some parts of a Backbone Marionette ItemView

I'm a new to Backbone.js and also to Backbone.Marionette and I'm having some troubles trying to follow the best patterns for my application.
I'm creating a mobile web application encapsulated by PhoneGap. I have a common layout (Facebook like): 2 sidebars, a main header and a content zone where all the views are loaded.
My initialization code for a Backbone.Marionette application is:
var App = new Backbone.Marionette.Application();
App.addRegions({
leftRegion: '#left-drawer',
rightRegion: '#right-drawer',
headerRegion: '#header',
contentRegion: '#content',
mainRegion: '#main'
});
This are my regions where the content will be attached. Later, just after initialize the router I have the next:
App.headerRegion.show(App.Views.Header);
App.leftRegion.show(App.Views.LeftSidebar);
App.rightRegion.show(App.Views.RightSidebar);
This will load my content to this new regions. The problem occurs in the HeaderView. The template for my Header view:
<button id="open-left"><img src="assets/img/icons/menu-icon.png"></button>
<h1 class="title logo"><img src="assets/img/logo.png" height="28px" width="167px"></h1>
<button id="open-right"><img src="assets/img/icons/chat-icon.png"></button>
The ItemView it's very simple too:
var HeaderView = Backbone.Marionette.ItemView.extend({
template: _.template(template),
});
In some parts of the app I don't want to use the <img> in the header. Some pages of the app will need to update this to a text, for example something like <h1>Settings</h1>. My question is, what's the best way to do it? Or better, what is the way to do it? Right now I don't know where to start and the only idea I've in mind is to create
a new ItemView with another template.
Thanks in advance!
Marionette provides a useful function for this case, getTemplate, and you use this function isntead of the template : template declarations, like this.
SampleView = Backbone.Marionette.ItemView.extend({
getTemplate: function(){
if (this.model.get("foo"))
return "#sample-template";
else
return "#a-different-template";
},
});
So in the parts of yur application where you need a diferent template wihtout image you just change modify a value on the view or your model that can be used in this function to made the change on the template at the time you call render.

How to use Select2 with Backbone Marionette views outside the DOM

I'm trying to integrate the Select2 widget with Backbone Marionette views. My simple setup uses a Marionette.CollectionView to create and handle the select tag and Marionette.ItemViews to render the option tags.
That basically looks like this:
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
itemView : SelectItemView,
tagName : "select",
onRender : function() {
this.$el.select2();
},
onClose : function() {
this.$el.select2("destroy");
}
}
SelectItemView = Backbone.Marionette.ItemView.extend({
tagName : "option",
render : function() {
// create the needed option tags
}
}
As you can see, I have to call the Select2 initialize and destroy methods upon render and close to have the needed additional tags added to the DOM.
This setup works very well, as long as the view handling the select tag (SelectCollectionView) has already been added to the DOM. If that is not the case, Select2's additional tags get lost, as they are not part of SelectCollectionView's $el and thus not added to the DOM.
I wonder how to elegantly solve this? One could add an extra div container and render everything inside it, but that would produce extra code for the script and the DOM. It also makes my view less versatile. I just hope for a better solution I didn't think of.
As you suspect, you are going to need a containing div to surround your template. Fortunately, this is really simple. Instead of what you currently have:
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
tagName : "select"
Get rid of tagName, and in your template (I'm assuming you use Handlebars or Underscore or something like that), define your HTML:
template.html
<select class="whatever-you-want"></select>
Then in your view:
SelectCollectionView.js
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
template: _.template(templateHtml)
Marionette will automatically wrap a div around your <select>, and then all of the extra markup that select2 adds will be safely contained within the view's $el.
It took me some time to figure it out, but I used to solve it this way:
var Select2View = Mn.View.extend({
template: _.template('<select class="form-control"><option></option></select>'),
onRender:function() {
this.$el.find('select').select2();
}
});
The important part here is
this.$el.find('select').select2();
which select the select tag, inside the element. But I don't use collection view in my example. If you need to fetch data you can use:
this.$el.find('select').select2({data: ['1','2','3','4']});
Also select2 provide very good API for manipulating select items during runtime.

Access $locale in angular.module('xxx').value

I'm using angular-ui:s date directive (uiDate) and need to configure it. I can do that like this:
app.value('ui.config', {
date: {
dateFormat: 'yy-mm-dd', // <-- I want to use $locale.shortDateFormat
changeMonth: true,
changeYear: false
}
});
where app is my application module.
What I would like to do is to define the dateFormat from angulars $locale object, which using i18n contains my cultures specific date-formats.
The value property of app does not seem to have access to $locale at that point though, so I'm feeling that this is the wrong way to do it.
How can I provide the dateformat and other stuff from the locale-object to my directives, without having to modify the source code of ui-date (since it's an external dependency of my project).
I can solve this by setting this object in my controllers, but that's not what I want. This is a configuration that should work in all my controllers.
AngularUI is currently undergoing a bit of refactoring which changes this slightly.
Anyway, you can always pass the dateFormat property to uiDate inline, it will be merged with the global configuration. I don't know if it's possible to set .value() from within app.run as was suggested though, but I would think .value() is supposed to be easily overridable. You can even try modifying the object since theoretically the reference would be preserved (depending on when the directive injection function is executed).
Sorry it's not more helpful.
You can use bootstrap to do that. You don't even need to be "inside" angular to use that.
More about bootstrap: http://docs.angularjs.org/guide/bootstrap and http://docs.angularjs.org/api/angular.bootstrap
Just a note ... When navigating on the site, set the version to 1.2 so you can see the comments. It's very helpfull.
HTML
<ul id="result">
</ul>
Javascript
var locale = angular.bootstrap(function ($locale) { }).get('$locale'),
html = '';
for(var i = 0; i < locale.DATETIME_FORMATS.MONTH.length; i++) {
html += '<li>' + locale.DATETIME_FORMATS.MONTH[i] + '</li>';
}
$('#result').html(html);
jsfiddle: http://jsfiddle.net/82aRW/

Resources