Search code examples
ember.jsember-cli

Proper place to put this functionality?


Just getting started with Ember.js and already have a stupid question! I would like to encapsulate some functionality (that will be called by several different routes). I'm not quite sure where to put it.

The "procedure" should do the following:

  1. Call out to an external service (currently a WP REST API but might be a database in the future) to get some data.
  2. If the data is an array of JSON objects, extract the first object.
  3. Perform some text formatting on the object, specifically call htmlSafe() on the HTML-formatted content property of the object.

Here's what I've tried so far:

  • A helper that performs all of the functionality. It doesn't seem like you can return a promise from a helper, so this doesn't work.
  • A util (imported and called from the route's model()) that does the data retrieval and a helper that does the data formatting. This works, but seems clumsy?
  • A component, which doesn't seem to have a good place to make the AJAX call.

This is a greenfield app so I'm using canary for a bit of future-proofing. I don't want to use triple-curlies for that reason. Here's my code for the middle (working) option:

app/routes/tos.js:

import Ember from 'ember';
import wpJson from '../utils/wp-json';

export default Ember.Route.extend({
  model() {
    return wpJson('terms-of-use');
  }
});

app/utils/wp-json.js:

/* global Ember */
export default function wpJson(slug) {
  var url = `/wp-json/posts?type[]=page&filter[name]=${slug}`;
  return Ember.$.getJSON(url).then(data => {
    return data.shift();
  });
}

app/templates/tos.hbs:

{{html-safe model.content}}

app/helpers/html-safe.js:

import Ember from 'ember';

export function htmlSafe([html]) {
  return html.htmlSafe();
}

export default Ember.Helper.helper(htmlSafe);

In some kind of a perfect world, app/routes/tos.js would be empty and app/templates/tos.hbs would look more like this:

{{wp-foo-bar slug='terms-of-use'}}

Thanks in advance for any suggestions or feedback.


Solution

  • How about putting all the logic in a component itself, like this:

    //app/components/wp-foo-bar/component.js
    import Ember from 'ember';
    
    export default Ember.Component.extend({
    
      remoteData: null,
    
      loadingData: false,//maybe show spinner depending on this
    
      fetchRemoteData: function(slug){    
          var url = '/wp-json/posts?type[]=page&filter[name]='+slug;
    
          return new Ember.RSVP.Promise(function(resolve, reject){
            Ember.$.getJSON(url, function(data){
                resolve(data.shift());
            });
          });
      },
    
      didInsertElement: function(){
        var slug = this.get('slug');
        this.set('loadingData', true);
        this.fetchRemoteData(slug).then(function(data){
            this.set('remoteData', data);
            this.set('loadingData', false);            
        }.bind(this));
      }
    });
    

    And the template for the component would look like:

    {{html-safe remoteData}}