1

This question may be duplicative, but I haven't quite been able to find the answer.

I have a Rails API serving a URI like this:

/locations/1/visits

In Ember I have a route like this:

Router.map(function() {
  this.route('location', { path: '/locations/:location_id' }, function() {
    this.route('visits' , { path: 'visits' });
 });
});

..and a model like this:

export default DS.Model.extend({
  name:           DS.attr(),
  visits:         DS.hasMany('visit', { nested: true }),
});

When I hit /locations/1/visits in the browser, I expect Ember to likewise hit /locations/1/visits on the backend. Instead, it attempts /locations/1 (without visits).

Any ideas how to convince Ember to properly nest the association?

Thanks!

Ray Bradley
  • 320
  • 2
  • 11

2 Answers2

1

I ended up ditching nested routes in favor of a flat, "simulated" nesting scheme, but borrowed @NullVoxPopuli's (thank you!) adapter technique:

Router.map(function() {
  this.route('visits',   { path: 'locations/:location_id/visits' });
  this.route('visit',    { path: 'locations/:location_id/visits/:id' });
  this.route('checkout', { path: 'locations/:location_id/visits/:id/checkout'} );
});

...and then inside /routes/visits.js:

export default Route.extend({
  model(params) {
    let locationId = params.location_id;
    this.set('locationId', locationId);
    this.store.adapterFor('visit').set('namespace', '/locations/' + locationId);
    let visitsForLocation = this.store.findAll('visit');

    return visitsForLocation;
  }
});

Since it's not important for me to resolve the Location resource, this approach seems to work fine.

Ray Bradley
  • 320
  • 2
  • 11
0

frontend routing isn't the same as backend routing.

The easiest way to think of frontend routing is "nested views" rather than nested resources.

So, as for getting your data (if you don't already have your data), you need a route file at this path:

app/routes/location/visits.js (I think, you can also ember g route location/visits and see what happens)

Then in your route:

import Route from '@ember/routing';

export default class LocationVisitsRoute extends Route {
  // this is the route lifecicle hook that actually gets the data
  // more on that here: https://guides.emberjs.com/release/routing/
  async model(params) {
    // this assumes that your location route loads the location resource
    // similar to how we're trying to load the visits resources is this route.
    let locationId = this.modelFor('location').location.id;

    // you could also get the location id from params,
    // but I don't remember the exact path where the id would live.
    console.log(params);

    // this is kind of hacky, and I wouldn't really recommend it, as there may
    // be concurrency issues. but this technique was taken from here:
    // https://discuss.emberjs.com/t/current-way-to-handle-nested-resources/7477/7
    this.store.adapterFor('visit').set('namespace', `/api/locations/${locationId}`);
    let visitsForLocation = await this.store.findAll('visit');

    return { 
      visits: visitsForLocation,
   };

  }
}

Then in your template for visits, you can do

{{#each model.visits as |visit|}}

  <h1>{{visit.name}}</h1>

{{/each}}

alternatively, if you're using { json:api }, you can just specify the links attribute, and things should "just work": How to use Ember Data with Nested Resources

So, all in all, if you are using { json:api }, configuring your payload to tell the client where resources/relations are is really the best way.

NullVoxPopuli
  • 61,906
  • 73
  • 206
  • 352
  • Thanks, @NullVoxPopuli. You answered one of my questions: how are route files organized. So I created /routes/location/visits.js, but if I'm reading my browser Ember plugin, it's not being called (application router appears to be in effect). Sorry for the rudimentary questions, but the Ember Guides are somewhat silent on these details. Thank you! – Ray Bradley Feb 23 '19 at 21:38
  • I highly recommend you check out the Ember Guides' tutorial: https://guides.emberjs.com/release/tutorial/ember-cli/ It will explain everything :) – NullVoxPopuli Feb 23 '19 at 22:13
  • I tried the Ember Guides; its nested route examples didn't really match my use case. I think I've found a workaround, which I'll post as an answer to my own question. Thanks so much for pitching in :) – Ray Bradley Feb 23 '19 at 23:47
  • what about the guides doesn't match you use case? I think this'd be close: https://guides.emberjs.com/release/routing/defining-your-routes/#toc_dynamic-segments – NullVoxPopuli Feb 24 '19 at 00:03
  • You're right, it's close: the Ember Guide you linked to starts to touch on my use case (particularly the photos/comments example in the _Dynamic Segments_ section). Then it links to Specifying a Route's Model where the photo example continues, but doesn't touch on the nested Comment models. The RSVP Hash bit seems like it might be appropriate, but it switches to Songs and Albums :) – Ray Bradley Feb 25 '19 at 14:43