create x number of components within a component in ember 2.8 - ember.js

So I have two components in my Ember app, game-instance and game-card. The idea is that I will pass a 'cards' number attribute to game-instance and it will create x instances of game-card. Here's the setup
game.hbs (the main file):
<div>
{{input type="number" min="2" max="24" placeholder="enter a number from 4 to 24"
value=inputValue
key-up=handleInputChange}}
{{game-instance cards=inputValue}}
</div>
game-instance.hbs
<div>
<!-- iteratively create game cards based on cards attribute passed -->
</div>
game-card.hbs
<div class="gamecard">
<img src="" />
</div>
When trying to figure out how to write up the game-instance.hbs logic, I've seen how handlebars allows you to iterate over an array. This is not really what I want. What I want is this behaviour:
game-instance reads its cards attribute passed from game.hbs. In this case let's assume 6
a type of for-loop logic creates six instances of game-card within game-instance.
Is there an appropriate handlebars syntax to achieve this? If not, how can I achieve similar behaviour? Thanks very much.

Idea is you need to use component helper along with #each block with cards array. so that whenever we change cards array,it will re-render the whole block.
After user entering input,you can create/update cardsArray. that will automatically rerender game-card component in game-instance component
game-instance.hbs
{{#each cardsArray as |value,index|}}
{{component 'game-card' value }}
{{/each}}
Whenever we change cardsArray using pushObject or chagning the entire reference then then each block will be rendered.

Related

Setting query param value based on input field value in ember 2.8

So I have a home.hbs template that looks like so:
<div>
{{input type="number" min="2" max="24"}}
{{#link-to 'game' (query-params cards="" class="button"}}
Let's Play!
{{/link-to}}
</div>
And I have a (not yet implemented) game route that will use a component to build up a game instance (which i pass with the 'cards' attribute). The thing is, the game route needs the value of the input field in this template to build this instance. Can i just do something like?:
{{#link-to 'game' (query-params cards={this.input.value} class="button"}}
Or is there a more efficient way to pass that number to the /game route? Thanks.
Definitely, you can add property to route controller (inputValue for example) and bound it to input value. And then use it in link-to helper.
Something like below (didn't check it in real app):
{{input type="number" min="2" max="24" value=inputValue}}
{{#link-to 'game' (query-params cards=inputValue class="button"}}

How to store EmberJS input value in object or array on the controller

I have a few input fields being displayed programatically via the Ember each helper. These inputs are related to data being returned from my database, and I have a corresponding unique ID for each input that I could use if necessary.
My question is how can I store the value of these dynamically generated inputs on my controller so that I can access the user's input data? I was trying to do something like this:
{{#each solutionTypes as |solutionType|}}
{{input value=inputData[solutionType.id]}}
{{/each}}
However, trying to access an object or array in this manner causes a build error related to the the above syntax in specifying the value (object dot notation causes a build error too).
In short, I am trying to save the value of the input field as a property on an object or in an array instead of as a plain variable on the controller. I would like the input data from all of the inputs in the form to be accessible from the "inputData" variable in the following form:
{
"1000": "data from first input",
"1001": "data from second input",
"1002": "data from third input"
}
The primary issue is utilizing the dynamic keys (from solutionType.id) in the handlebars code without getting a build error.
If this is not possible using the value attribute but you know how to accomplish this with actions or with something else, I'm more than open to your ideas.
The question is a tad confusing so I'll answer in both ways I interpreted your question.
Two-way binding
The {{input}} helper establishes a two way binding with the value so in your example:
{{input value=solutionType.value}}
will bind the solutionType.value to the input. Not only will it display that value but it means as the user types into the input it will update solutionType.value.
One-way bindings (Data Down Actions Up)
Based on your use of inputData being different then solutionType I assume you want a one way binding.
The community standard is to use Data Down Actions Up in such that the solutionType.value does not change as the user enters data but instead sends an action back up so you can manage it as you see fit.
Unfortunately the current Ember {{input}} helper does not support this. There is an addon called ember-one-way-controls which will do this for you. You might want to experiment with that.
A caveat with the above addon is that you will have to manage the solutionTypes data manually as the actions come back up.
Ultimately you will have to decide just how tightly coupled the data you display via an input field is to the data you expect the user to type and adjust your design accordingly.
Yes. You can utilize the dynamic keys (from solutionType.id) in the handlebars code without getting a build error by using get and mut helper it's possible. ember-twiddle
For two way binding,
{{input value=(mut (get inputData (get solutionType 'id'))) }}
For one way binding,
{{input value=(get inputData (get solutionType 'id')) }}
routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return [{id:'1000'},{id:'1001'},{id:'1002'}];
},
setupController(controller,model){
this._super(...arguments);
controller.set('solutionTypes',model);
}
});
controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
inputData:{'1000': "data from first input", '1001': "data from second input",'1002': "data from third input"},
});
templates/application.hbs
<h1>Welcome to {{appName}}</h1>
<br>
<h1> One way binding </h1>
{{#each solutionTypes as |solutionType|}}
{{input value=(get inputData (get solutionType 'id')) }}
{{/each}}
<h2> Two way binding </h2>
{{#each solutionTypes as |solutionType|}}
{{input value=(mut (get inputData (get solutionType 'id'))) }}
{{/each}}
<br>
<h2> Result </h2>
{{#each solutionTypes as |solutionType|}}
<span> {{get inputData (get solutionType 'id')}} </span>
{{/each}}
<br />
{{outlet}}
<br>
<br>

How to use a component from within a component in Ember

G'day all,
I'm trying to wrap a component with another to provide a simplified editing wrapper.
The component is to conditionally show either a label or a select component that allows the user to pick the right value.
I want to wrap the power-select component, and pass it's values through to the sub-component, so the page template component reference looks like this:
{{cm-select
title="Country ##"
options=countries
selected=selectedCountry
searchField="name"
action="selectCountry"
}}
"countries" is an array of country objects, and selectedCountry is one of those country objects.
The component template has the following:
<td>{{title}}</td>
<td>
{{#if edit}}
{{#power-select
options=options
selected=selected
searchField=searchField
as |object|}}
{{object.name}}
{{/power-select}}
{{else}}
<small>{{modelElement}}</small>
{{/if}}
</td>
Unfortunately the power-select component renders with an empty list of options.
I thought wrapping those parameters in handlebars might do the trick, but it seems that handlebars in handlebars isn't a valid syntax.
Does anyone have any ideas?
Thanks,
Andy
That should work, I created a twiddle for you, demonstrating your use case. You'll see I updated the your cm-select template to this:
{{title}} |
<button {{action 'toggleEdit'}}>Toggle Edit</button>
<br/>
{{#if edit}}
Search for a Item via {{searchField}}
<br/>
{{power-select
options=options
selected=selected
searchField=searchField
onSelect=(action "itemSelected")
}}
{{else}}
{{search-list
options=options
searchField=searchField
onSelect=(action "itemSelected")
}}
{{/if}}
Where you iterated over options for power-select in the cm-select component, I moved that down into the power-select template. It's better to try and encapsulate some functionality there, instead of having everything in cm-select. I wasn't sure what you had in mind with {{modelElement}} so I just demonstrate what it would look like, using two different components in cm-select.

Binding values of all element content values in Ember

I have this an ArrayController with some elements in it. I display a property "content" of the elements in a list with Handlebars like this:
{{#each}}
<div id="editable" contenteditable="true">
<li>{{content}}</li>
</div>
{{/each}}
As noted in the template, I also make this editable (I have integrated the inline CKEditor) and I can edit these list items when I load the app.
The problem is that the changed data is not reflected back to the element object of the ArrayController, so that after I call save on the model, the "content" property is back to its original value.
If I modify the data by the ember textfield view, everything works fine, so changes are stored back into the element.
{{#each}}
<div id="editable" contenteditable="true">
<li>{{textarea value=content}}</li>
</div>
{{/each}}
Is there a way to tell handlebars that all element values should be two-way bound to the properties?
You need to bind value to the content.
This should solve your problem
<textarea {{bind-attr value=content}} />
// or
{{view Ember.TextArea value=content}}
The CKEditor must create a 'textarea' when you add 'contenteditable="true"'
This textarea is not bound to Ember controller.
So you can do :
1) get the value from CKeditor in the 'save' action.
or
2) Change CKeditor to make it Ember compatible.

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.