sheriffderek
sheriffderek

Reputation: 9043

Dynamic segments other than id in Ember.js

I'm trying to wrap my head around dynamic segments and I want to be able to use a slug or other property instead of the id. When I can get things working it feels like a fluke. (I'm using ember 2.7+)

I've been looking at the following sources/threads for ideas: https://guides.emberjs.com/v1.10.0/cookbook/ember_data/linking_to_slugs (1.10) http://discuss.emberjs.com/t/slugs-for-replacing-id-path-for-dynamic-segments

I plan on using ember-data, but I want to ensure I'm in control - and so I don't want to use the :post_slug / underscore style that has some built in magic that I want to avoid.

Here is an ember-twiddle Here are step-by-step commits in a github repo



My thought process


1. Conceptually, lets say I need a list of cats - so I need to describe the model for what a 'cat' is.

models/cat.js

import Model from "ember-data/model";
import attr from "ember-data/attr";

export default Model.extend({
  name: attr('string'),
  slug: attr('string')
});


2. define where the dynamic segment will be in the url. I'm going to use catId to prove a point instead of cat_id or :id like most of the tutorials I've seen. For this example, I'm also writing an actual app structure instead of the smallest router possible - to test the edges of this. + what if I needed something like this later: animals.com/cats/:catId/best-friend/:dogId

router.js

Router.map(function() {
  this.route('index', { path: '/' });
  this.route('cats', { path: '/cats' }, function() {
    this.route('index', { path: '/' }); // list of cats
    this.route('cat', { path: '/:catId' }); // cat spotlight
  });
});


3. pull in the catData into the store ~ in the /cats route

routes/cats.js

import Ember from 'ember';

const catData = [
  {
    id: 1,
    name: 'Dolly',
    slug: 'dolly'
  },
  {
    id: 2,
    name: 'kitty cat',
    slug: 'kitty-cat'
  },
  {
    id: 3,
    name: 'Cleopatra',
    slug: 'cleo'
  }
];

export default Ember.Route.extend({
  model() {
    return catData;
    // return this.get('store').findAll('cat'); // with mirage or live api
  }
});


Update from comments:

I do not believe that you can use queryRecord with your test data. Ember data plays dumb with query and queryRecord; it doesn't assume anything about your data and just passes the call on to your server.

~ @xcskier56

So this kinda blows my twiddle as is. The git repo example is Mirage.


4. create the templates... + set up the 'cat' route. The records are in the store... right? so I should be able to 'peek' at them based on id. The docs use params - but -

Ember will extract the value of the dynamic segment from the URL for you - and pass them as a hash to the model hook as the first argument

: ~ and so the params object name isn't special and could really be anything you wanted... and is just replaced with a hash - so to that point / I'm using 'passedInThing' just to assert control over the confusing conventions (many tutorials use param instead of params)

routes/cats/cat.js

model( passedInThing ) {
  return this.store.peekRecord('cat', passedInThing.catId );
} // not going to happen - but in theory...

model( passedInThing ) {
  return this.store.queryRecord('cat', { slug: passedInThing.catId } );
}


5. At this point, I should be able to navigate to the url /cats/2 - and the 2 should get passed through the model hook - to the query. "Go get a 'cat' with an id of 2" --- right??? ((the twiddle example uses a hard-coded set of catData - but in my other attempts I'm using mirage with a combination of fixtures and dynamic slugs: https://github.com/sheriffderek/dynamic-segments-tests/commits/queryRecord


6. Typing in the segment works - but for link-to helpers I need to pass in the explicit cat.id

{{#link-to 'cats.cat' cat.id}}
  <span>{{cat.name}}</span>
{{/link-to}}


7. I can get all that working - but I don't want an ID in the URL. I want cats/cleo with the 'slug' ~ in theory I can just switch catId for catSlug and cat.id to cat.slug etc - but that is not the case. I've seen many tutorials outlining this but they are outdated. I've tried passing in { slug: params.slug } and every combo of find query and peek etc. Sometimes the url will work but the model wont render - or the opposite.


8. This seems like 101 stuff. Can anyone explain this to me? Oh wise emberinos - come to my aid!


UPDATES

Upvotes: 3

Views: 1368

Answers (1)

xcskier56
xcskier56

Reputation: 621

I've struggled with this same issue, and AFIK there is not a way to handle this without querying the server. While this is old and the code is a little out dated the logic still stands. http://discuss.emberjs.com/t/use-slug-for-page-routes-id-for-store-find/6443/12

What I have done is to use store.queryRecord() and then my server is able to return a record fetched via a slug.

This is what the route would look like:

model: function (params) {
  return this.store.queryRecord('cat', {slug: params.catSlug})
}

This will enable you to not expose the ID in the url, but it will issue a query to the store every single time that the model gets hit. There seems to be some discussion of caching query and queryRecord in ember-data, but nothing working yet.

Other helpful resources:

Upvotes: 2

Related Questions