EmberJS custom adapter using ember-data-url templates

I am struggling again with adapters in EmberJS. This time it is connected with nested api requests using ember-data-url patterns.

First of all, the corresponding code:

// /app/application/adapter.js import DS from 'ember-data'; var AppAdapter = DS.JSONAPIAdapter.extend({ host: 'http://coursedev.api' }); export default AppAdapter; // /app/router.js import Ember from 'ember'; import config from './config/environment'; const Router = Ember.Router.extend({ location: config.locationType }); Router.map(function() { this.route('courses'); this.route('course', { path: '/course/:course_id' }); this.route('lesson', { path: '/course/:course_id/lesson/:lesson_id' }); }); export default Router; // app/course/model.js import DS from 'ember-data'; export default DS.Model.extend({ order: DS.attr('number'), title: DS.attr('string'), body: DS.attr('string'), lessons: DS.hasMany('lesson') }); // app/lesson/model.js import DS from 'ember-data'; export default DS.Model.extend({ order: DS.attr('number'), title: DS.attr('string'), body: DS.attr('string'), course: DS.belongsTo('course'), questions: DS.hasMany('question') }); // app/lesson/adapter.js import AppAdapter from '../application/adapter'; import UrlTemplates from "ember-data-url-templates"; export default AppAdapter.extend(UrlTemplates, { urlTemplate: '{+host}/courses/{courseId}/lesson/{id}', urlSegments: { host: function () { return this.get('host'); }, courseId: function(type, id, snapshot, query) { return snapshot.belongsTo('course', { id: true }); } } }); // app/lesson/route.js import Ember from 'ember'; export default Ember.Route.extend({ model(params) { return this.store.findRecord('lesson', params.lesson_id ); } }); // app/lesson/template.hbs <h1>{{model.title}}</h1> {{model.body}} <br> This lesson is part of {{#link-to 'course' model.course.id}}{{model.course.title}}{{/link-to}} 

The idea is that the course consists of several questions. Api provides data in a nested manner:

 /courses > all the courses /courses/{courseId} > detailed course info /courses/{courseId}/lesson/{lessonId}/ > detailed lesson info 

This code works fine when navigating "inside" the application, as, for example, without direct access to the route. It then shows the corresponding heading, the body and course of which it is part. But when moving directly to / courses / 1 / lesson / 3, the adapter cannot find the course from the snapshot, since the request points to http: //coursedev.api/courses//lesson/3 . Note the lack of course. This url does not exist as a course must be provided.

My first question is: is there an approach above to handle nested api url? And if so, then how should you put together a course to make the right request for api?

Many thanks!

+5
source share
2 answers

Good, yes. I understand your confusion.

What you have works great when you already have a lesson loaded with a course. When navigating directly to the lesson, you will need to get the course identifier from the URL and somehow pass it to the adapter. Obviously, there is no better solution to this; Here are a few options, in order of preference:

1. Do not use sub-URLs in the API

If you have control over the structure of your API, I recommend that you do not place your resources this way. It might make sense to load the collection subresource in a nested way (e.g. /courses/:course_id/lessons ), but /lessions/:lesson_id preferable to /courses/:course_id/lessons/:lesson_id .

However, you may not have control over your API, which is why ember-data-url templates were created ...

2. Use queryRecord

You can use queryRecord to load a single record and pass in additional information containing only an identifier.

 // adapter export default AppAdapter.extend(UrlTemplates, { queryRecordUrlTemplate: '{+host}/courses/{course_id}/lesson/{lesson_id}', }); // route this.store.queryRecord('lesson', params); 

3. Use the service

Another way to transfer information to your adapter is to use the service. This approach is not suitable for your situation, but I will include it here if it helps another similar problem. I often use the service to store general session information (e.g. user ID). Here, as you could do the same as above, using the session service:

 // adapter export default AppAdapter.extend(UrlTemplates, { session: Ember.inject.service(), findUrlTemplate: '{+host}/courses/{courseId}/lesson/{id}', urlSegments: { courseId() { return this.get('session.courseId'); }, }, }); // somewhere else in your application this.set('session.courseId', params.course_id); // route model hook this.store.findRecord('lesson', params.lesson_id); 

Hope this helps :)

+4
source

Slides: http://www.slideshare.net/RyanMHarrison/nest-v-flat-with-emberdata

Video: https://www.youtube.com/watch?v=DjAMSjFPLW8

Developing attachment options using buildURL and the add-on @amiel ember-data-url-templates .

  • We recommend avoiding the provision of services.
  • Use a snapshot instead of a query (works with other storage methods, including findAll() and findRecord() ). YMMV.
  • Links are an option if you only need read-only access and the API is HATEOAS style.
0
source

Source: https://habr.com/ru/post/1240475/


All Articles