问题
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