Ember.js accessing controllers model from another controller - ember.js

there is already a couple of questions asking for similar thing, but none of the answers works for me so far. I have following code, where I want to access model from UsersController in DashboardController:
JP.DashboardController = Ember.ArrayController.extend({
needs: ['users']
});
JP.UsersController = Ember.ArrayController.extend({
model: function(){
return JP.User.find();
},
sortProperties: ['id']
});
My dashboard.template looks like this:
<div class="row-fluid">
<div class="span2">
{{#if controllers.users.isLoaded}}
{{#each user in controllers.users }}
{{user.name}}
{{/each}}
{{else}}
Users not loaded
{{/if}}
</div>
<div class="span10">
{{ outlet }}
</div>
</div>
Why are users never loaded? What is wrong with my code?
Thanks

The model function in your UsersController is the cause of the problem. Always assign a real object (or object array) to the model/content property of your controller. You should do the data fetching in the Route instead of the Controller. I guess you also have a UsersRoute, if you have a UsersController? This could work:
JP.UsersRoute = Ember.Route.extend({
setupController : function(controller){
// the controller param is the instance of UsersController
controller.set("model",JP.User.find());
}
});
JP.UsersController = Ember.ArrayController.extend({
sortProperties: ['id']
});
If you have no UsersRoute and just a DashboardRoute, try this:
JP.DashboardRoute = Ember.Route.extend({
setupController : function(controller){
// the controller param is the instance of DashboardController, so we have to get the UsersController first
this.controller("users").set("model",JP.User.find());
}
});

Related

unable to parse API response in ember

I am trying to learn EmberJS and restify. I have get method API with following response :
{"products":[{"id":1,"name":"lappy1"},{"id":2,"name":"lappy2"}]}
This response I am getting in my browser's network log.
My product route is like :
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return {
products :this.get('store').findAll('product')
};
}
});
My product.hbs is :
<div>
<div class="row">
<div class="col-md-4"><b>id</b></div>
<div class="col-md-4"><b>Name</b></div>
</div>
{{#each model.products as |product|}}
<div class="row">
<div class="col-md-4">{{product.id}}</div>
<div class="col-md-4">{{product.name}}</div>
</div>
{{/each}}
</div>
My product model is :
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string')
});
There is no any error on log, but my page only shows header part i.e
<div class="col-md-4"><b>id</b></div>
<div class="col-md-4"><b>Name</b></div>
which thing I am missing ?
Install Chrome Ember plugin and use Chrome for debugging.
I assume you are using the RESTAdapter?
Your Route's model hook returns an object, not a promise. This is a little odd, I would recommend using a Hash:
model() {
return Ember.RSVP.hash({
products: this.store.findAll('product')
});
}
Alternately, you could just return the result of calling the store:
model() {
return this.store.findAll('product');
}
But in this case, you would change your templates to just use model instead of model.products.

ember - loading model data in route for power sort?

I have a very simple set up right now. I have a book model that has a name and author. I'm trying to create a simple form that will create a new book. For the author I'm using power select to load the authors from the author model. The form set up looks like this:
<form {{action "save" on="submit"}}>
{{input value=model.title placeholder="Title"}}<br>
{{#power-select class="select"
selected=model.author
options=authors
onchange=(action (mut model.author)) as |author|}}
{{author.name}}
{{/power-select}}
<input type="submit" value="Save">
</form>
However I'm having trouble setting up the route to get this working. So far no authors show up in the select, even though there are authors stored in my database. My route looks like this:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.createRecord('book');
},
actions: {
save() {
this.modelFor(this.routeName).save();
}
},
store: Ember.inject.service(),
authors: Ember.computed({
get() {
return this.get('store').findAll('author');
}
}).readOnly()
});
First of all, how should I properly load data from the author model in the route for the books/new route? Secondly, should I be doing this in the route? From what I have read, and what people have told me, loading model data should be done in the route.
Move authors property to corresponding controller.
Also you don't need to add readonly.
So in controller :
authors: Ember.computed(function(){
return this.get('store').findAll('author');
})
And for loading model in route. Yes you should load that model which is to be a resource manipulating, in route. So now you're doing it right.
1) Using Ember.RSVP.hash inside route model hook
your route file-> I assume books/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
newBook : this.store.createRecord('book'),
authors : this.store.findAll('author')
});
},
actions: {
save() {
this.modelFor(this.routeName).newBook.save();
}
}
});
and inside template you can access authors by using model.authors. and title by using model.newBook.title
<form {{action "save" on="submit"}}>
{{input value=model.newBook.title placeholder="Title"}}<br>
{{#power-select class="select"
selected=model.newBook.author
options=model.authors
onchange=(action (mut model.newBook.author)) as |author|}}
{{author.name}}
{{/power-select}}
<input type="submit" value="Save">
</form>
2) Like ebrahim suggested, you can have the below code in required controller,
authors: Ember.computed(function(){
return this.store.findAll('author');
})
3)As author model data is going to be shared data model for authors,books,books.new routes. so you can keep it in service and access it from all the required routes.
authors-service.js -> In service
import Ember from 'ember';
export default Ember.Service.extend({
store:Ember.inject.service(),
authors: undefined,
init(){
this._super(...arguments);
this.get('store').findAll('author', { reload: true }).then(function(results){
this.set('authors',results); //As this is going to be Ember.enumerables, you can iterate and get the data.
});
}
});
You can access authors from authors-service.js in any where by injecting it
authorsService:Ember.inject.service(). I guess in your case, you need to create controller books/new.js for books/new.hbs template,
books/new.js -> controller file
import Ember from 'ember';
export default Ember.Controller.extend({
authorsService:Ember.inject.service(),
authors: Ember.computed.alias('authorsService.authors');
});
Inside books/new.hbs template you can access authors property.

Why doesn't Ember model update?

My goal is to simply update append a number to an array model. This should create a new HTML element due to the iterator I have used in the template.
My action does get called, but the model doesn't really update.
Here is my directory structure:
- app
- routes
- compare.js
- templates
- compare.hbs
- application.hbs
- app.js
- index.html
- router.js
compare.hbs:
<div id="container">
<form method="post" name="login" {{action "submit" on="submit"}}>
<p>
Member ID
</p>
<p> {{input id="member-id" type="text"}} <input type="submit" value="Search"></p>
</form>
<div id="results">
{{#each model as |result|}}
<p>{{ result }}</p>
{{/each}}
</div>
</div>
router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('compare');
});
export default Router;
compare.js
import Ember from 'ember';
let results = [12, 34, 56];
export default Ember.Route.extend({
model: function() {
return results;
},
actions: {
submit: function() {
results.push(123);
this.get('model').push(123);
this.refresh();
}
}
});
What is the problem?
It looks like you have a few issues. You don't need results.push as that is just adding the value to the array outside of Embers knowledge. When adding to the model use pushObject as that should notify ember of the change. There is also no need to call refresh on the model.
The documentation for pushObject shows an example very similar to what you are attempting:
http://emberjs.com/api/classes/Ember.NativeArray.html#method_pushObject
import Ember from 'ember';
let results = [12, 34, 56];
export default Ember.Route.extend({
model: function() {
return results;
},
actions: {
submit: function() {
this.model().pushObject(123);
}
}
});

How do I pass the value of a query param into the form?

My URL looks like http://localhost:4099/checkout/schedule/new?addressId=12 I am trying to pass the query param addressId to the form.
I've tried submitting it as a hidden input, but by the time it hits the save action. I check the Network tab of Ember inspector and this is what it is passing:
{"delivery":{"instructions":"foo","deliver_on":"bar","address_id":null}}
address_id is still null. What am I missing?
Full code below:
// app/pods/checkout/schedule/new/route.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('delivery');
// return this.store.createRecord('delivery', { addressId: this.get('addressId')});
},
// Cleanup the controller, when you leave the new route so the stale new record is also
// removed from the store.
// You can also use https://github.com/dockyard/ember-data-route instead
resetController: function (controller, isExiting) {
var model = controller.get('model');
if (!model.get('isDeleted') && isExiting && model.get('isNew')) {
model.deleteRecord();
} else {
model.rollback();
}
}
});
// app/pods/checkout/schedule/new/controller.js
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['addressId'],
addressId: null,
actions: {
save: function() {
var _this = this;
// this.get('model').set('addressId', this.get('addressId'));
this.get('model').save().then(function(){
_this.transitionToRoute('checkout.address.index');
}, function() {
// Need this promise, so we can render errors, if any, in the form
});
return false;
},
cancel: function() {
return true;
}
}
});
// app/pods/checkout/schedule/new/template.hbs
<form {{action "save" on="submit"}}>
{{addressId}}
{{input type="hidden" value=addressId}}
<p>
<label>Instructions:
{{input value=model.instructions}}
</label>
{{#each error in errors.instructions}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>Deliver on:
{{input value=model.DeliverOn}}
</label>
{{#each error in errors.DeliverOn}}
<br />{{error.message}}
{{/each}}
</p>
<input type="submit" value="Next"/>
<button {{action "cancel"}}>Cancel</button>
</form>
// app/models/delivery.js
import DS from 'ember-data';
export default DS.Model.extend({
address: DS.belongsTo('address', { async: true }),
items: DS.hasMany('item', { async: true }),
instructions: DS.attr('string'),
deliverOn: DS.attr('string')
});
I believe what's happening is that you are not really submitting your form. Instead, you are calling save() on your model, which submits your model data. Therefore, hidden parameter in the form will not help you here.
Your addressId in the URL is tied to your addressId property in the controller, where as the addressId: null you are seeing being submitted in Chrome is the value of addressId property in the model

Link route to single object, without id

I have prepared this jsfiddle. This is the code:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 13,
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function() {
// put your routes here
});
App.User = DS.Model.extend({
name : DS.attr('string'),
email : DS.attr('string'),
});
App.User.FIXTURES = [{ id: 'me', name: 'Max Smith', email: 'max.smith#email.com' }];
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
And this is the template:
<script type="text/x-handlebars" data-template-name="index">
{{#each model}}
<div class="form_labels_wrapper">
<dl class="dl-horizontal">
<dt>Id:</dt> <dd>{{id}}</dd>
<dt>Name:</dt> <dd>{{name}}</dd>
<dt>Email:</dt> <dd>{{email}}</dd>
</dl>
{{/each}}
</div>
</script>
What I want to do is to display the data of a single user. The opbject has no id (it represents the logged in user, which is session related, and has no id visible for ember). Instead, I am forced to create a list of users (with one user), and give it a fake id (me). This makes no sense in my application.
I would like to use this template, but I have no idea how to configure ember for this:
<script type="text/x-handlebars" data-template-name="index">
<div class="form_labels_wrapper">
<dl class="dl-horizontal">
<dt>Name:</dt> <dd>{{name}}</dd>
<dt>Email:</dt> <dd>{{email}}</dd>
</dl>
</div>
</script>
With this fixture:
App.User.FIXTURES = { name: 'Max Smith', email: 'max.smith#email.com' };
Note that this is a single element, and that I am not looping through the models with #each, because this should not be a list: there is only a single element.
Ember refuses to accept this. What can I do to get this to work?
You can returning an array of records from the model hook, as a result ember is generating an ArrayController for it, which expects it's content to be an array.
Change the model hook to return the single record. For instance using me as the id.
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.User.find('me');
}
});
Then your template works. See the updated jsfiddle.