Skip to content

Instantly share code, notes, and snippets.

@fragsalat
Created February 27, 2019 23:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fragsalat/ed369219dfaca4dc22255e7b6b20857f to your computer and use it in GitHub Desktop.
Save fragsalat/ed369219dfaca4dc22255e7b6b20857f to your computer and use it in GitHub Desktop.
Aurelia computedFrom array of objects
import {Parser, Expression, ComputedExpression, Binding, Scope, createOverrideContext} from 'aurelia-binding';
class ComputedListExpression extends Expression {
/**
* @param {String} expressionStr Expression string for one dependency
*/
constructor(expressionStr: string) {
super();
const parser = new Parser();
this.expressions = expressionStr.split('[*].').map(part => parser.parse(part));
}
/**
* Connect template interpolation to dependency. Once dependency is changed and subscribers are called this ensures
* the binding is notified for changes.
* @param {Binding} binding
* @param {Scope} rootScope
*/
connect(binding: Binding, rootScope: Scope): void {
let currentScopes = [rootScope];
// Iterate trough expressions e.g. children => Array<Child>, isSelected => boolean
for (let expression of this.expressions) {
let scopes = [];
currentScopes.forEach(scope => {
scopes = scopes.concat(this.connectAndEvaluate(expression, binding, scope));
});
currentScopes = scopes;
}
}
/**
* Connect current expression to binding with current scope
* @param {Expression} expression
* @param {Binding} binding
* @param {Scope} scope
* @returns {Array<Scope>}
*/
connectAndEvaluate(expression, binding, scope): Array<Scope> {
expression.connect(binding, scope);
const scopes = [];
let results = expression.evaluate(scope);
// If result is not a list make it to one
if (!Array.isArray(results)) {
results = [results];
}
results.forEach(result => {
scopes.push({bindingContext: result, overrideContext: createOverrideContext(result)});
});
return scopes;
}
}
/**
* Mark getter to be computed from a list. This decorator allows to use properties of objects in arrays as dependencies.
* This works like common computedFrom except that arrays can be accessed by appending [*]. to properties of type array
* @param {Array<string>} expressions Expressions like children[*].property
* @returns {Function}
*/
export function computedFromList(...expressions) {
return function(target, property, descriptor) {
const dependencies = expressions.map(expression => new ComputedListExpression(expression));
descriptor.get.dependencies = new ComputedExpression(property, dependencies);
}
}
import {computedFrom} from 'aurelia-binding';
import {computedFromList} from "./decorator";
export class Child {
selected = false;
@computedFrom('selected')
get isSelected() {
return this.selected;
}
}
export class Parent {
children = [];
@computedFromList('children[*].isSelected')
get isSelected() {
console.log('Get isSelected');
return this.children.every(child => child.isSelected)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment