Accessing model data members in action? Need cast? - ember.js

This must be a simple concept, but it seems to be assumed rather than covered...
I have a simple model, journal, and component, journal-list:
<select class="form-control" id="journal" onchange={{action 'showJournal' value="target.value"}} >
<option value="" disabled="disabled" selected="selected">Periodicals:</option>
{{#each model as |journal|}}
<option value="{{journal}}"> {{journal.name}}</option>
{{/each}}
</select>
and action:
import Ember from 'ember';
export default Ember.Component.extend({
editstate: Ember.inject.service('edit-state'),
actions: {
showJournal(journal) {
this.get('editstate').selectedJournal = journal;
alert('got ' + journal.name);
this.sendAction('displayJournal', journal);
}
}
});
That selected value is not, as far as I can tell, useful, and the journal instance in the action isn't either. Ember knows it's type Journal (e.g. <em-journalapp#model:journal::ember385:3>), but I can't access the members via, e.g., journal.name, journal.get('name') or anything else I've thought of.
I can pass in journal.id instead, and see it. But if I look up the model from the store, the variable again isn't useful.
Is there some magic to casting or storing model data in variables and getting it back out?

I will encourage you to use ember-power-select addon.
When you say <option value="{{journal}}"> this will be rendered in html like <option value="[object object]"> you can't store object for html attribute.
So you need to include the id like this <option value={{journal.id}}> you can get the selected object through findBy method from options list.
Created this twiddle for this.
application.hbs
<select class="form-control" id="journal" onchange={{action 'showJournal' value="target.value"}} >
<option value="" disabled="disabled" selected={{unless editState.selectedJournal.id 'selected'}}>Periodicals:</option>
{{#each model as |journal|}}
<option value={{journal.id}} selected={{if (eq editState.selectedJournal.id journal.id) 'selected'}}> {{journal.name}}</option>
{{/each}}
</select>
application.js route file,
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return [{id:'1',name:'BBC'},{id:'2',name:'TV'}];
}
});
application.js controller file,
import Ember from 'ember';
export default Ember.Controller.extend({
editState: Ember.inject.service(),
appName: 'Ember Twiddle',
actions: {
showJournal(journalId) {
this.set('editState.selectedJournal',this.get('model').findBy('id',journalId));
console.log('Yes ',this.get('editState.selectedJournal'));
}
}
});
As you maintained state is service so I also did it.
edit-state.js service file
import Ember from 'ember';
export default Ember.Service.extend({
selectedJournal:{},
});

Related

Ember JS - Add select box option to URL string via Query Param

I have a Select Box in my form and I would like to be able to use the selection as a Query Param so that I can refresh a model based on its selection. The Select Box is from this ember add-on.
{{#select-box/native value=sb name=module on-select=(action 'selected') class="form-control" as |sb| }}
{{sb.option value='Select a Module:'}} {{sb.option value='Option1'}} {{sb.option value="Option2" }} {{sb.option value="Option3" }} {{sb.option value="option4" }}
{{/select-box/native}}
The 'selected' action simply adds the option to a variable so that I can use it later in a switch statement:
selected(x) {
module = x
},
I'd like to have the selection (or a representation of the selection) in my URL string but I can'tt work out how. I have other inputs building into the URL string but none of them are select boxes.
I have a 'module' item in the QueryParams on my route but it doesn't do anything, I suspect I'll have to do something in the 'selected' action but I'm not sure what.
I haven't used the add-on you mentioned, but here is how you can do it using normal <select>, so just bridge the gap between normal <select> and the add-on you are using in terms of making sure that the status variable in the example below changes depending on what you select in your select box - Ember will do the rest.
Here's a configuration that works if you want to filter a list of users based on the status value you select from a dropdown:
// app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
status: DS.attr('string')
});
// app/templates/users.hbs
<select onchange={{action (mut status) value="target.value"}}>
<option value="" selected={{eq status ''}}>- All users -</option>
<option value="active" selected={{eq status 'active'}}>Active</option>
<option value="inactive" selected={{eq status 'inactive'}}>Inactive</option>
</select>
<ul>
{{#each model as |user|}}
<li>{{user.name}}, {{user.status}}</li>
{{/each}}
</ul>
// app/controllers/users.js
import Controller from '#ember/controller';
export default Controller.extend({
queryParams: ['status'],
status: ''
});
// app/routes/users.js
import Route from '#ember/routing/route';
export default Route.extend({
queryParams: {
status: {
refreshModel: true
}
},
model(params) {
var options = {};
if (params.status) {
options.status = params.status;
}
return this.get('store').query('user', options);
}
});
How does it work?
In the controller you define a property status, which you also indicate to be a query parameter (in the URL). Then in the route, you also define status to be a query parameter which refreshes the model. In the model() hook you extract the parameter and use it for Ember Data Store's query() to fetch the model every time you change the value of status. Your route URL will have ?status=... appended to it, and your server will receive a request similar to example.com/api/users?status=.... Of course, you can configure options in the model() hook differently to format the request URL for the server, but I kept it like this for the sake of simplicity.
The only thing that might be confusing is the template file. Apart from the {{eq status '...'}} syntax, which is a truth helper that simply determines whether the option is selected, the rest of the selecting simply aims to change the status variable (explained in depth here).

Ember-cli filtering model by name, with html select options

Im getting started with emberjs, and I have connected my ember app to an API, and my models working fine.
I can display the model in my template, but how can I filter them?
Example, this is my model:
import DS from 'ember-data';
export default DS.Model.extend({
placeofdamage: DS.attr('string'),
ees: DS.attr(),
type: DS.belongsTo('type'),
brand: DS.belongsTo('brand')
});
How can I display ex. only BMW? With this method:
<select class="form-control" id="selectBrand">
{{#each model.brands as |brand|}}
<option>{{brand.name}}</option>
{{/each}}
</select>
Thank you for any further reply.
There are two main approach. You can ask server to filter them for you or filter it yourself. The later will obviously not scale well for large amount of records.
To filter it yourself you can use addon like ember-composable-helpers it will make your life a bit easier or you can create computed property for it yourself.
To let it filter your api you will have to use query on store.
If you want allow users to toggle this filter you can wire it up yourself or use query-params. I would recommend you to read this one.
My answers is specific to your example. You can install ember-truth-helpers addon simply you can put eq check.
{{#each model.brands as |brand|}}
{{#if (eq brand.name "BMW") }}
<option>{{brand.name}}</option>
{{/if}}
{{/each}}
Or create computed property and filter the brand using filterBy,
onlyBMWBrand: Ember.computed('model.brands', function(){
return this.get('model.brands').filterBy('name','BMW');
})
Could write an is-equal helper by
ember generate helper is-equal
Content for helpers/is-equal.js
import Ember from "ember";
export function checkEquality([leftSide, rightSide]) {
return leftSide === rightSide;
}
export default Ember.Helper.helper(checkEquality);
In template
{{#each cars as |car|}}
{{#if (is-equal car.brand "BMW")}}
<img src={{bmwLogo}} />
{{else if (is-equal car.brand "Volvo")}}
<img src={{volvoLogo}} />
{{else}}
No match.
{{/if}}
{{/each}}
Shouldnt really use this approach when you have hundreds of car records. Let the backend do the job.

ember - combining data for two models into single result for power sort

I'm getting data from two models in one of my routes using RSVP hash, and then trying to combine those results in my controller so that they can be used in a select for power sort. However something doesn't seem to be working.
My route looks like this:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
newBook : this.store.createRecord('book'),
authors : this.store.findAll('author'),
publishing_houses : this.store.findAll('publishing-house')
});
},
setupController(controller, model) {
this._super(...arguments);
Ember.set(controller, 'authors', model.authors);
Ember.set(controller, 'publishing_houses', model.publishing_houses);
},
actions: {
save() {
this.modelFor(this.routeName).get('newBook').save();
}
}
});
My template looks like this:
<form {{action "save" on="submit"}}>
{{input value=model.newBook.title placeholder="Title"}}<br>
{{input value=model.newBook.price placeholder="Price"}}<br>
{{#power-select class="select"
selected=model.newBook.author
options=model.authors
onchange=(action (mut model.newBook.author)) as |author|}}
{{author.name}}
{{/power-select}}
{{#power-select class="select"
selected=model.newBook.publisher
options=model.publishers
onchange=(action (mut model.newBook.publisher)) as |publisher|}}
{{publisher.name}}
{{/power-select}}
<input type="submit" value="Save">
</form>
and my controller, which I think is the problem looks like this:
import Ember from 'ember';
export default Ember.Controller.extend({
publishers: function() {
var authors = this.get("authors");
var publishingHouses = this.get("publishing_houses");
return authors.concat(publishingHouses);
}
});
I'm still figuring out how to use controllers. Am I accessing the model data correctly in the controller? Also is this the proper way to create a property to be used in a template?
In setupController hook, you don't need to explicitly set authors and publishing_houses since it will be set by default through super function call.
In your controller, you can try accessing it like this.get("model.authors") and in the same way for other properties publishing_houses, newBook
To access RSVP return model from route, you should access it directly without get function.
`this.modelFor(this.routeName).newBook.save()
For concatenation and other stuff you refer http://emberjs.com/api/classes/Ember.Enumerable.html
reduce might suite your needs.

Specifying which properties to use in a custom drop-down Ember component

I've created a reusable drop-down component in Ember:
/app/components/dropdown/component.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'select',
classNames: ['form-control'],
placeholder: null,
items: null,
selected: null,
change: function(event) {
var items = this.get('items');
var index = event.target.selectedIndex;
var selected = items ? items[index - 1] : null;
this.sendAction('selectedChanged', selected);
}
});
/app/components/dropdown/template.js
{{#if placeholder}}
<option value="">{{placeholder}}</option>
{{/if}}
{{#each items as | item |}}
<option value="{{item.id}}" selected={{is-equal item selected}}>{{item.name}}</option>
{{/each}}
The component currently uses the 'name' property as the label for the option. However, I want the ability to specify what property to use, in order to make the component more flexible (so that I can sometimes use, for example, 'displayName').
With the old Ember Select component, you could do the following:
{{view "select"
content=programmers
optionValuePath="content.id"
optionLabelPath="content.firstName"
value=currentProgrammer.id
}}
...and tell it which properties to use for both the value and label. I'd like to do something similar, but I'm not sure how. (I tried reading through the source but it was a bit beyond me). Thanks in advance.
<option value="{{ember-get item optionValuePath}}" selected={{is-equal item selected}}>{{ember-get item optionLabelPath}}</option>
You will need to implement the ember-get helper, in Ember 2.0 there is a default heper called get which will make the following redundant.
import Ember from 'ember';
export function emberGet(params/*, hash*/) {
return Ember.get(params[0], params[1]);
}
export default Ember.HTMLBars.makeBoundHelper(emberGet);
Also take a look at this jsbin which has an example of what you are trying to achieve.

replace deprecated Ember.ObjectController used in View in ember.js

I am using Ember.js to create a one page map editing software.
In my app, I use a model to represent the layer's state of the map and to associate it with an actual openlayers' layer.
There is a summary of my work:
in my entry point map.hbs, I call the mapLayers view:
{{view "mapLayers"}}
Here is the mapLayers view definition:
export default Ember.View.extend({
templateName: "mapLayers",
classNames: ["map-layers"]
});
The mapLayers template :
<ul>
{{#each layer in tileLayers itemController="mapLayer"}}
<li {{bind-attr id=layer.identifier}}>
<a>
<label class="hint--top" {{bind-attr data-hint=layer.title}}>
{{str-sub layer.title 20}}
</label>
{{input class="range" type="range" name="range" min="0" max="100" value=layer.opacity}}
</a>
</li>
{{/each}}
</ul>
And the mapLayer controller:
export default Ember.ObjectController.extend({
opacity: function(key, value){
var model = this.get('model');
if (value === undefined) {
// property being used as a getter
console.log("get layer opacity: " + model.get('opacity'));
return model.get('opacity') * 100;
} else {
// property being used as a setter
model.set('opacity', value / 100);
model.get('layer').setOpacity(value / 100);
model.save();
return value;
}
}.property('model.opacity')
});
As you see, I am using the proxy ObjectController to modify on the fly the values set and get in the view.
I'm trying to understand how to remove the ObjectController without success.
I tried to change to Ember.Controller but how can I proxy my model properties then??
I read this without help:
OBJECTCONTROLLER
Experienced Ember users have enjoyed the use of proxying behavior in
the Ember.ObjectController class since 1.0. However, this behavior
will be removed in Ember 2.0 as the framework migrates to routable
components.
New users hit three roadbumps when learning about the object
controller pattern.
Given a certain model, which of the three controller options should I
be using? Which controller is generated by the framework if I do not
specify one? When using an object controller, why should the this
context not be passed to actions if it has the properties of my model?
For these reasons, the Road to Ember 2.0 RFC listed object controllers
as a concept to be removed from the framework.
To migrate from an explicitly defined object controller, first convert
the class definition to inherit from Ember.Controller. For example:
import Ember from "ember";
// Change: export default Ember.ObjectController.extend({ // To:
export default Ember.Controller.extend({
// ...
Next update any use of {{modelPropertyName}} in templates with
{{model.modelPropertyName}}. You should also review any computed
property dependent keys, observer keys, and get and set statements on
the route and controller.
Instead of proxying you just have to fully qualify you are getting the property off of the model, instead of the controller, which is what is in scope in your template and controller.
Template
<ul>
{{#each layer in tileLayers itemController="mapLayer"}}
<li id={{layer.model.identifier}}>
<a>
<label class="hint--top" data-hint={{layer.model.title}}>
{{str-sub layer.model.title 20}}
</label>
{{input class="range" type="range" name="range" min="0" max="100" value=layer.opacity}}
</a>
</li>
{{/each}}
</ul>
Controller
export default Ember.Controller.extend({
opacity: function(key, value){
var model = this.get('model');
if (value === undefined) {
// property being used as a getter
console.log("get layer opacity: " + model.get('opacity'));
return model.get('opacity') * 100;
} else {
// property being used as a setter
model.set('opacity', value / 100);
model.get('layer').setOpacity(value / 100);
model.save();
return value;
}
}.property('model.opacity')
});