mwp
mwp

Reputation: 8477

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:

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.

Upvotes: 0

Views: 82

Answers (2)

mwp
mwp

Reputation: 8477

Here's my final solution, based off of Deewandra's solution (which I've marked "correct"). The main difference is the use of ic-ajax, which the folks in #emberjs on Freenode strongly recommended for various reasons. I also deleted the html-safe helper and moved the htmlSafe() function call inside the component.

The only thing I'm still confused about is some intermittent warnings I've been seeing about not setting properties from within the didInsertElement() hook. I guess I'll keep an eye on it as Ember.js 2.0 matures and my understanding of the framework continues to improve.

app/components/wp-get-page.js:

import Ember from 'ember';
import ENV from '../config/environment';
import ajax from 'ic-ajax';

export default Ember.Component.extend({
  slug: null,
  isLoadingData: false,
  remoteData: null,

  didInsertElement() {
    const slug = this.get('slug');
    if (Ember.isEmpty(slug))
      throw new Error('slug not specified in wp-get-page call');

    this.set('isLoadingData', true);
    ajax(`${ENV.APP.WP_API_URL}/posts?type[]=page&filter[name]=${slug}`)
      .then(data => {
        if (Ember.isEmpty(data))
          throw new Error('no data from wp-json');

        if (Ember.isArray(data) && data.length === 1)
          data = data.shift();

        this.set('remoteData', {
          title: data.title,
          content: data.content.htmlSafe()
        });
      })
      .catch(() => {
        this.set('remoteData', {
          title: 'Sorry',
          content: 'Unable to retrieve page contents! Please try again later.'
        });
      })
      .finally(() => {
        this.set('isLoadingData', false);
      });
  }
});

Upvotes: 0

Deewendra Shrestha
Deewendra Shrestha

Reputation: 2465

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}}

Upvotes: 1

Related Questions