How can I force Ember to get into the second level of an object in a template using a helper function?

久未见 提交于 2019-12-14 03:15:49

问题


I have a 'has-many' helper (thanks to the help of @GJK in In an ember component template how can I use 'if' to check for at least one of an array having a property equal to X?) that allows me to check one level of an array in my template for a match on a property:

helpers/has-many.js

import Ember from 'ember';

// Param[0]: Array to iterate over
// Param[1]: Comparison operator to use, currently supports '===', '>', '<' and nested 'has-any'
// Param[2]: Value to check for
// (Optional) Param[3]: The property of the arrayObject to check, if this is not supplied the element of the array is checked directly

// Returns: True if array has at least one match to value

export function hasAny(params) {
console.log(params);
  var compare = function(a, b, operator) {
    console.log(a + ' ' + b + ' ' + operator);
    if (operator === '===') {
      return a === b;
    }
    if (operator === '>') {
      return a > b;
    }
    if (operator === '<') {
      return a < b;
    }

    return false;
  };

  if (params.length < 3) {
    return false;
  }

  if (params[0] === undefined || params[1] === undefined || params[2] === undefined) {
    return false;
  }

  if (params.length === 3) {
    return params[0].any((item) => compare(item, params[2], params[1]));
  }

  if (params[3] === undefined) {
    return false;
  }

  return params[0].any((item) => compare(item.get(params[3]), params[2],     params[1]));

}

export default Ember.Helper.helper(hasAny);

I now want to add a second level to this so that I can do the equivalent of

notification.account.contacts.Any(c => c.communications.Any(cm => cm.communicationType.description === 'Email'));

I tried adding to my code with the following:

helpers/has-many.js

import Ember from 'ember';

// Param[0]: Array to iterate over
// Param[1]: Comparison operator to use, currently supports '===', '>', '<' and nested 'has-any'
// Param[2]: Value to check for
// (Optional) Param[3]: The property of the arrayObject to check, if this is not supplied the element of the array is checked directly

// Returns: True if array has at least one match to value

export function hasAny(params) {
console.log(params);
  var compare = function(a, b, operator) {
    console.log(a + ' ' + b + ' ' + operator);
    if (operator === '===') {
      return a === b;
    }
    if (operator === '>') {
      return a > b;
    }
    if (operator === '<') {
      return a < b;
    }
    if (operator.lastIndexOf('has', 0) === 0) {
      var innerParams = operator.split('%');
      console.log(a);
      return hasAny(a, innerParams[1], innerParams[2], b);
    }
    return false;
  };

  if (params.length < 3) {
    return false;
  }

  if (params[0] === undefined || params[1] === undefined || params[2] === undefined) {
    return false;
  }

  if (params.length === 3) {
    return params[0].any((item) => compare(item, params[2], params[1]));
  }

  if (params[3] === undefined) {
    return false;
  }

  return params[0].any((item) => compare(item.get(params[3]), params[2],     params[1]));

}

export default Ember.Helper.helper(hasAny);

And then in the template I have:

{{#if (has-any notification.account.contacts 'has-any%===%Email' 'communicationType.description' 'communications')}}
  <p>working!</p>
{{/if}}

The problem I have is although the template knows to fire the helper again once notification.account.contacts has resolved from the API, it doesnt know to go again once each contacts' communications have come down from the API - how can I force the template/helper to get my next level of promises?


EDIT 1 These are the models (slightly stripped down, but with the key fields):

models/order-notification.js

Export default DS.Model.extend({
  orderNotificationType: DS.belongsTo('orderNotificationType', {async: true}),
  orderNotificationDetail: DS.attr('string'),
  sent: DS.attr('boolean'),
  account: DS.belongsTo('account', {async: true}),
});

models/account.js

export default DS.Model.extend({
  name: DS.attr('string'),
  accountType: DS.hasMany('accountType', {async: true}),
  contacts: DS.hasMany('contact', {async: true}),
  addresses: DS.hasMany('address', {async: true}),
});

models/contact.js

export default DS.Model.extend({
  title: DS.attr('string'),
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  accounts: DS.hasMany('account', {async: true}),
  communications: DS.hasMany('communication', {async: true})
});

models/communication.js

export default DS.Model.extend({
  value: DS.attr('string'),
  communicationType: DS.belongsTo('communicationType', {async: true}),
  contact: DS.belongsTo('contact', {async: true})
});

models/communication-type.js

export default DS.Model.extend({
  description: DS.attr('string'),
  communications: DS.hasMany('communication', {async: true})
});

EDIT 2

I've got something nearly working:

{{#each notification.account.contacts as |contact index|}}
  {{#each contact.communications as |communication id|}}
    {{#if (has-any contact.communications '===' 'Email' 'communicationType.description' communication.communicationType.description)}}
    <p>Working!</p>
    {{/if}}
  {{/each}}
{{/each}}

By going within the each and passing a fourth parameter to the has-any helper (that is unused), all the promises are resolved - the problem I have is that <p>Working!</p> comes up multiple times (because the any is satisfied multiple times by multiple contacts/communications). This method could work if there is a way within an #each to display the contents of an if block only the first time the if is satisfied.


回答1:


So I couldn't do this the way I wanted to but GLK got me in the right direction. At the very lowest layer I aliased communicationType.description in the communication model:

communicationTypeDescription: Ember.computed.alias('communicationType.description')

I then observe that in a property at the next level up

communicationsAvailable: function() {
  var commsAvailable = [];
  this.get('communications').forEach(function(comm) {
    commsAvailable.push(comm.get('communicationType.description'));
  });
  return commsAvailable.filter(function(value, index, ca) {
    return ca.indexOf(value) === index;
  });
}.property('communications.@each.communicationTypeDescription')

I then observe that observable at the next layer up (you could repeat this bit for however deep you need), in my account model:

communicationsAvailable: function() {
  var commsAvailable = [];
  this.get('contacts').forEach(function(contact) {
    commsAvailable.push.apply(commsAvailable, contact.get('communicationsAvailable'));
  });
  return commsAvailable.filter(function(value, index, ca) {
    return ca.indexOf(value) === index;
  });
}.property('contacts.@each.communicationsAvailable')

Note the subtle difference here of push.apply.

Finally, I then used my has-many function in it's 3 param form in my template:

{{#if (has-any notification.account.communicationsAvailable '===' 'Email')}}
  <p>Working!</p>
{{/if}}


来源:https://stackoverflow.com/questions/31459209/how-can-i-force-ember-to-get-into-the-second-level-of-an-object-in-a-template-us

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