Ember HTML attribute with spaces - ember.js

I have this Bootstrap accordion item
{{#bs-accordion-item value=index title=client.name}}
But I would like the title attribute to contain a string with a mix of variable data and static text, such as title="{{client.value}} at {{client.name}}" but since I can't use the quotes I'm stuck...

You can use the concat helper.
{{#each model as |rental|}}
{{#bs-accordion selected=selected}}
{{#bs-accordion-item value=rental.id title=(concat rental.title " Owned by " rental.owner)}}
{{!--The accordion content goes here--}}
{{/bs-accordion-item}}
{{/bs-accordion}}
{{/each}}

This is what Computed Properties in Ember are for...
You can construct the desired string using a computed property in your controller, for example, and assign the computed property to the title attribute.
In the controller, you could do something like this:
client: {
name: 'Indianapolis Motor Speedway',
value: 'Alexander Rossi',
},
constructedString: Ember.computed('client.value', 'client.name', function () {
const clientValue = this.get('client.value');
const clientName = this.get('client.name');
return `${clientValue} at ${clientName}`;
}),
In the bs-accordion-item helper, the constructedString value is assigned to the title attribute:
{{ bs-accordion-item value=index title=constructedString }}
And the bs-accordion-item template could then reference title, like so:
<h3>{{ title }}</h3>
I've created an Ember Twiddle example to demonstrate.

Related

Ember.js: Is it possible to have a component for each field in a model?

So I thought I had this solved already as it worked with my prototype using just arrays, but now that I'm actually dealing with the model, I'm not sure if it will work.
If I create a property on my controller like so:
myAttributes: [{
name: 'attr1',
label: 'Attribute 1',
value: null
}, {
name: 'attr2',
label: 'Attribute 2',
value: 1
}]
then I can loop through myAttributes with {{#each}}, so while each object is essentially a field, that's different than the fields on a single model, which only have a value, so I can't do {{#each model as |rec|}} when there's only one record.
In a nutshell, I want to have a button group to set the value for each field in my model, like so:
I have around 60 of these fields so that's why I wanted to use {{#each}} and my component for each field, but of course each goes over records in the model, not fields in a record.
Is this impossible to do? Do I just have to bite the bullet and write out the markup for each field like I would do if I had only a few fields?
Update: Even if I could loop through the fields on a single record (maybe with {{#each model.#each as |field|}}?), for this case what I also need to do is break out the fields into sections in the UI, so for example loop through fields 1-10 in the first section, and 11-20 in the next section, and there doesn't seem to be a good way to do that.
In the end, I think I'm better off just using a component on each field, like so:
{{attribute-component value=model.attr1}}
{{attribute-component value=model.attr2}}
.
.
.
There's a neat helper called each-in that iterates over key/attributes of an object.
https://guides.emberjs.com/v2.6.0/templates/displaying-the-keys-in-an-object/
There is a way to do that in your case, but I think that actually a simpler way to get the same effect is this:
template.hbs
{{#each myArrayOfPeople as | person | }}
{{#each attribs as | anAttrib | }}
{{get person (mut anAttrib)}},
{{/each}}
<br>
{{/each}}
component.js
attribKeys:['name', 'phoneNumber', 'otherAttrib'];
This will output something like:
joe, 123, something,
sam, 456, anotherthing,
sarah, 944, foo
you can use that same (mut anAttrib) helper to bind an attribute to an input or whatever your need is.
{{attribute-component value=(mut anAttrib)}}
I have now forgone trying to iterate over each field because I actually need to break out the fields into accordion containers based on other criteria, although #averydev's answer about nested {{#each}} was a very cool tip and useful.
I also updated to Ember 1.13 in order to use the mut helper (although I had previously used an action on the component, that passes the new value to an action in the controller, that sets the value to the model property. Since that was convoluted, this new method using mut is much more understandable.
Just to help anyone else out in the future, here's my code (simplified for SO, the real code has nothing to do with rooms and furniture). I have a custom component that uses a radio button group component to actually set the value (this is from ember-radio-button):
template.hbs
{{room-style title='Living Room' field=(mut model.livingRoomStyle)}}
{{room-style title='Master Bedroom' field=(mut model.masterStyle)}}
templates/components/room-style.hbs
<div class="btn-group" data-toggle="buttons">
{{#radio-button value=1 groupValue=field classNames="btn btn-default" changed="changed"}}
Modern
{{/radio-button}}
{{#radio-button value=2 groupValue=field classNames="btn btn-default" changed="changed"}}
French
{{/radio-button}}
{{#radio-button value=3 groupValue=field classNames="btn btn-default" changed="changed"}}
Rustic
{{/radio-button}}
</div>
components/room-style.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
changed: function() {
//update() is a function you get with attrs.[prop] when using the mut helper
this.attrs.field.update(this.get('field'));
}
}
});

Ember.js/handlebars how to bind variable passed to a component to a model

Let's say I have a model called "Article" with only one property called "title"...
I could write hanblebars to edit the article property like this:
<span>Title</span><span>{{input value=title}}</span>
And Ember/handlebars magically binds the value of that input box to the "title" property value.
No problem there. But I am working on a project in which I will need to generate the handlebars code dynamically, based on a model definition model.
For example, I will not know there is a property called "title", but would have instead to loop into a "modelFields" list in the model definition model.
So, the handlebars will be something like this:
Looking at the code below:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
The result HTML for the "title" property will be:
<span>Title</span><span><input value="title"></span>
Now here is my question, is there a way to have the value coming dynamically from propertyName (title, in this example) to be handled by ember as a binding property title, instead of a string title?
To clarify, is there a way for the result of this:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
to be treated as this (title is a binding property):
<span>Title</span><span>{{input value=title}}</span>
instead of this (title is a string):
<span>Title</span><span><input value="title"></span>
?
I tried with views,components, with no luck.
I've found answer in another post that help me find the solution. The post is:
Ember.js: TextField with dynamic binding
Although, for my purposes, I had to tweak his/her solution a little bit:
Ember.Handlebars.helper('dataTextField', function (key, options) {
options.hash.valueBinding = 'controller.' + key;
return Ember.Handlebars.helpers.input.apply(this, [options]);
});
and in the template I call:
{{#each modelField in modelFields}}
<span>{{modelField.displayName}}</span>
<span>
{{dataTextField modelField.name}}
</span>
{{/each}}

Using Ember.js, how do I get a template to show dynamically all of the properties of a model? [duplicate]

Is there a way to iterate over a view's context's attributes in EmberJS? I am using Ember-Data (https://github.com/emberjs/data) for ORM.
Lets say I use connectOutlets to register a UserView with a user that has attributes such as email, name, etc. In the connected Handlebars template, is there anyway that I can iterate over those attributes?
I basically need to build a generic view that can be reused with different models...
Ryan is right about the attributes, but it takes some doing to actually get where you're going. My examples here are using the latest RC1 Ember.
Here is an editor template that is model agnostic:
<script type="text/x-handlebars" data-template-name="edit_monster">
{{#if clientId}}
<h1>Edit Monster: {{name}}</h1>
<div>
{{#each metadata}}
<span class="edit-label">{{name}}</span>
<span class="edit-field">
{{view App.AutoTextField typeBinding="type" nameBinding="name" }}
</span>
{{/each}}
</div>
{{else}}
No monster selected.
{{/if}}
</script>
To make that work, we need a couple of pieces of magic-magic. This controller is a good start:
App.EditMonsterController = Em.ObjectController.extend({
metadata: function() {
var vals = [];
var attributeMap = this.get('content.constructor.attributes');
attributeMap.forEach(function(name, value) {
vals.push(value);
});
return vals;
}.property('content')
});
That uses that "attributes" property that Ryan mentioned to provide the metadata that we are feeding into our #each up there in the template!
Now, here is a view that we can use to provide the text input. There's an outer container view that is needed to feed the valueBinding in to the actual textfield.
App.AutoTextField = Ember.ContainerView.extend({
type: null,
name: null,
init: function() {
this._super();
this.createChildView();
},
createChildView: function() {
this.set('currentView', Ember.TextField.create({
valueBinding: 'controller.' + this.get('name'),
type: this.get('type')
}));
}.observes('name', 'type')
});
Here is a fiddle demonstrating the whole crazy thing: http://jsfiddle.net/Malkyne/m4bu6/
The Ember Data objects that represent your models have an attributes property that contains all of the attributes for the given model. This is what Ember Data's toJSON uses to convert your models into Javascript objects.
You can use this attributes property to read a models attributes and then pull those specific attributes out of an instance. Here is an example.
http://jsfiddle.net/BdUyU/1/
Just to reiterate what's going on here. We are reading the attributes from App.User and then pulling the values out of App.ryan and App.steve. Hope this makes sense.

Bind to a value that will be fetched from an API

I have an endpoint that returns a list of artists (json data).
And an endpoint that returns a specific property given an id.
What I would like to do is to iterate through all the artists and display one or more properties in the template to the user but the property should only be fetched from the API if it is bound in the template.
In my ArtistsRoute, I set the model to be all those artists fetched by calling getJSON...
I want, somehow to be able to fetch a property for an artist and display it (through binding).
The Properties map could be stored in the ArtistController maybe.
I could not find a good example for this. Any help is appreciated!
Template example:
Name is on the artist object itself, but the Properties object has been created manually. So in the ArtistController it could be initialized to empty:
Properties = {}
And then it sets Propertes['ShortName'] = to the fetched value.
<ul>
{{#each}}
<li>
{{Name}}
{{Properties.ShortName}}
<img {{bind-attr src=Properties.MainImage}} />
</li>
{{/each}}
</ul>
Should I use a function instead as a property or a Handlebars helper? Like:
{{Property this 'ShortName'}}
where 'this' is the ArtistController and 'ShortName' is the property to fetched. The property id can be calculated through the ArtistController and propertyName.
function(tag, propertyName) {
Ember.$.getJSON('/Properties/' + tag.Id + '_' + propertyName).then(function(response) {
var propertyValueToBind = response.Value; // This is the value I want to display in the template.
});
}
Then the Property function has to know when to rerender the template (once the property has been fetched from the API).
Firstly you'll want to generally avoid properties that are uppercase. They are usually considered global properties, and not in scope.
Ember lazy loads computed properties by default, so adding that functionality to a computed property would be an excellent way to go.
someProperty: function(){
var promise = Ember.$.getJSON('/Properties/buildupurl');
return Ember.ObjectProxy.extend(Ember.PromiseProxyMixin).create({
promise: promise
});
}.property()
Then in your template someProperty would be accessed like this
{{someProperty.value}}
And would be asynchronously populated.
Example: http://emberjs.jsbin.com/OxIDiVU/769/edit

How to give custom Id to an element in handlebars template?

I am using ember and handlebars for template and I have a case where I need to give custom Ids to the element which is created using loops.
Something like:
{{#each item in list}}
<li {{bindAttr id="item.id"+item.name}}>item.text</li>
{{/each}}
i wanted my id to be a sum of strings id and name. e.g. if
id = 3 and name = summary
then it should render as
<li id="3summary">Summary</li>
You can't concatenate in Handlebars like that, instead you'll need to either create a Handlebars helper to do that, or create a computed property in the Ember.Object:
App.MyModel = Ember.Object.extend({
idName: function() {
return '%#%#'.fmt(this.get('id'), this.get('name').toLowerCase());
}.property('id', 'name')
});
Please see the JSFiddle for a working example: http://jsfiddle.net/u4th3/