Accessing a service's promise from a route

空扰寡人 提交于 2020-01-06 04:00:06

问题


I am struggling to a Service's promised data in a Route. The problem occurs when I am transitioning to the Route at application init; that is, if I load the application, then transition, everything is fine, because the promise is already fulfilled, but if I hit browser reload on that Route, the offending lines won't run. The Service is:

// services/lime-core.js
import Ember from 'ember';

export default Ember.Service.extend({
  store: Ember.inject.service(),
  resources: null,

  init() {
    this.set('resources', []);

    this.get('store').findAll('resource').then(resources => {
      this.set('resources', resources);
    });
  }
});

This service works perfectly in a template, assuming I have injected the service into the component. I access this service in the route as follows: (assume slug has a meaningful value)

// dashboard/route.js
export default Ember.Route.extend({
  limeCore: Ember.service.inject(),
  ...
  model(params) {
    ...
    this.set('resource', this.get('limeCore.resources').findBy('slug', slug));
    ...
  }
}

When the model() hook is called, the limeCore service's init() method is still waiting for the Promise to fulfill. I tried to be clever, but changing the code to something like:

this.get('limeCore.resources').then(resources => {
  this.set('resource', resources.findBy('slug', slug))
});

doesn't work, because this.get('limeCore.resources') does not return a Promise. This logic has to be in the route (i.e. can't be moved to a template), because I'm dependent on the slug value to determine an ID which loads a different set of ember-data.

Again, this code works properly once the Promise has been fulfilled — that is, on a future transition to this route, but not on initial application load.

I'm sure there is a correct way to do this... either the Service needs to return a Promise (while still being usable in templates), or I need to make sure that the Promise is fulfilled before the Route.model() method can be executed.

Thanks!


回答1:


An approach i would use

app/misc/lime_core.js

function getResources(store) {
     return store.findAll('resource')
}

export { getResources };

random route

import { getResources } from 'app/misc/lime_core';

export default Ember.Route.extend({
   model: function() {
     const store = this.get('store');
     const sourcePromise = getResources(store);
   }     
})

But if you're still looking for service approach i would use it like this

export default Ember.Service.extend({
  resources: null,
  store: Ember.inject.service(),


  getResources: function() {
     return this.get('store').findAll('source')
  }
});

route

    limeCore: Ember.inject.service(),

    model: function() {
       const store = this.get('store');
       const sourcePromise = this.get('limeCore').getResources(); // sourcePromise.then(...

    } 

" My route's model() method result depends on the id of the resource"

    model: function() {
       this.get('limeCore').getResources().then(sources => {
          return Ember.RSVP.hash({
               artifact: store.find('artifact', { source: source.get('firstObject.id)})
          })
       })
   }

Or solution 2

     model: function() {
           return Ember.RSVP.hash({
                   artifact: this.get('limeCore').getResources().then(sources => {
                       return store.find('artifact', {source: xx}) 
                  })
              })
           })
       }

Also your getResources function can be modified by your criteria

function getResources(store) {
         return store.findAll('resource').then(r => r.findBy('id', 1))
}



回答2:


I think my question was poorly phrased, so I apologize to the reader for that. This happened because if I'm at the point of asking a question, it's often because I'm having trouble expressing the problem.

The approach suggested above didn't work for me, although it gave a few helpful hints. The significant requirement was that (as I mentioned in the comment) I needed to use the resource.id value in the model query. kristjan's approach addressed that, but my question didn't sufficiently show how complicated the model() method was.

An unwritten second requirement was that the ajax request is only made once, because the data rarely changes and is required in a lot of places on application load.

In the end, I was able to use a blend of kristjan's approach — creating a limeCore.getResource() method that loads the data in a Promise, and then require that promise in my route's beforeModel() hook. The key thing I realized was that beforeModel(), like model(), will wait until a Promise resolves before calling the next hook. In my application, model() should never run until these core objects have been loaded (model() is dependent upon them), so it made sense to have them loaded before. Perhaps there is a more elegant approach (which I'm open to hearing), but at this point I feel the issue has been resolved!

// services/lime-core.js
import Ember from 'ember';

export default Ember.Service.extend({
  store: Ember.inject.service(),
  resources: null,
  clients: null,

  init() {
    this.set('resources', []);
    this.set('clients', []);
  },

  // getCoreObjects() needs to be called at least once before the resources, clients and projects are available in the application. Ideally this method will be called in the route's beforeModel() hook. It cannot be called from the application route's beforeModel() hook because the code will not succeed if the user isn't authenticated.
  getCoreObjects() {
    if (this.get('resources').length === 0 || this.get('clients').length === 0) {
      return Ember.RSVP.hash({
        resources: this.get('store').findAll('resource').then(resources => {
          this.set('resources', resources);
        }),
        clients: this.get('store').findAll('client', {include: 'projects'}).then(clients => {
          this.set('clients', clients);
        })
      });
    } else {
      return Ember.RSVP.hash({});
    }
  }
});

and in my route:

// routes/dashboard.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  limeCore: Ember.inject.service(),
  session: Ember.inject.service(),
  ...
  beforeModel(transition) {
    this._super(...arguments);
    if (this.get('session.isAuthenticated')) {
      return this.get('limeCore').getCoreObjects();
    }
  },

  model(params) {
    ...
    this.set('resource', this.store.peekAll('resource').findBy('slug', slug));
    ...
    return this.store.query('artifact', {'resource-id': this.get('resource.id')});
  }
}


来源:https://stackoverflow.com/questions/37756214/accessing-a-services-promise-from-a-route

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!