Sane way to concat string and variable in Handlebars.js helper argument? - ember.js

I'm trying to build a simple modal component in Ember, but it seems the "logic-less" of Handlebars is too illogical for me. Is there any sane way to achieve a result somewhat this?
<h2>Nice block about {{title}}</h2>
<a href="#" data-toggle="modal" id="add-item-{{title}}"> {{!this works}}
{{#my-modal modal-id="add-item-{{title}}" header='New {{title}}'}} {{! those don't}}
<p>My body blabla</p>
{{/my-modal}}
Currently I end up by getting my modal id being "add-item-{{title}}", literally, as well as the modal title.
And... no, for now I'm not considering passing the "title" as a new param and using it in the modal. The modal header in another template might not be "New {{title}}" but "are you sure?" or "details about {{title}}".

What you're looking for the is the concat helper. Using it, your second example would become:
{{#my-modal modal-id=(concat 'add-item-' title) header=(concat 'New ' title)}}
<p>My body blabla</p>
{{/my-modal}}

I got here looking for a concat helper in handlebars.js. In case it's useful to someone landing here looking for the same, handlebars-helpers has an append helper built-in.
{{> my-modal modal-id=(append "add-item-" title) header=(append "New " title)}}
https://github.com/helpers/handlebars-helpers

Yep, passing in the title is how I do it. If you need to add something to the title that's more than just model.title, then stuff a computed property on your controller (es6 string interpolation syntax):
controller
modalHeader: function() {
return `New ${this.get('model.title')}`;
}.property('model.title')
template
{{#my-modal header=modalHeader}}
<p>My body blabla</p>
{{/my-modal}}
As for the id, you can do some fun stuff in the component to override it, see this codepen, but I don't know how it messes with ember. Why do you want to set an id for a modal anyway?

Related

Render sidebar based on model in EmberJS

I started with learning EmberJS and maybe the answer is trivial, but after some researching, I still can't find a solution.
In my model template, I have some buttons(each for the different object) which after click should expand sidebar with its details.
What do I want to reach is something like this:
Could someone provide me with some simple twiddle?
There are two ways to achieve this effect.
Using controller's variable
{{#foreach model as |obj|}}
<button onclick={{action (mut activeModel) obj}}>{{obj.name}}</button>
{{/foreach}}
<!--Somewhere later in template-->
{{#if activeModel}}
<!--Code of overlay and sidebar, close button sets activeModel to undefined-->
{{/if}}
Using child (nested) route
Parent template:
{{#foreach model as |obj|}}
{{#link-to 'parentRoute.childRoute' obj tagName="button"}}
{{obj.name}}
{{/link-to}}
{{/foreach}}
<!--Somewhere later in template-->
{{outlet}}
Child template should contain code of overlay and sidebar, close button redirects back to parent route
well, one of the options is that you can create components and pass the modified model(modify the model using onclick function) as the data to that component.
for example,
let us just say that this is your main template
<button onclick="changeSideBar()">click</button>
<div style="display:none; //render it in the left-half(using bootstrap models)">
{{sidebar-component model=model.modified}}
</div>
in the javascript code (component.js),
function changeSideBar()
{
var modified= ;//set as per your convienince by iterating actual models or by any means
this.set('model.modified',modified);
//make display of sidebar div "block"
}
sidebar-component is your component. make the component as per your wish.
hope it helps.
I can't help much without your templates or codes. It would be great if you provide some of your works.

Get contents of Meteor template

I have the following Meteor template:
<template name="homeBoxTpl">
<div>
Some content
</div>
</template>
I have an event binding so that when a button on the page is clicked it should get the html contents of the template "homeBoxTpl" - how can this be achieved?
What you need is an event handler, and like Chase say you can access the content of meteor templates using jquery, but meteor has its own way. In order to get a copy of the html, you can place a wrapper round the content in the template, then do this:
Template.homeBoxTpl.events({
'click #someButton':function(e,t){
var templateContents = t.$('.wrapperInTemplate').html();
}
})
Here we are using Template.$ wich returns a jQuery object of those same elements.
Take a look into Template.instances for more information
Just to clarify, where is the button? Is it in another template? I assume you only render homeBoxTpl once.
In which case the event handler for the template where your button exists will have no reference to another template instance. There is no global lookup where you can find all rendered instances of a specific Template
You will have to set an unique identifier "id" for it, or some other discerning info such as a class/attribute and find it via old fashioned JS DOM selectors/traversal.
document.getElementById is fastest, but if there are multiple instances of that template, t.firstNode does give you a good starting point for the DOM traversal.
However making your code dependent on a specific DOM layout is bad practice / too much coupling. Is there any reason why the data underlying that Template's HTML content isn't available somewhere else like a session or collection? It would perhaps be more flexible too to access the data not the HTML.
You can use jquery to access what you need. For example, if you have the following template:
<template name="homeBoxTpl">
<div class="content-container">
Some content
</div>
<button id="btn" type="button">Click me</button>
</template>
Then use the following javascript:
Template.homeBoxTpl.events({
'click #btn': function(e) {
e.preventDefault();
var content = $(".content-container").text();
console.log(content); //Result is "Some content"
}
});

ember.js does not {{bindAttr}} the <label> For attibute to the correct inputField.elementId in a collection

I'm trying to link a label to an input field using the {{bindAttr}} and the input field's [viewName].elementId. It works on a single entry view, but not when there are several records being displayed: it just links the label to the last input field in the collection. (This used to work in a previous iteration using an older ember library but now it doesnt.) I've created a fiddle but the gist of it is:
{{#each controller}}
<fieldset>
<label {{bindAttr for="view.tbFullName.elementId"}}>Full Name</label>
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
</fieldset>
{{/each}}
I thought maybe I could create a collectionView and create a calculated property for viewName which would generate a unique ID for each item in the collection, sort of mentioned in answer to another problem here. But that is getting WAY too complicated - just so that I can have the input field highlight itself if the user clicks on the corresponding label.
Any help appreciated.
Create a wrapper Ember.View around the label and input field. Let's call it App.FieldView:
App.FieldView = Ember.View.extend({
tagName: 'fieldset'
});
Then in your template:
{{#each controller}}
{{#view App.FieldView}}
<label {{bindAttr for="view.tbFullName.elementId"}}>Full Name</label>
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
{{/view}}
{{/each}}
Fiddle: http://jsfiddle.net/NQKvy/26/
Panagiotis Panagi, has answered the question correctly. I'll just add why this is happening, ie:- linking to the incorrect view.
The view property inside a template refers to the Ember View wrapping the html markup. This property however has different value depending on the context it is in.
This value is dependent on the view block it placed in. By default the template itself corresponds to a view in this case, ListOfPeopleTemplateView.
So when you are binding to view.tbFullName.elementId, you are actually binding to an {instance of ListOfPeopleTemplateView}.tbFullName.elementId. And when the loop finishes the only tbFullName visible is the last one.
Panagiotis Panagi's solution is to wrap the label inside another view, so the value of view changes to within that block, and hence points to the correct tbFullName
Finally an even easier way to achieve the same result is to wrap the textfield inside the label. Then you do not need the label for binding at all.
<label>Full Name
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
</label>
See this jsfiddle
Forms are somewhat tricky I must admit if you want to do things right. But there are is an ember add-on that comes to the rescue, for example easyForm.
Have a look it might helps you solving exact the problems you are facing, like the ones on having unique labels for your form fields etc.
Hope it helps.

Ember JS/Handlebars view helper

Currently, if we define our view as {{#view App.myView}}, ember/handlebars will wrap the view element inside a <div id="ember-1234" class='ember-view'>.
Is there a way to stop this?
You probably want to set tagName as ''.
App.MyView = Em.View.extend({
tagName: ''
});
At least it stops wrapping inner contents.
If you want to customize view element's id, you can use:
{{#view App.myView id="my-id"}}
I usually think my views as wrapper. For example, if the initial given html code is :
<div class="item"><p>my stuff></p></div>
Then I create a view with a tagName property as "div" (which is default), and classNames property as "item". This will render, with proper handlebars template :
{#view App.myView}
<p>my stuff></p>
{/view}
-> render as
<div id="ember-1234" class="ember-view item">
<p>my stuff></p>
</div>
Then if you need to have your proper ID on this div, you could define the "elementId" property on your view class (before create()). (#see source code)
I assume you are talking about the 'ember-view' class which is not customizable (element type being customizable thanks to the tagName attribute...).
Actually, Ember later uses this class to register (once!) an event listener on the document in order to dispatch events to the JS view.
I don't mind it would be that simpler to avoid using this class. There would have to find another way to select all ember's controlled element, but I have no idea how.
See source, # line 117.
If I understand you correctly you want to achieve something like jQuery's replaceWith? You can use this in Ember.js when my Pull Request gets merged: https://github.com/emberjs/ember.js/pull/574.
Also have a look at Create Ember View from a jQuery object

how can i unbind an emberjs handlebar helper

I have a custom handlebar helper:
Handlebars.registerHelper('foo', function(key) {
return (key + ' bar');
});
and in my html I have:
{{foo beer}}
the result is
<div id="ember127" class="ember-view">beer bar</div>
how can I make my own handlebar helper act like the ember {{unbound beer}} and just produce "beer bar" without any additional markup ?
So I think you might be confused on how the helpers, templates, and Ember views work exactly. The markup you created is expected and is the exact markup you'd get with a working unbound helper.
Ember.Handlebars templates are always placed within an Ember view object (as you have above). Something that a normal bound helper would produce would be:
<div id="ember127" class="ember-view">
<script id="metamorph-1-start" type="text/x-placeholder"></script>
beer bar
<script id="metamorph-1-end" type="text/x-placeholder"></script>
</div>
Now if you want to surround your string with some other tag than a div (lets say an anchor tag or something), then you'd need to create a view, set it's template and tag name, then append that view.
Take a look at this jsFiddle and take a look at the results pane in your inspector for some examples of what I'm talking about. Hope that clears things up for you.
Ember has a helper called unbound that lets you wrap another helper. You can thus turn your bound (automatically) foo helper into an unbound one like so
{{unbound foo beer}}