In my app, I am using the handlebars template plugin, I am little bit confused here, how can i integrate handlebars template to marionette Item View which is using separate template?
here is my code :
define([
'jquery',
'underscore',
'backbone',
'marionette',
'hbs!scripts/templates/login/loginTemp'], // this is my handlebars template.
function ($,_,Backbone,Marionette,loginTemplate) {
"use strict";
socialApp = window.socialApp || {};
socialApp.loginView = Backbone.Marionette.ItemView.extend({
tagName:'div',
className:'loginContainer',
template: '#loginTemplate' //this is template for login alone (from DOM )
});
return socialApp.loginView;
}
);
"loginTemp" - has all details what i require for login template.
You could override Marionette.TemplateCache.prototype.compileTemplate function to achieve needed behavior:
Marionette.TemplateCache.prototype.compileTemplate = function (yourRawTemplate) {
// In case if template is function
if (_.isFunction(yourRawTemplate)) {
return yourRawTemplate;
} else {
return Handlebars.compile(yourRawTemplate);
}
};
My "Marionette-Handlebars Boilerplate" might help you:
https://github.com/yuraji/marionette-handlebars-boilerplate
If you are using regions in your marionette application, such as
socialApp.addRegions({
loginRegion:'<id of the region>'
})
and if your template is written inside a script tag, then you can easily render your template in this manner:
//defining the view
socialApp.loginView = Marionette.ItemView.extend({
template:Handlebars.compile(document.getElementById('loginTemplate').innerHTML),
...
...//other code and view logic
});
and then,
var loginView = new socialApp.loginView(); //creating instance of the view
socialApp.loginRegion.show(loginView); //rendering it inside the region
Related
I'm changing the value of a property in my controller and the helper fails to recompute them.
Sample code here:
My template looks like,
{{#if (my-helper info)}}
<span>Warning</span>
{{/if}}
In my controller,
changeAction: function() {
let that = this,
info = that.get("info");
set(info, "showWarning", true);
}
my helper,
import { helper as buildHelper } from '#ember/component/helper';
export default buildHelper(function(params) {
let that = this,
info = that.get("info");
if(info.showWarning ) {
return true;
} else {
return false
}
});
I see several issues with your code:
The template helper seems to get an object as it's first and only position param: {{my-helper info}} while info is { showWarning: true }. A template helper does recompute if the value passed it changes but not if a property of that value changes. A quick fix would be {{my-helper info.showWarning}}.
In your template helper your are trying to access the property on it's this context. As far as I know that's not supported. As you are using a positional param and it's the first one, it's available as first entry inparams array. So your template helper should look like:
export default buildHelper(function([info]) {
if(info.showWarning ) {
return true;
} else {
return false
}
});
What version of Ember are you using? If it's >= 3.1 you don't need to use this.get() in your controller. If you are using Ember < 3.1 you need to use info.get() also in template helper.
But as described before I would not recommend passing an object to a template helper as it's only updated if the object itself is replaced. Changing a property of it is not enough. You might be able to do so using Class-based Helpers but I wouldn't recommend to do so as it's error prune.
I have a variable in JS, which could be the currentPage for instance.
and now i would like to do something like this:
{{> currentPage}}
Which, of course does not work!
I would actually like to write a Handlebars helper like this:
Handlebars.registerHelper('currentPage', function(context, options) {
var currentPage = ...;
return Handlebars._defaultHelpers(">",currentPage);
}
But unfortunately ">" is not registered as a helper function in Handlebars and I don't know how to access this code.
I could also imagine using something like this:
Handlebars.registerHelper('currentPage', function(context, options) {
var currentPage = ...;
document.body.appendChild(Meteor.render(Template[currentPage]));
return "";
});
Which kind of works, but breaks the updating system.
If I return an HTML string, the template doesn't get updated anymore.
I think this is pretty common, but I don't know how to solve this.
Do it without > mark. This is the method renderPage helper in Router, or yield in Iron Router, are defined.
html:
{{currentPage}}
js:
Handlebars.registerHelper('currentPage', function(...) {
var currentTemplate = ...;
var templateData = ...;
return new Handlebars.SafeString(Template[currentTemplate](templateData));
});
You can omit the templateData part if you don't need it.
I want to get handlebarjs template name using handlebar helper, how to do it? thanks! for example,
give:
a.hbs
<p>hello</p>
{{fileName}}
I want:
a.html
hello a
I think that the easy and the safe way, is to get the template of the current view.
And get your name, based in the identity, using Ember.TEMPLATES.
Since the TEMPLATES structure is { templateName: compiledTemplate }.
Ember.Handlebars.registerHelper('filename', function(options) {
var template = options.data.view.get('template');
for (var templateName in Ember.TEMPLATES) {
if (Ember.TEMPLATES[templateName] === template) {
return templateName;
}
}
});
Live demo http://jsbin.com/ucanam/674/edit
For instance, is there a way to nest my "i18n" helper inside another helper's hash variable?
{{view "SearchView" placeholder="{{t 'search.root'}}" ref="search" url="/pages/search" className='home-search' polyfill=true}}
Update: Handlebars now supports subexpressions, so you can just do:
{{view "SearchView" (t 'search.root')}}
Your scenario is not directly supported, but there a couple of workarounds you can use. The handlebars helpers are just javascript code, so you can execute them from within the helper code itself:
function translateHelper() {
//...
}
function viewHelper = function(viewName, options) {
var hash = options.hash;
if(hash.placeholder) {
hash.placeholder = translateHelper(hash.placeholder);
}
};
Handlebars.registerHelper('view', viewHelper);
Handlebars.registerHelper('t', translateHelper);
And just pass the i18n key to as the argument:
{{view placeholder="search.root"}}
This is nice, as long as your helper knows which arguments should be localized, and which not. If that is not possible, you can try running all the helper arguments through Handlebars, if they contain a handlebars expression:
function resolveNestedTemplates(hash) {
_.each(hash, function(val, key) {
if(_.isString(val) && val.indexOf('{{' >= 0)) {
hash[key] = Handlebars.compile(val)();
}
});
return hash;
}
function view(viewName, options) {
var hash = resolveNestedTemplates(options.hash, this);
}
And use the nested template syntax you described:
{{view placeholder="{{t 'search.root'}}" }}
I realize neither of these options are perfect, but they're the best I could think of.
I have a problem using knockoutjs with custom template bindings.
Suppose I have a HTML body like this:
<div id="1">
<div data-bind="template:{name: '2', data: data}"></div>
</div>
<div id="2">
<h3 data-bind="text: caption"></h3>
</div>
JS code looks like this:
var ViewModel2 = function () {
this.caption = ko.observable("Caption");
}
var ViewModel1 = function () {
this.data = new ViewModel2();
}
ko.applyBindings(new ViewModel1(), document.getElementById("1"));
If we test this code, everything will work just fine;
See JSFiddle example: http://jsfiddle.net/4eTWW/33/
Now suppose we want to make our custom template binding. We'll use 'templatex' binding instead of 'template'.
In HTML we need to change just one line:
<div data-bind="templatex:{name: '2', data: data}"></div>
Next, let's add custom template binding to JS:
/*Custom binding*/
ko.bindingHandlers.templatex = {
init: function (element) {
ko.bindingHandlers.template.init.apply(this, arguments);
},
update: ko.bindingHandlers.template.update
}
See: http://jsfiddle.net/4eTWW/35/
But in this case we have an error, saying that it can't find 'caption' in the model.
Now let's add template {} to html bindings:
<div data-bind="template: {}, templatex:{name: '2', data: data}"></div>
See: http://jsfiddle.net/4eTWW/36/
And now everything works just fine.
It seems that while binding parent div it can't determine that child div is a template.
So how can I mark it as a template in my custom template binder?
Thanks.
You have wrong update handler, change to this:
ko.bindingHandlers.templatex= {
init: function(element) {
// do things
return ko.bindingHandlers.template.init.apply(this, arguments);
},
update: function(element) {
return ko.bindingHandlers.template.update.apply(this, arguments);
}
}
Here is working fiddle: http://jsfiddle.net/vyshniakov/4eTWW/39/
I don't think you can use a custom binding to create a new template engine. You need to register your custom engine with ko.setTemplateEngine().
From the knockoutjs source:
If you want to make a custom template engine,
[1] Inherit from the ko.templateEngine class (like ko.nativeTemplateEngine does)
[2] Override 'renderTemplateSource', supplying a function with this signature:
function (templateSource, bindingContext, options) {
// - templateSource.text() is the text of the template you should render
// - bindingContext.$data is the data you should pass into the template
// - you might also want to make bindingContext.$parent, bindingContext.$parents,
// and bindingContext.$root available in the template too
// - options gives you access to any other properties set on "data-bind: { template: options }"
//
// Return value: an array of DOM nodes
}
[3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
function (script) {
// Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
// For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
}
This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
and then you don't need to override 'createJavaScriptEvaluatorBlock'.
Example: http://jsfiddle.net/6pStz/ (see Note 7 on this page)