Ember action : set property of only target of #each - ember.js

I have a few actions that I'm placing on each item in a loop. Currently the action reveals all of the book-covers, instead of just one I want to target.
http://guides.emberjs.com/v2.0.0/templates/actions
Looks like I can pass a parameter, but I'm not sure of the syntax.
I've done this before in earlier version and remember using this or should it be
{{action 'showCover' book}} ... ?
Controller
import Ember from 'ember';
export default Ember.Controller.extend( {
actions: {
showCover(book) { // ?
this.set('coverVisible', true); // or
this.toggleProperty('coverVisible');
},
...
}
});
other thoughts...
actions: {
showCover(book) {
// currently this is just setting the *route in general* to coverVisible:true - which is not what I want
this.set('coverVisible', true);
// I can see this class - the route...
console.log(this);
// I can see the model of this route...
console.log(this.model);
// and I can see the book object...
console.log(book);
// but how do I set just the book object???
// I would expect book.set('property', true) etc.
console.log(book.coverVisible);
console.log(this.coverVisible);
}
}
Template
{{#each model as |book|}}
<li class='book'>
<article>
{{#if book.coverVisible}}
<figure class='image-w book-cover'>
<img src='{{book.cover}}' alt='Cover for {{book.title}}'>
</figure>
{{/if}}
...
{{#if book.cover}}
{{#unless book.coverVisible}}
<div {{action 'showCover'}} class='switch show-cover'>
<span>Show cover</span>
</div>
{{/unless}}
{{/if}}
{{/each}}
ALSO - please suggest a title for this if you can think of a more succinct one.
http://ember-twiddle.com/f44a48607738a0b9af81

#sheriffderek, You have already provided the solution in your question itself. You can pass the additional parameters after the action name. Something like:
<button {{action "showCover" book}}>Show Cover </button>
Working example using ember-twiddle: http://ember-twiddle.com/e7141e41bd5845c7a75c

You should be calling book.set('coverVisible', true); as you are wanting to set the property on the book itself.
actions: {
showCover: function(book) {
book.set('coverVisible', true);
}

Related

ember action 'Nothing handled the action' within block component

If I have component in block form:
//some-component.hbs
{{#some-component}}
<button {{action "someAction"}}>test</button>
<!-- assume you have multiple buttons here with multiple actions -->
{{/some-component}}
//some-component.js
Ember.Component.extend({
actions: {
someAction() {
alert('NOT BEING CALLED');
}
}
});
using Ember > v2.0. The action is not being called.
If I call it:
{{some-component}}
and put:
<button {{action "someAction"}}>test</button>
inside the some-component.hbs template. then it works. but this way has some drawbacks which I want to avoid.
I've looked at the docs and everywhere it doesn't seem to have this sort of case.
the answer is:
{{yield this}}
in the template
and:
{{#some-component as |component|}}
<button {{action "someAction" target=component}}>TEST</button>
{{/some-component}}

Ember Checkbox getting name of element in callback

If I define a checkbox as follows:
{{input type="checkbox" name="email" checked=controller.isEmailChecked}} Email
In the callback controller.isEmailedChecked, defined as:
isEmailChecked: function(key, value) {
...
}
How do I get the value of name ("email")?
My controller is responsible for displaying multiple checkboxes so I do not want to have to write lines like this:
{{input type="checkbox" name="x" checked=controller.isXChecked}} X
{{input type="checkbox" name="y" checked=controller.isYChecked}} Y
Only to be doing:
ixXChecked: function(key, value) {
// Set or Get
this.set('x', value);
this.get('x');
}
ixYChecked: function(key, value) {
// Set or Get
this.set('y', value);
this.get('y');
}
Can someone point me to the docs please.
Edit: Same applies to actions on Buttons:
<button {{action 'toggleSetting'}}>
</button>
How would I get the name of the element in the toggleSetting callback?
Thanks a lot,
Thanks albertjan but this is more simple:
{{view Ember.Checkbox checkedBinding=someModelProperty}}
It updates the someModelProperty when clicked, and it also sets the correct value initially.
You can pass a parameter to a action like this: {{action "post" "test"}} where the second test can also be a variable. {{action "post" mail}}
As for the checkboxes I'd solve that with an item controller:
{{#each thing in things itemController='thing'}}
{{input type="checkbox" checked=onChecked}}
{{/each}}
and your controller would looks something like:
App.ThingController = Ember.Controller.extend({
needs: ['parent'] //where parent is the name of your other controller.
action: {
isChecked: function() {
this.get('contollers.parent')
.send('checkboxChecked', this.get('model.name'));
}
}
});
Or you can add a component, lets call it named-checkbox. And that would look something like this:
Template (app/templates/components/named-checkbox.hbs):
{{input name=name type="checkbox" checked=onChecked}}
{{yield}}
Component (app/components/named-checkbox.js):
import Ember from 'ember';
import layout from '../templates/components/named-checkbox';
export default Ember.Component.extend({
layout: layout,
name: "",
action: {
onChecked: function() {
this.sendAction(this.get('action'), this.get('name'));
}
}
});
Then you can use it like this:
{{named-component name="email" action=isChecked}}
This will result in the isChecked action being called on the controller with the name as it's attribute:
actions: {
isChecked: function(name) {
this.set('name', !this.get('name'))
}
}

Different layouts depending on sub resources

Sorry if this is a really obvious questions but I have the following routes:
Web.Router.map(function () {
this.resource('orders', function(){
this.resource("order", {path:":order_id"});
});
});
And for my orders template I have something like:
<div class="someclass">
{{outlet}}
</div>
And what I want todo is:
{{#if onOrderRoute}}
<div class="someclass">
{{outlet}}
{{else}}
<div class="someotherclass">
{{/if}}
</div>
I was wondering what the best way of doing this is, or am I mising something?
There are multiple ways to accomplish this. The view has a layoutName property you can use to specify your layout. Another option is to specify a property on your child view, and then your template can bind to that by using the view property.
For example:
Web.OrderView = Ember.View.extend({
childView: true
);
Then, in your template you bind to view.childView
{{#if view.childView}}
<!-- code goes here -->
{{/if}}
Further, you can even create a mixin and then just inject that mixin into every view.
Web.ChildViewMixin = Ember.Mixin.create({
childView: true
});
Web.ChildView = Ember.View.extend(ChildViewMixin, {
});

Ember checkbox and value

I have checkboxes created in a loop. I set checked to a property name, and want to reach a dynamic value from the checkbox on click. In the property (for key, value) I'm getting the wrong thing - the label of my property instead of the value. Is there a simple way in Ember to get the value out of the checkbox?
Any help is much appreciated.
In HTML:
{{#each url in controllers.application.env.urls}}
<div>
{{view Ember.Checkbox checked=updateServerList valueBinding="url"}}{{url}}
</div>
{{/each}}
In javascript:
updateServerList:function(key,value)
{
if(value!=undefined)
{
console.log("----1 ", key, value);
}
}.property(''),
Per your comment, I started actually playing around with this and realized that it was not as simple as I thought. What I ended up doing was just building a custom component rather than working with Ember.Checkbox.
In your template:
{{#each}}
{{check-box url=url}}
{{/each}}
Component Template:
<script type='text/x-handlebars' id="components/check-box">
{{input type="checkbox" checked=toggleURL}}{{url}}
</script>
The component code:
App.CheckBoxComponent = Ember.Component.extend({
toggleURL: false,
logURL: function() {
console.log(this.url);
// Do something with the URL
}.observes('toggleURL')
});

getting back reference to a specific model using Ember's Array Controller

I'm new to Ember and am finding some of their concepts a bit opaque. I have a app that manages inventory for a company. There is a screen that lists the entirety of their inventory and allows them to edit each inventory item. The text fields are disabled by default and I want to have an 'edit item' button that will set disabled / true to disabled / false. I have created the following which renders out correctly:
Inv.InventoryitemsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON("/arc/v1/api/inventory_items/" + params.location_id);
}
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" data-id=id}}>edit item</button>
<button {{action "saveInventoryItem" data-id=id}}>save item</button>
</div>
{{/each}}
</script>
So this renders in the UI fine but I am not sure how to access the specific model to change the text input from disabled/true to disabled/false. If I were just doing this as normal jQuery, I would add the id value of that specific model and place an id in the text input so that I could set the textfield. Based upon reading through docs, it seems like I would want a controller - would I want an ArrayController for this model instance or could Ember figure that out on its own?
I'm thinking I want to do something like the following but alerting the id give me undefined:
Inv.InventoryitemsController=Ember.ArrayController.extend({
isEditing: false,
actions: {
editInventoryItem: function(){
var model = this.get('model');
/*
^^^^
should this be a reference to that specific instance of a single model or the list of models provided by the InventoryitemsRoute
*/
alert('you want to edit this:' + model.id); // <-undefined
}
}
});
In the Ember docs, they use a playlist example (here: http://emberjs.com/guides/controllers/representing-multiple-models-with-arraycontroller/) like this:
App.SongsRoute = Ember.Route.extend({
setupController: function(controller, playlist) {
controller.set('model', playlist.get('songs'));
}
});
But this example is a bit confusing (for a couple of reasons) but in this particular case - how would I map their concept of playlist to me trying to edit a single inventory item?
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" this}}>edit item</button>
<button {{action "saveInventoryItem" this}}>save item</button>
</div>
{{/each}}
</script>
and
actions: {
editInventoryItem: function(object){
alert('you want to edit this:' + object.id);
}
}
Is what you need. But let me explain in a bit more detail:
First of all, terminology: Your "model" is the entire object tied to your controller. When you call this.get('model') on an action within an array controller, you will receive the entire model, in this case an array of inventory items.
The {{#each}} handlebars tag iterates through a selected array (by default it uses your entire model as the selected array). While within the {{#each}} block helper, you can reference the specific object you are currently on by saying this. You could also name the iteration object instead of relying on a this declaration by typing {{#each thing in model}}, within which each object would be referenced as thing.
Lastly, your actions are capable of taking inputs. You can declare these inputs simply by giving the variable name after the action name. Above, I demonstrated this with {{action "saveInventoryItem" this}} which will pass this to the action saveInventoryItem. You also need to add an input parameter to that action in order for it to be accepted.
Ok, that's because as you said, you're just starting with Ember. I would probably do this:
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled=headerEnabled}}</p>
<p>{{input type="text" value=detail disabled=detailEnabled}}</p>
<button {{action "editInventoryItem"}}>edit item</button>
<button {{action "saveInventoryItem"}}>save item</button>
</div>
{{/each}}
</script>
with this, you need to define a headerEnabled property in the InventoryitemController(Note that it is singular, not the one that contains all the items), and the same for detailEnabled, and the actions, you can define them also either in the same controller or in the route:
App.InventoryitemController = Ember.ObjectController.extend({
headerEnabled: false,
detailEnabled: false,
actions: {
editInventoryItem: function() {
this.set('headerEnabled', true);
this.set('detailEnabled', true);
}
}
});
that's just an example how you can access the data, in case the same property will enable both text fields, then you only need one, instead of the two that I put . In case the 'each' loop doesn't pick up the right controller, just specify itemController.