Quantcast
Channel: Ember.JS - Latest topics
Viewing all articles
Browse latest Browse all 4831

Issue with nested promises for a group-by helper

$
0
0

@scott.werner.vt wrote:

I am creating a group-by helper (addon when ready) which supports nested async paths (relationships). The helper works for an array with nested objects but for some reason it is not displaying correctly for nested model relationships. I believe the issue is related to promises resolving and the helper recomputing/updating.

I created an ember-twiddle to demonstrate the issue.

group-by helper:

/* Reference
 * https://gist.github.com/Asherlc/cc438c9dc13912618b8b
 * https://github.com/DockYard/ember-composable-helpers/blob/master/addon/helpers/group-by.js
 * */
import Ember from 'ember';
import DS from 'ember-data';

const {
  A,
  isArray,
  Helper,
  RSVP,
  computed,
  computed: { mapBy },
  defineProperty,
  get,
  observer,
  set,
  isEmpty,
} = Ember;

const {
  PromiseObject,
} = DS;

const groupBy = function () {
  const groups = Ember.Object.create({});
  const array = get(this, 'array');
  const byPath = get(this, 'byPath');
  const paths = byPath.split('.');

  const prom = RSVP.resolve(array).then((items) => {
    return items.map((itemPromise) => {
      return RSVP.resolve(itemPromise).then((item) => {
        const groupNamePromise = paths.reduce(
          (previousItemPromise, path) => RSVP.resolve(previousItemPromise)
            .then(previousItem => get(previousItem, path))
          , item);

        return RSVP.resolve(groupNamePromise).then((groupName) => {
          let group = get(groups, groupName);

          if (!isArray(group)) {
            group = A();
            groups[`${groupName}`] = group;
          }

          group.pushObject(item);
        });
      });
    });
  });

  return PromiseObject.create({
    promise: RSVP.resolve(prom).then(() => groups),
  });
};

export default Helper.extend({
  compute([byPath, array]) {
    set(this, 'array', array);
    set(this, 'byPath', byPath);

    return get(this, 'content');
  },

  update() {
    const byPath = get(this, 'byPath');
    if (!isEmpty(byPath)) {
      if (byPath.includes('.')) { // nested path: item.pathA.pathB.pathX
        // Dependent keys containing @each only work one level deep.
        byPath.split('.').forEach((path, index, paths) => {
          if (index === 0) {
            defineProperty(this, `_nested${index}`, mapBy('array', path));
          } else if (index + 1 === paths.length) {
            defineProperty(this, 'content', computed(`_nested${index - 1}.@each.{${path}}`,
              groupBy));
          } else {
            defineProperty(this, `_nested${index}`, mapBy(`_nested${index - 1}`, path));
          }
        });
      } else { // single path: item.pathA
        defineProperty(this, 'content', computed(`array.@each.${byPath}`, groupBy));
      }
    } else {
      defineProperty(this, 'content', null);
    }
  },

  paramsDidChanged: observer('byPath', 'array.[]', function () {
    console.debug('paramsDidChanged');
    this.update();
    this.recompute();
  }),

  contentDidChange: observer('content.[]', function () {
    console.debug('contentDidChange');
    this.recompute();
  }),
});

Sample data:

let nestedData = [
  { skill: { type: { label: 'language' }, name: 'Python' } },
  { skill: { type: { label: 'language' }, name: 'JavaScript' } },
  { skill: { type: { label: 'web' }, name: 'Django' } },
  { skill: { type: { label: 'web' }, name: 'Flask' } },
  { skill: { type: { label: 'database' }, name: 'MySQL' } },
];

Usage:

{{#each-in (group-by "skill.type.label" nestedData) as |label stacks|}}
  <h4>{{label}}</h4>
  <ul>
    {{#each stacks as |stack|}}
      <li>{{stack.skill.name}}</li>
    {{/each}}
  </ul>
{{/each-in}}

Posts: 1

Participants: 1

Read full topic


Viewing all articles
Browse latest Browse all 4831

Trending Articles