Add local controller property to store models - ember.js

I want to add a simple 'selected' property to store objects, just localised to the particular route/controller.
So in my controller I'm loading the 'groups' from the store. For each 'group' I want to add in a 'selected' property. Tried a few different approaches but just can't get it working.
Advice on "the ember way" would be much appreciated.

If what you're trying to do is display something different for each group that isSelected. I'd take the following approach:
I'd create an array on your controller of selectedGroups, which can either be an array of group objects from the store or just simply an array of ids. As a group is selected/unselected, it can be added to/removed from the array. You can then use a computed function or pass selectedGroups into components to do whatever you need with selectedGroups.
So, if you had your groups template like so:
{{#each model as |group|}}
{{my-item-component item=group selectedItems=selectedGroups}}
{{/each}}
Then in your my-item-component.js, you would have a computed function like:
isSelected: computed('item', 'selectedItems.[]', function () {
// This assumes selectedItems contains item/group objects rather
// than ids. You will need to tweak a little for ids.
return (this.get('selectedItems').indexOf(this.get('item')) !== -1);
})

Related

Get the value of another select control in Ember

My situation is I have a table where the user can add suppliers, and edit any existing ones (so there are potentially multiple records). Three of the fields (Type, Name, Version) come from a lookup object returned by the API (which is one table in the backend database).
Before clicking 'edit'
In edit mode
The thing is, I need these select elements to be "chained" (or cascading), but since they're populated from the same object, it's more like the selection of "Type" will filter the options available for "Name" and likewise selecting Name will further restrict the options available for Version.
However, since this is just one record being edited, these selects are in an {{#each supplier in suppliers}} block to generate the rows, and show selects if that record's isEditing property is true, so the value or selection is the per-record value, e.g. supplier.type and not a single property on the whole controller.
I've tried to come up with multiple ways to do this, but so far haven't found a solution to cascading dropdowns with multiple records since that means the value of any one select is dependent on the record.
I think I could get the option filtering to work if I knew how to reference the value of say the Type dropdown from within the controller, but then again it's conceivable that two records could be in edit mode at once, so modifying any property on the controller to populate the selects would affect the others too, and that's not good. I just really wanted to figure this out so I didn't have to pop up a modal dialog to edit the record.
You should use components to handle each row seperately.
Let's say that you have something like this:
{{#each suppliers as |supplier|}}
// .. a lot of if's, selects and others
{{/each}}
If you find yourself using {{#each}} helper and your block passed to that helper is more than one line, than it's a good sign you probably need a component there.
If you create a component named, let's say, SupplierRow you could make it as follow:
module export Ember.Component({
editing: Ember.computed.alias('model.isEditing'),
types: Ember.computed('passedTypes', function() {
// .. return types array for that exact supplier
}),
names: Ember.computed('passedNames', 'model.type', function() {
// .. return names array for that exact supplier based on possibleNames and model.type
}),
versions: Ember.computed('passedVersions', 'model.type', 'model.name', function() {
// .. return versions array for that exact supplier based on possibleVersions and model.type and model.name
}),
actions: {
saveClicked() {
this.sendAction('save', this.get('model'));
}
}
});
The template would basically look similiary to what you have currently in your {{#each}} helper. It would be rendered something like this:
{{#each suppliers as |supplier|}}
{{supplier-row model=supplier possibleTypes=types possibleNames=names possibleVersions=versions save="save"}}
{{/each}}
Seems like you are using an old version of Ember wich allows context switching in {{#each}} helpers. Assuming that, you can set itemController for each iteration and handle selectable values for each row separately:
{{#each suppliers itemController="supplierController"}}
// this == supplierController, this.model == supplier
{{/each}}
So inside the supplierController you can calculate select content for each single supplier. You can also access main controller from item controller by this.parentController property.

emberjs add code on the fly, after template render

In emberjs, you can add code to your template file:
{{input type="text" value=color}}
the template then renders. But the question is, how can you add this dynamically after the template renders? For example, I want to add a button, to generate new input fields (colors), and the user can continue to add new colors as needed? How would one go about doing this?
First of all, if you want to let user add another input for color, I am sure that you want to somehow access the value user inputs after all - e.g. in some action. Therefore, you will have to make some bindings that will store that values.
Let's say you need to store them in some kind of an array - e.g. colors. This array will be initially containing only one object, automatically added when user enters the route. This setup (e.g. in setupController hook in route) may look like this:
setupController: function(controller, model) {
controller.set("colors", []);
controller.get("colors").pushObject({ value: "" });
}
And let's handle the click on the button by an action in controller:
actions: {
handleClick: function() {
this.get("colors").pushObject({ value: "" });
}
}
Then, your template can look like this:
{{#each color in colors}}
{{input type="text" value=color.value}}
{{/each}}
Using pushObject method make pushing binding-compliant. Every time you push anything to colors array, the template will automatically rerender and inject another input field with properly binded value to color.value. Thanks to that, in some other action (like submit) you can access all the values provided by the user and process them as you want.

Handlebars looping over an Ember component simple property

In an ember component how can I generate 6 things in the template, given a component property called num with value 6?
Do I have to create an array just for the purposes of this in the component? If so can someone advise the most reusable way to do this?
I think I would need to do this via a helper:
Ember.Handlebars.helper('highlight', function(value, options) {
var escaped = Handlebars.Utils.escapeExpression(value);
return new Ember.Handlebars.SafeString('<span class="highlight">' + escaped + '</span>');
});
So in my experience, this works well:
{{#each row in rows}}
//do something
{{/each}}
However, as you say, you'll probably need an array populated. I've used this with an array of Ember models to populate a table, but I'm sure you'll be able bend it to your purpose!

How to use itemControllerClass to achieve a singleton like experience with ember.js

I've got a fairly simple form but it should never carry any state with it. I started reading an older discussion about how you can use itemControllerClass to get a "singleton" class created each time you enter the route.
If I want to use this how would I plug this into the template and wire it up from the parent controller?
Here is what I'm guessing you would do from the javascript side
App.FooController = Ember.Controller.extend({
itemControllerClass: 'someclass_name'
});
The only "must have" is that I need to have access to the parent route params from the child singleton controller.
Any guidance would be excellent -thank you in advance!
Update
Just to be clear about my use case -this is not an ArrayController. I actually just have a Controller (as shown above). I don't need to proxy a model or Array of models. I'm looking for a way to point at a url (with a few params) and generate a new instance when the route is loaded (or the parent controller is shown).
I'm doing this because the template is a simple "blank form" that doesn't and shouldn't carry state with it. when the user saves the form and I transition to the index route everything that happened in that form can die with it (as I've cached the data in ember-data or finished my $.ajax / etc)
Anyone know how to accomplish this stateless behavior with a controller?
I'm betting you're talking about this discussion. It's one of my personal favorite discoveries related to Ember. The outcome of it was the itemController property of an ArrayController; I use it all the time. The basic gist of it is, when you're iterating over an array controller, you can change the backing controller within the loop. So, each iterating of the loop, it will provide a new controller of the type you specify. You can specify the itemController as either a property on the ArrayController, or as an option on the {{#each}} handlebars helper. So, you could do it like this:
App.FooController = Ember.ArrayController.extend({
itemController: 'someclass'
});
App.SomeclassController = Ember.ObjectController.extend({});
Or, like this:
{{#each something in controller itemController='someclass'}}
...
{{/each}}
Within the itemController, you can access the parent controller (FooController, in this case), like:
this.get('parentController');
Or, you can specify the dependency using needs, like you ordinarily would in a controller. So, as long as the params are available to the parentController, you should be able to access them on the child controller as well.
Update
After hearing more about the use case, where a controller's state needs to reset every time a transition happens to a particular route, It sounds like the right approach is to have a backing model for the controller. Then, you can create a new instance of the model on one of the route's hooks; likely either model or setupController.
From http://emberjs.com/api/classes/Ember.ArrayController.html
Sometimes you want to display computed properties within the body of an #each helper that depend on the underlying items in content, but are not present on those items. To do this, set itemController to the name of a controller (probably an ObjectController) that will wrap each individual item.
For example:
{{#each post in controller}}
<li>{{title}} ({{titleLength}} characters)</li>
{{/each}}
App.PostsController = Ember.ArrayController.extend({
itemController: 'post'
});
App.PostController = Ember.ObjectController.extend({
// the `title` property will be proxied to the underlying post.
titleLength: function() {
return this.get('title').length;
}.property('title')
});
In some cases it is helpful to return a different itemController depending on the particular item. Subclasses can do this by overriding lookupItemController.
For example:
App.MyArrayController = Ember.ArrayController.extend({
lookupItemController: function( object ) {
if (object.get('isSpecial')) {
return "special"; // use App.SpecialController
} else {
return "regular"; // use App.RegularController
}
}
});
The itemController instances will have a parentController property set to either the the parentController property of the ArrayController or to the ArrayController instance itself.

Pass array to linkTo helper

I want to create a component which needs to generate dynamics links. I tried passing the link data as an array, but this does not work.
var user1 = get("store").find("user", 1);
var data = {link: ["users.show", user1], title: "User1"};
{{#link-to data.link}}{{data.title}}{{/link-to}}
This should be equal to
{{#link-to "users.show" 1}}{{data.title}}{{/link-to}}
How to generate fully dynamic links from a variable?
You can specify an array as params argument into a link-to helper. Similar to nickiaconis' answer answer, but with just the default {{link-to}} helper:
{{#link-to params=data.link}}{{data.title}}{{/link-to}}
...will render something like:
User1
(tested with Ember 2.3.0)
Ember 1.13.x
The LinkComponent, which is what the link-to helper creates for you, is exposed as -link-to. I've created an example of its use here: http://emberjs.jsbin.com/rinukefuqe/2/edit?html,js,output
{{#-link-to params=(unbound link) hasBlock="true"}}
{{title}}
{{/-link-to}}
The params attribute is what the link-to helper normally bundles your positional parameters onto, although you must use the unbound helper here because the LinkComponent expects params to be an array rather than a value binding object. Additionally, the determination of use as block or inline component is not built into components yet, so you must pass hasBlock="true" unless you include the link text as the first parameter in your array.
Ember ≤ 1.12.x
Although it is not done already, you can manually expose the LinkView component, which is the equivalent of the new LinkComponent.
App.XLinkToComponent = Ember.LinkView.extend();
Then use it like:
{{#x-link-to params=link}}
{{title}}
{{/x-link-to}}
Using unbound and hasBlock="true" are not necessary as the internal logic of LinkView differs from LinkComponent.
I think that isn't possible to pass an array, but you can pass each argument directlly, like the following:
route
var user1 = this.store.find('user', 1);
var data = { data: { link: "users.show", model: user1, title: "User1" } };
return data;
template
{{#link-to data.link data.model.id}}{{data.title}}{{/link-to}}
I hope it helps