Ok, so let me preface this by saying I'm completely new to Ember. I'm having an interesting time just trying to get a basic binding to work. Here's my relevant code:
App.js
var App = Ember.Application.create({
rootElement: '#emberApp'
});
And routes.js:
App.Router.map(function () {
});
App.IndexRoute = Ember.Route.extend({
model: function () {
return { Foo: 3 };
}
});
And then here is my HTML document:
<div id="emberApp"></div>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/handlebars.js"></script>
<script src="~/Scripts/ember-1.4.0.js"></script>
<script src="~/Scripts/EmberApp.js"></script>
<script src="~/Scripts/Routes.js"></script>
<script type="text/x-handlebars" data-template-name="index">
<div>The current foo value: {{Foo}}</div>
{{input valueBinding=Foo}}
</script>
The intended result is that when I type in the input field created, the value that is bound in the template changes at the same time. It should be fairly simple right? I have to be missing something.
The template correctly renders The current foo value: 3, and it renders a text field. However, typing anything into this field does nothing to the above binding. I have tried marking it with a valueBinding tag, as well as switching to Ember.TextField instead of a input helper. I've also tried making a custom Ember.Object.extend class and returning that as the model for the index route.
What am I missing in order to bind the text box value to the {{Foo}} value in the template?
EDIT - Ok, I've figured out it's because of the capitalization of the variable: foo works, but not Foo. Why is this?
I'm expecting to receive JSON results similar to this:
{
RemoteUserId: 0,
UserPhotoUrl: '....',
RemoteUserName: 'Bob',
}
I'm assuming this means I need to 'hide' these values by making controller wrappers for each element? ie:
remoteUserId: function() {
return this.get('RemoteUserId');
}.property()
I'm afraid you've been bitten by one of Embers naming conventions which is normally awesome as it usually means things just work, but occasionally will bite you if you're not aware of it.
Ember expects that Classes or Namespaces are capitalized and that instances are lowercase. When Ember sees the Foo property used in a binding it assumes it's a namespace and will then look for a global variable called Foo instead of a controller property.
When you use {{Foo}} in a template the behavior is slightly different as Ember will first check the current context (the controller) to see if the property exists there. If it does it uses that value, otherwise it will assume it's a namespace and look for it globally. Bindings don't work like templates due to performance concerns as you don't want to have to check two locations for a value in a binding that could be updated very frequently (like a textbox being typed in).
This is why you can use Foo in the template and it works:
<script type="text/x-handlebars" data-template-name="index">
<!-- This works! -->
<div>The current foo value: {{Foo}}</div>
</script>
But when you try to use Foo as part of a binding it won't work:
<script type="text/x-handlebars" data-template-name="index">
<!-- This doesn't work as Ember thinks Foo is global (i.e., a namespace) -->
{{input valueBinding=Foo}}
</script>
Your best bet is to just follow ember conventions and make sure all your property names start with a lowercase character. However, if you want to continue using properties in your controllers that start with a capital character then you will need to explicitly tell Ember that the property is from the controller and is not global when you try to use it in a binding:
<script type="text/x-handlebars" data-template-name="index">
<!-- Tell Ember Foo is in the controller which is what we want-->
{{input valueBinding=controller.Foo}}
</script>
Here is a Fiddle demonstrating everything written above:
http://jsfiddle.net/NQKvy/881/
Its because in Ember properties should start with a lowercase letter! Uppercase letters are globals.
so u could simple convert your JSON bevore import it into ember:
var decapitalize = function(source) {
var result = {};
for(var prop in source) {
result[prop.substr(0,1).toLowerCase() + prop.substr(1)] = source[prop];
}
return result;
};
Related
For few days, I've begun to use Handlebars and I'm currently trying Decorators. I've understood how it works when it remains simple :
Ex: decorate the name alone : bill gaTes -> Mr Bill GATES
I then tried to use decorator in a "each" loop, to do exactly the same as before but with a list of people instead of just one. The problem is: when I am in the decorator function, I need to know which element (from the array) I am looking at.
So I would like to either give in argument the index of the "each" loop (then, as I have access to the data, I could retrieve the current element) or the current element.
I tried to use #index (which usually works well), but I get undefined when debugging.
And I can't find a way to get the current element in my decorator.
Here is the code:
index.html
<!doctype html>
<html>
<head>
<title>Test Handlebars Decorators 4</title>
</head>
<body>
<div id = "main"> </div>
<script id = "rawTemplate" type = "text/x-handlebars-template">
{{#each this}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter #index}}
<br />
{{/each}}
</script>
<script type = "text/javascript" src = "js/lib/handlebars.min-latest.js"></script>
<script type = "text/javascript" src = "js/lib/jquery-2.2.4.min.js"></script>
<script type = "text/javascript" src = "js/data.js"></script>
<script type = "text/javascript" src = "js/index.js"></script>
</body>
</html>
data.js
var data = [{
"firstname": "Bill",
"lastname": "Gates",
"gender": "MALE"
}, {
"firstname": "Hillary",
"lastname": "Clinton",
"gender": "FEMALE"
}];
function formatMale(author) {
return "M. " + author.firstname + " " + author.lastname.toUpperCase();
}
function formatFemale(author) {
return "Mme " + author.firstname + " " + author.lastname.toUpperCase();
}
Handlebars.registerDecorator('activateFormatter', function(program, props, container, context) {
var genderHelper;
var gender = context.args[0] || context.data.root[0].gender;
switch(gender) {
case "MALE":
genderHelper = formatMale;
break;
case "FEMALE":
genderHelper = formatFemale;
break;
default:
console.log('Gender format not set. Please set before rendering template.');
genderHelper = function() {};
}
container.helpers = {
formatGenderHelper: genderHelper
};
});
index.js
// Get the template
var rawTemplate = $('#rawTemplate').html();
// Compile the template
var compiledTemplate = Handlebars.compile(rawTemplate);
// Apply the template on the data
var content = compiledTemplate(data);
// Finally, re-inject the rendered HTML into the DOM
$('#main').html(content);
If you need further information, please let me know.
Thanks you for helping :)
There are two issues that make your example fail. One is a slight issue with your code, and the other is the way decorators seem to work, which is essentially just "not in loops" (unless you're using partials, see the last section of this answer).
First, you haven't told the decorator what to do with the #index that you're passing to it. You could change the decorator function, but since you have the decorator inside the #each block, the this is the same one that gets passed to formatGenderHelper, meaning that we can pass the decorator this.gender which resolves to just the gender string. This means that the writer of the template knows exactly what they're passing to the decorator, and the logic isn't trying to second-guess what the template is telling it.
index.html
...
{{#each this}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter this.gender}}
<br />
{{/each}}
...
Also, I figure you are basing your example on Ryan Lewis's sitepoint demo. One problem/limitation his code has that isn't immediately obvious (because in the simple case it isn't even an issue), is that his decorator overwrites of all of the available helpers except for the formatting one it provides. To avoid "Error(s): TypeError: Cannot read property 'call' of undefined" errors when doing things that are a little bit more complex, I recommend using this in your decorator function.
data.js
Handlebars.registerDecorator('activateFormatter', function(program, props, container, context) {
// leaving out the code here for brevity
container.helpers = Object.assign({}, container.helpers, {
formatGenderHelper: genderHelper
});
});
The second issue is a general one with using decorators in loops, that is simply a result of the way Handlebars works with decorators. According to the decorators API doc,
Decorators are executed when the block program is instantiated and are passed (program, props, container, context, data, blockParams, depths).
This means (afaict) that when the template for the block is first instantiated, meaning before any of the other helpers or programs that will need to run on the block, it executes the decorator, and the decorator makes the modifications it needs, before executing the program that created the block (in this case the #each). This means that to get the decorator to differentiate each iteration of the #each differently, it needs to run in a grandchild block of the #each, so it is executed after the #each but before the formatGenderHelper, however, if you're doing this and redefining the decorated helper with each iteration based on the iterated-over context, you're better off just registering a helper that has the gender formatting logic baked in.
However, that's sort of a non-answer. So, to answer your question, I've found that you can make handlebars do what you're trying to do, but it's sort of a hack. Since the trick is that you need the block with the decorator to be rendered from a sub-sub block of the #each we can render it in a partial. To do this we can stick it in another file and register that as a partial, but that's not really convenient, so we have two better options. 1) we can wrap the code in an undefined partial-block and let Handlebars render it as the fallback block when that partial fails, or (the slicker option) 2) we can use the provided builtin decorator #*inline to define an inline partial.
Fail-over partial block method
index.html
...
{{#each this}}
{{#>nothing}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter this.gender}}
<br />
{{/nothing}}
{{/each}}
...
#*inline method
index.html
...
{{#*inline "formattedAuthor"}}
Decorated: {{formatGenderHelper this}}
<br />
{{/inline}}
{{#each this}}
{{#>formattedAuthor}}
{{* activateFormatter this.gender}}
{{/formattedAuthor}}
{{/each}}
...
The key here is that we use our *activeFormatter decorator to dynamically reconfigure the partial each time it gets called. The second, inline example demonstrates this more clearly. There may certainly be other good use-cases, but this is where I see decorators really shining, i.e. allowing us to dynamically reconfigure the logic or helpers of partials right from where we call them.
However, there is a caveat: if our partial uses a helper that is only provided in a decorator that is called from outside of that partial's definition (like we have above in example 2) the partial will not be able to find the helper if that decorator is not called in the right place. This is also why it's better to use either of the two methods above for providing the partial: we keep the definition of the partial in the same file as the decorator call, so we can know that the helper is only provided dynamically and not registered globally.
I am new to Meteor and tried to follow the "Your First Meteor Application":
http://meteortips.com/first-meteor-tutorial/
I tried to define a helper function.
In the html file I wrote:
<head>
<title>Leaderboard</title>
</head>
<body>
<h1>Leaderboard</h1>
{{> leaderboard}}
</body>
<template name="leaderboard">
<!-- Hello World -->
<!-- {{player}} -->
<!-- {{otherHelperFunction}} -->
<ul>
{{#each player}}
<li>{{name}}:{{score}}</li>
{{/each}}
</ul>
{{numOfPlayer}}
</template>
And in the JS file I wrote:
if(Meteor.isClient){
Template.leaderboard.helpers({
"player": function(){
// return "Some other text";
return PlayersList.find();
},
"numOfPlayer": function(){
// return "Some other text";
return PlayersList.find().count();
},
"otherHelperFunction": function(){
return "Some other funciton";
}
})
Template.leaderboard.player = function(){
return "Some other text";
}
// console.log("Hello client");
}
if(Meteor.isServer){
console.log("Hello server");
}
PlayersList = new Mongo.Collection('players');
So here in the JS file for the client part, I defined two "player" helper functions: one in the old way and one in the new way. The old way is actually that I forgot to comment out but when I run this project, the website turned out to be executed in the "new way" and seemed that the old-way defined "player" helper function did not influence the project at all and the compiler did not say that there is any error or ambiguity (since you can see these two "player" helper function are defined for different functionalities). What is the reason for this? Is it because that the old-way defined helper function is overwritten by the new helper function?
This is the output interface.
From the Meteor source code:
The first identifier in a path is resolved in one of two ways:
Indexing the current data context. The identifier foo refers to the foo property of the current data context object.
As a template helper. The identifier foo refers to a helper function (or constant value) that is accessible from the current template.
Template helpers take priority over properties of the data context.
Your Template.leaderboard.player function definition is on the data context, therefore Blaze looks for a template helper first. Since you have the helper defined, it takes precedence and gets executed.
It is because the new way overrides the old way. New way assigns a new function reference the variable 'player'.
I'm currently dealing with handlebars.js in an express.js application. To keep things modular, I split all my templates in partials.
My problem: I couldn't find a way to pass variables through an partial invocation. Let's say I have a partial which looks like this:
<div id=myPartial>
<h1>Headline<h1>
<p>Lorem ipsum</p>
</div>
Let's assume I registered this partial with the name 'myPartial'. In another template I can then say something like:
<section>
{{> myPartial}}
</section>
This works fine, the partial will be rendered as expected and I'm a happy developer. But what I now need, is a way to pass different variables throught this invocation, to check within a partial for example, if a headline is given or not. Something like:
<div id=myPartial>
{{#if headline}}
<h1>{{headline}}</h1>
{{/if}}
<p>Lorem Ipsum</p>
</div>
And the invokation should look something like this:
<section>
{{> myPartial|'headline':'Headline'}}
</section>
or so.
I know, that I'm able to define all the data I need, before I render a template. But I need a way to do it like just explained. Is there a possible way?
Handlebars partials take a second parameter which becomes the context for the partial:
{{> person this}}
In versions v2.0.0 alpha and later, you can also pass a hash of named parameters:
{{> person headline='Headline'}}
You can see the tests for these scenarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462
https://github.com/wycats/handlebars.js/blob/e290ec24f131f89ddf2c6aeb707a4884d41c3c6d/spec/partials.js#L26-L32
Just in case, here is what I did to get partial arguments, kind of. I’ve created a little helper that takes a partial name and a hash of parameters that will be passed to the partial:
Handlebars.registerHelper('render', function(partialId, options) {
var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
source = $(selector).html(),
html = Handlebars.compile(source)(options.hash);
return new Handlebars.SafeString(html);
});
The key thing here is that Handlebars helpers accept a Ruby-like hash of arguments. In the helper code they come as part of the function’s last argument—options— in its hash member. This way you can receive the first argument—the partial name—and get the data after that.
Then, you probably want to return a Handlebars.SafeString from the helper or use “triple‑stash”—{{{— to prevent it from double escaping.
Here is a more or less complete usage scenario:
<script id="text-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="text" id="{{id}}"/>
</script>
<script id="checkbox-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="checkbox" id="{{id}}"/>
</script>
<script id="form-template" type="text/x-handlebars-template">
<form>
<h1>{{title}}</h1>
{{ render 'text-field' label="First name" id="author-first-name" }}
{{ render 'text-field' label="Last name" id="author-last-name" }}
{{ render 'text-field' label="Email" id="author-email" }}
{{ render 'checkbox-field' label="Private?" id="private-question" }}
</form>
</script>
Hope this helps …someone. :)
This can also be done in later versions of handlebars using the key=value notation:
{{> mypartial foo='bar' }}
Allowing you to pass specific values to your partial context.
Reference: Context different for partial #182
This is very possible if you write your own helper. We are using a custom $ helper to accomplish this type of interaction (and more):
/*///////////////////////
Adds support for passing arguments to partials. Arguments are merged with
the context for rendering only (non destructive). Use `:token` syntax to
replace parts of the template path. Tokens are replace in order.
USAGE: {{$ 'path.to.partial' context=newContext foo='bar' }}
USAGE: {{$ 'path.:1.:2' replaceOne replaceTwo foo='bar' }}
///////////////////////////////*/
Handlebars.registerHelper('$', function(partial) {
var values, opts, done, value, context;
if (!partial) {
console.error('No partial name given.');
}
values = Array.prototype.slice.call(arguments, 1);
opts = values.pop();
while (!done) {
value = values.pop();
if (value) {
partial = partial.replace(/:[^\.]+/, value);
}
else {
done = true;
}
}
partial = Handlebars.partials[partial];
if (!partial) {
return '';
}
context = _.extend({}, opts.context||this, _.omit(opts, 'context', 'fn', 'inverse'));
return new Handlebars.SafeString( partial(context) );
});
Sounds like you want to do something like this:
{{> person {another: 'attribute'} }}
Yehuda already gave you a way of doing that:
{{> person this}}
But to clarify:
To give your partial its own data, just give it its own model inside the existing model, like so:
{{> person this.childContext}}
In other words, if this is the model you're giving to your template:
var model = {
some : 'attribute'
}
Then add a new object to be given to the partial:
var model = {
some : 'attribute',
childContext : {
'another' : 'attribute' // this goes to the child partial
}
}
childContext becomes the context of the partial like Yehuda said -- in that, it only sees the field another, but it doesn't see (or care about the field some). If you had id in the top level model, and repeat id again in the childContext, that'll work just fine as the partial only sees what's inside childContext.
The accepted answer works great if you just want to use a different context in your partial. However, it doesn't let you reference any of the parent context. To pass in multiple arguments, you need to write your own helper. Here's a working helper for Handlebars 2.0.0 (the other answer works for versions <2.0.0):
Handlebars.registerHelper('renderPartial', function(partialName, options) {
if (!partialName) {
console.error('No partial name given.');
return '';
}
var partial = Handlebars.partials[partialName];
if (!partial) {
console.error('Couldnt find the compiled partial: ' + partialName);
return '';
}
return new Handlebars.SafeString( partial(options.hash) );
});
Then in your template, you can do something like:
{{renderPartial 'myPartialName' foo=this bar=../bar}}
And in your partial, you'll be able to access those values as context like:
<div id={{bar.id}}>{{foo}}</div>
Yes, I was late, but I can add for Assemble users: you can use buil-in "parseJSON" helper http://assemble.io/helpers/helpers-data.html. (Discovered in https://github.com/assemble/assemble/issues/416).
Not sure if this is helpful but here's an example of Handlebars template with dynamic parameters passed to an inline RadioButtons partial and the client(browser) rendering the radio buttons in the container.
For my use it's rendered with Handlebars on the server and lets the client finish it up.
With it a forms tool can provide inline data within Handlebars without helpers.
Note : This example requires jQuery
{{#*inline "RadioButtons"}}
{{name}} Buttons<hr>
<div id="key-{{{name}}}"></div>
<script>
{{{buttons}}}.map((o)=>{
$("#key-{{name}}").append($(''
+'<button class="checkbox">'
+'<input name="{{{name}}}" type="radio" value="'+o.value+'" />'+o.text
+'</button>'
));
});
// A little test script
$("#key-{{{name}}} .checkbox").on("click",function(){
alert($("input",this).val());
});
</script>
{{/inline}}
{{>RadioButtons name="Radio" buttons='[
{value:1,text:"One"},
{value:2,text:"Two"},
{value:3,text:"Three"}]'
}}
I would like to store different objects in the same controller content array and render each one using an appropriate view template, but ideally the same view.
I am outputting list objects using the code below. They are currently identical, but I would like to be able to use different ones.
<script type="text/x-handlebars">
{{#each App.simpleRowController}}
{{view App.SimpleRowView class="simple-row" contentBinding="this"}}
{{/each}}
</script>
A cut-down version of the view is below. The other functions I haven't included could be used an any of the objects, regardless of model. So I would ideally have one view (although I have read some articles about mixins that could help if not).
<script>
App.SimpleRowView = Em.View.extend({
templateName: 'simple-row-preview',
});
</script>
My first few tests into allowing different object types ended up with loads of conditions within 'simple-row-preview' - it looked terrible!
Is there any way of dynamically controlling the templateName or view used while iterating over my content array?
UPDATE
Thanks very much to the two respondents. The final code in use on the view is below. Some of my models are similar, and I liked the idea of being able to switch between template (or a sort of 'state') in my application.
<script>
App.SimpleRowView = Em.View.extend({
templateName: function() {
return Em.getPath(this, 'content.template');
}.property('content.template').cacheable(),
_templateChanged: function() {
this.rerender();
}.observes('templateName'),
// etc.
});
</script>
You can make templateName a property and then work out what template to use based on the content.
For example, this uses instanceof to set a template based on the type of object:
App.ItemView = Ember.View.extend({
templateName: function() {
if (this.get("content") instanceof App.Foo) {
return "foo-item";
} else {
return "bar-item";
}
}.property().cacheable()
});
Here's a fiddle with a working example of the above: http://jsfiddle.net/rlivsey/QWR6V/
Based on the solution of #rlivsey, I've added the functionality to change the template when a property changes, see http://jsfiddle.net/pangratz666/ux7Qa/
App.ItemView = Ember.View.extend({
templateName: function() {
var name = Ember.getPath(this, 'content.name');
return (name.indexOf('foo') !== -1) ? 'foo-item' : 'bar-item';
}.property('content.name').cacheable(),
_templateChanged: function() {
this.rerender();
}.observes('templateName')
});
Is it possible to use the subscriber/observer pattern in Ember.js? For example, view A and view B both listens to changes inside a model C. This requires model C to be able to trigger custom events. I've been trying to figure out how to make a model trigger event in Ember.js but no luck so far.
I believe the feature you are looking for is called "Bindings" in Ember.js.
There are tons of examples on the homepage that describe how to do what you are suggesting, but here is a quick recap:
window.MyApp = Ember.Application.create();
MyApp.MyModel = Ember.Object.create({
myProperty: "Hello World!",
goodbye: function() {
this.set("myProperty", "Goodbye!");
})
});
MyApp.modelInstance = MyApp.MyModel.create();
Now create your two views inside your <body> tag:
<script type="text/x-handlebars">
View1: <b>{{MyApp.modelInstance.myProperty}}</b>
</script>
<script type="text/x-handlebars">
View2: <b>{{MyApp.modelInstance.myProperty}}</b>
</script>
Now the page should render and you'll see both views say "Hello World!". Open up the console and type
MyApp.modelInstance.goodbye();
And you'll see your views change to say "Goodbye!".
The views automatically create Bindings to MyApp.modelInstance.myProperty by using the double curly braces, but you can create bindings in a variety of ways. Whenever the value of myProperty changes, all of the bindings will be automatically updated. Note, however, that you must call set("myProperty", "something new") so that Ember knows to update the bindings for you; it won't fire any change events if you just say myProperty = "something new".
At least in Sproutcore, that is what bindings are for.
If you have a model
App.Person = SC.Object.extend({
name: 'Bruce Banner'
});
You would then have a controller such as
App.personController = SC.ObjectController.create();
You could then set the content on the controller
App.personController.set('content', somePerson);
Now, any view can bind to the data on the model object.
SC.LabelView = SC.LabelView.extend({
...
valueBinding: 'App.personController.name'
})
So if you ever change the name
somePerson.set('name', 'Chris');
The view will update automatically.