I'm brand new to ember.js (started with 3.0) and struggling to achieve what I would expect is a fairly conventional use case without using what seems like unconventional methods or soon to be deprecated features.
The use case in Ember is to display, and paginate, multiple related resources on a parent resources page.
I'm really enjoying using Ember so far but a little exasperated that such a simple use case has been so difficult to overcome. Been ok with the learning curve with everything else so far, but I've hit a wall here.
I have a JSON API backend, with a reasonably complex, (mostly) normalized DB where there are a few key resources and multiple join tables describing the various ways they can relate to each other.
Stripped down setup below:
Models:
// app/models/label.js
export default DS.Model.extend({
name: DS.attr(),
contactInfo: DS.attr(),
releases: DS.hasMany('release'),
artists: DS.hasMany('artist'),
//...
});
// app/models/artist.js
export default DS.Model.extend({
name: DS.attr(),
realname: DS.attr(),
profile: DS.attr(),
labels: DS.hasMany('label'),
//...
});
// app/models/release.js
export default DS.Model.extend({
title: DS.attr(),
released: DS.attr(),
label: DS.belongsTo('label'),
//...
});
Router:
// app/router.js
//...
// labels
this.route('labels', function() {
this.route('label', {path: '/:id'}, function(){
this.route('artists');
this.route('releases');
});
});
// artists
this.route('artists', function() {
this.route('show', {path: '/:id'});
});
// releases
this.route('releases', function() {
this.route('show', {path: '/:id'});
});
// ...
Routes:
// app/routes/labels/label.js
export default Route.extend({
model(params){
return this.store.findRecord('label', params.id)
}
});
// app/routes/labels/label/artists.js
export default Route.extend({
model(){
let label = this.modelFor('labels.label')
return label.query('artists')
},
});
// app/routes/labels/label/releases.js
export default Route.extend({
model(){
let label = this.modelFor('labels.label')
return label.query('releases')
},
)};
Templates:
// app/templates/application.hbs
<h1> Welcome to Ember! </h1>
// app/templates/labels/label.js
<h2></h2>
<h4>Artists</h4>
// insert sensible way to render artists here
<h4>Releases</h4>
// insert sensible way to render releases here
Strategies I've attempted so far:
- Calling a labels related
artists
and releases
directly in the labels/label
template, ignoring the nested routes for relations and passing the promise into a component, or using
This gets the context lazily onto the page, but pagination here was a nightmare. Using the ember-data-has-many-query
extension I managed to cobble together a controller action for the labels.label
route:
// app/controllers/labels/label.js
export default Controller.extend({
queryParams: ['artistsPage'],
artistsPage = 1,
actions:{
pageArtists(direction){
// console.log(direction)
const label = this.currentModel;
if(direction == 'forward'){
let artists = label.query('artists', {page: this.artistsPage+1});
this.set('artistsPage', this.artistsPage+1);
}else if (direction == 'back'){
let artists = label.query('artists', {page: this.artistsPage-1});
this.set('artistsPage', this.artistsPage-1);
}
}
}
});
This is very broken though in that the route knows nothing about the param being set in the action, so on any page refresh they decouple. I abandoned trying to get the labels.label
routes model hook to play nicely with the controller params in lieu of another approach:
- Using nested routes (as shown above) to handle the relations independently of the parent, and load them in individually into the
label
route.
Status Quo:
I'm attempting to use named outlets, but haven't been able to get the context onto the page without hitting a single related resources url, eg /labels/123/artists
or /labels/123/releases
.
So :
// app/routes/labels/label/artists.js
// ...
renderTemplate(){
this.render({
'into':'labels/label',
'outlet':'artists'
})
}
Will get the context into the named outlet in templates/labels/label
, but I can't repeat this for the releases
relation because we are at /labels/123/artists
.
Attempting to set renderTemplate
in the labels.label
route, ie:
this.render('labels/label/artists',{
into: 'label/label',
outlet: 'artists',
controller: 'labels.label.artists'
})
doesn't actually render the route and/or send it to the outlet.
Ultimately I'm hoping to use a pagination approach similar to the one described by Balint Erdi here which implements nested routes and some custom adapter logic to handle the pagination for related resources.
The desired behaviour is for the relationship contexts to be displayed, along with pagination at the url labels/123
.
I've exhausted looking through the guides, API documentation, discourse forum and here, and still at a loss.
What (assuming multiple things) am I doing wrong here?