Reputation: 123
As the title suggests, I am trying to bind an elements source to a computed array which is populated by results from a http.fetch call. Here is the code (the interesting parts for this question):
The view:
<tr>
<td repeat.for="boardItemFilter of boardItemFilters">
<input if.bind="boardItemFilter.isStringFilter()" type="text" value.bind="boardItemFilter.Value & debounce:400" />
<div if.bind="boardItemFilter.isCheckboxFilter()" repeat.for="sourceValue of boardItemFilter.SourceValues">
<input type="checkbox" value.bind="sourceValue" checked.bind="boardItemFilter.SelectedValues" />
<span textcontent.bind="sourceValue"></span>
</div>
</td>
</tr>
The viewmodel:
@computedFrom('selectedUnit')
get boardItemFilters(){
let unitName = "";
if(this.selectedUnit != null){
unitName = this.selectedUnit.Name;
}
return this.datacontext.fetchBoardItemFiltersByUnit(unitName)
.then(result => console.log(result);
}
Where the fetchBoardItemFiltersByUnit() function is:
fetchBoardItemFiltersByUnit(unitName){
let self = this;
let request = {'unitName': unitName};
return this.http.fetch('BoardFilters',{
method: 'post',
body: json(request),
}).then(response => response.json())
.then(response => this.ExtendFilters(response));
}
The boardItemFilters()
computed triggers on selectedUnit change properly. The datacontext call is made, that part works just fine. The problem happens when aurelia tries to bind the property before the http.fetch is done. It then thinks that the property is not repeatable, and breaks. After those errors, the console.log(result)
displays the expected array of objects that the server returned, but the result is not assigned to the computed property. I have tried to manually re trigger the binding using the signal behavior, with no luck.
The question is naturally, what am i doing wrong? Are the results not being stored into the computed property, or am binding it in a way that is not expected? What would be the proper solution to this problem?
I have tried a lot of things, and the only thing that seemed to work is to create a local variable which stores the http results, and always return that variable OUTSIDE the promise functions. But naturally, that will always return the previous state since it will run the asynch call, and return the local variable before the call finishes. This is of course unacceptable.
Or does Aurelia simply not yet support async binding?
Thank you in advance!
EDIT: I have managed to hack into a solution, but it's pretty ugly (using the bindingSignaler in the promise function to rebind, using the newValue and oldValue comparison to avoid infinite computed calls etc). I would still much like to hear the proper way to achieve this!
Upvotes: 1
Views: 1801
Reputation: 687
Here is a way to solve this kind of lengthy computed operation, which do not rely on async binding:
Example:
export class App {
@bindable count;
constructor() {
this.count = 3;
this.computeNums(this.count)
.then(result => this.nums = result);
}
countChanged(value) {
this.computeNums(value)
.then(result => this.nums = result);
}
computeNums(count) {
let result = [];
for (let i = 1; i <= count; i++) {
result.push(i);
}
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result), 1000);
});
}
}
Here, nums
is computed from count
. computeNums
simulate a lengthy operation.
Observing count
is achieved by a combination of @bindable
and countChanged
. You may want to use binding engine API instead, as in this question.
Full example that can run: https://gist.run/?id=f2e26044796bfe2334d786554039aab0
Upvotes: 3