Passing multiple store-data to Ember Component - javascript

I am trying to pass a second model to a component. I have several books that are ordered by categories. The categories are chosen by a Multi-Select-Box and I need to pass the categories (which are stored in my db) to the component. I dont need to change the categories I just need an array. Where and how do I request them and pass them on, if i dont want to inject the store into the component? (There is no way to build a many-to-many relationship, the books only have a string-array for the categories).
Right now I am trying to do this:
books/route.js
model() {
this.store.findAll('book');
}
afterModel() {
this.set('categories', this.store.findAll('category');
}
books/template.hbs
{{#each books as |book|}}
{{book-details book=book categories=categories}}
{{/each}}
components/book-detail/template.hbs
<h2>{{book.title}}</h2>
{{#each categories as |category|}}
<p>{{category.name}}</p>
{{/each}}
It doesn't work for the categories like this, so I need to find a way to get them from the store and pass them on to my component.

If it's not necessary for you to fetch categories afterModel, I recommend considering this approach and dropping your afterModel implementation.
model() {
return {
books: this.store.findAll('book'),
categories: this.store.findAll('category')
}
}
Personally, I've moved towards defining a route's model only if there's a single model associated with the route in question (i.e. /books/thug-kitchen). In the case where I have multiple models that apply for a given route I favor using properties.

A better approach would be to use Ember.RSVP.hash, as follows:
model: function() {
return Ember.RSVP.hash({
books: this.store.findAll('book'),
categories: this.store.findAll('category')
});
},
setupController: function(controller, models) {
controller.setProperties(models);
}
This code will work most correctly if the response from the server is delayed. Tyler's approach works, but the transition to the route happens too soon, before the find is complete. I think his approach works because you're using Ember Data, but it doesn't work if it's a more generic Promise. I wish I could explain better, but my own understanding is still very basic.
After that you shouldn't have any problem passing both models on to the component.

Related

Ember Data - Lazy loading child data of a model without re-creating previously created objects again

I'm new to ember-data. I'm trying to load comment list from a API using multiple API calls. The comment list feature works like below,
A comment object can have a parent comment or children comments (replies)
All comments (children & parent) from different comment threads are list down in a single comment list using a 1st API call.
If user click on specific comment from above list it will prompt respective comment thread. Respective parent or children comments loading using 2nd API call
The comment model is implemented as below,
export default CommentModel.extend( {
parent: computed(function() {
return get(this, 'store').queryRecord('comment', {
_overrideURL: `comments/${get(this, 'id')}/parent`,
});
}),
children: computed(function() {
return get(this, 'store').query('comment', {
_overrideURL: `comments/${get(this, 'id')}/children`,
});
}),
...
As this implementation, if user click on child comment (reply) from the comment list, the 2nd API call with load the respective parent comment and parent comment will load its children comments again. That behaviour cause reload the comment list component in UI.
Is there any other way in ember-data to lazy load relationship without creating already existing objects?
If you really need to go that road, you may try to perform a findRecord instead of a queryRecord and use adapterOptions to customize your model's adapter urlForFindRecord method.
TL;DR
Why you shouldn't:
IMHO, you have a data flow problem in your proposed design.
You shouldn't be performing async code inside a computed property (nor returning immutable object as queryRecord response).
Tasks work great for that purpose.
You shouldn't be having your model to load data (that should be route's responsibility), which violates both MVC and DDAU principles.
There is this great article from 2015 on that
As a matter of fact since ember octane, you shouldn't be using computed properties at all, they have been replaced by actual getters and tracked properties.
More on that
Ember is a great framework, good luck on your journey!

then() returning a null value when it should be pending

Ember 2.17
I am calling an helper from my template :
{{#each invoice.invoiceLines as |line| }}
{{pricings/full-pricing line.pricing}}
{{/each}}
invoice,invoiceLine, as well as pricing are ember models.
Here is how invoice is created in model () :
model(params) {
let invoice= this.store.findRecord('invoice',params.invoice_id)
return Ember.RSVP.hash({
invoice: invoice,
allShares: invoice.then((i)=>{return i.allShares()}),
detailShares: invoice.then((i)=>{return i.detailShares()})
});
}
The goal of the helper is to take pricing, extract numbers (everything is in the model, no more relations) and return a string formatting the initial price and the subscription price.
The helper is as following :
import { helper } from '#ember/component/helper';
export function pricingsFullPricing([pricing]) {
return pricing.then(
p=>{
debugger
},p=>{
}
)
}
export default helper(pricingsFullPricing);
When I run the page, debugger is called twice (the template loop run once).
First time p is null, the second time it is a pricing.
Isn't then supposed to prevent that? Why does it behave like that?
Your route is wrong, routes are promise aware (that's what hash is for), it should be:
model(params) {
return Ember.RSVP.hash({
invoice: this.store.findRecord('invoice',params.invoice_id)
//allShares: invoice.then((i)=>{return i.allShares()}),
//detailShares: invoice.then((i)=>{return i.detailShares()})
});
}
Then your handlebars is just:
{{#each model.invoice.invoiceLines as |line| }}
{{line}}
{{/each}}
You also shouldn't call methods like you are on a model. It's not really clear what allShares(), etc does but these should (probably) be computed in the controller. Something along the lines of:
import { computed } from '#ember/object';
export default Controller.extend({
allShares:computed('model.invoice', function(){
return this.get('model.invoice').allShares();
});
});
Though this doesn't seem ideal. Like I said, it's hard to be explicit as it's not clear what your trying to do here. It'd probably make more sense if your extracted these methods into a service.
You then don't need the helper at all. This appears to be just trying to work around promises.
It makes life a lot easier if you try and load all server side data in the route before load.
First rule of helpers
Each time the input to a helper changes, the compute function will be called again.
Second, there's nothing about helpers that will make this block subsequent calls because you are returning a promise.
export function pricingsFullPricing([pricing]) {
return pricing.then(
p=>{
debugger
},p=>{
}
)
}
You've created a simple helper here that will use the promise itself as the value. Look at ember-promise-helpers/await to see how a class based helper is used to manually set the value that's displayed in the template.
Now, if you're wondering why the recomputation is happening, I'm going to have to speculate based off the knowledge I have of Ember data just from being part of the Ember community (I've never actually used Ember Data). You know line.pricing is a promise? I can then assume your using some sort of relationship, which will most likely have to be loaded via an ajax call (hence the promise). But these relationships in Ember data, iirc, use this PromiseProxyMixin that allow them to behave simultaneously like a promise or like an object (depending on whether the data is in the store already or not). This is what allows you to reference the promise in your template without then
See this article for a better understanding of what I mean

How I do a search with POST in Ember 2 and Ember Data

I'm make a search for a application in Ember 2 which my backend only accept a POST for this search, so Im trying send data through customize createRecord, but the behavior is completely different from I'm expected, two points I believe be a problem.
After several console.log(), I see my actions don't work, even action setted in route.
Inside Ember Inspector the route for this search haven't a model related
Anyone have a hint about why my route don't have a model related, follow the model declaration for this specific route.
model() {
return {
data: this.store.findAll('booking'),
booking: {}
};
}
PS: I edited the title, to be more clear about I need.
I believe you need to use Ember.RSVP.hash for that:
model() {
return Ember.RSVP.hash({
data: this.store.findAll('booking'),
booking: {}
});
}

Ember 'needs' property doesn't work unless you visit the needed controllers route first

I've come across something that is either an issue with Ember's 'needs' controller property or I don't understand the proper way to achieve my goal.
The goal is to be able to have access to one or more controller's content from another controller.
So for example I have a route All Accounts that will need to have access to the contents of Bank Accounts and Credit Accounts so that it may display all accounts :)
The problem is the content is alway empty for those controllers unless you visit the bank and credit account routes first!
Here's a jsbin illustrating the problem:
http://jsbin.com/yubul/1/edit?html,js,output
A controller only has it's model automatically populated when you visit the route needing it. Controllers can exist without models. needs should generally only happen upstream, not to sibling resources/routes.
If a resource depends on another, then it should be part of your nesting structure, or fetched at the same time.
this.resource('accounts', function(){
this.resource('bank-accounts');
.....
});
Generally in the use case where you don't necessarily want a nested route, but you do want multiple resources I return multiple resources from the single route, or setup multiple controllers in the setupController hook.
Model with multiple models (properties)
App.FooRoute = Em.Route.extend({
model: function(){
return Em.RSVP.hash({
cars: this.store.find('car'),
dogs: this.store.find('dog')
});
}
});
Using the above technique your controller would be an ObjectController with two properties, cars and dogs each of which would be a collection.
setupController set up multiple models
App.FooRoute = Em.Route.extend({
model: function(){
return this.store.find('car');
},
setupController: function(controller, model){
// this._super does the default implementation of setupController
this._super(controller, model);
this.controllerFor('dogs').set('model', this.store.find('dog'));
}
});
Or you can do something in-between, mixing and matching.

How to fetch hasMany relationship data array inside controller?

My model relation is defined as follows
App.Person = DS.Model.extend({
name: DS.attr(),
imageUrl: DS.attr(),
bills: DS.hasMany("bill", { async: true}),
});
App.Bill = DS.Model.extend({
description: DS.attr(),
amount: DS.attr(),
person: DS.belongsTo("person")
});
How to fetch bills of all persons inside controller and pass it to controller so that i can display all bills?
One solution is to iterate inside the handlebars template.
But i need to fetch the data in the controller itself and use them as property in template?
App.DashboardController = Ember.ArrayController.extend({
bills: function(){
return /*Bills here*/;
}.property()
});
If I understand you correctly, and the App.Person instance is the model of your DashboardRotue, then I'm not sure what you want is possible with what you have currently.
As a little background info, controllers in Ember.js decorate a model. In layman's terms, it means that all properties come from the controller, even if the controller has to ask the model for them. So if you asked your controller for the bills property, it would see that it didn't have one, and get the one from the model instead. So if all you wanted was the bills for that one person, you wouldn't need to declare anything in your controller at all. You could just do this in your template and it would work fine:
<ul>
{{#each bills}}
<li>{{amount}} - {{description}}</li>
{{/each}}
</ul>
But if I understand you correctly, you seem to wants bills for all people. That is a little trickier, but still possible. If you wanted all of the bills in the whole system (which I think would be the same), the easiest way would be to fetch them all and use them as the model in an ArrayController.
model: function() {
return this.store.find('bill'); // Find all bills
}
You template would then look like this:
<ul>
{{#each}}
<li>{{amount}} - {{description}}</li>
{{/each}}
</ul>
But if you wanted to get the bills for all of the people, you have your work cut out for you. You would need do the same as above, only get all of the persons instead of the bills. You would then need a computed property in your controller to join all of the bills for all of the people. But instead you can't nest #each observers, your property wouldn't update properly. If this is really your use case, I would strong suggest rethinking it. If you really need it, I can help you, but again, it won't be easy.

Categories

Resources