I am trying to build a web app with aurelia and I couldn't find a way to add data via AJAX after everything on page has been rendered. The scenario (simplified):
There's a page, some part of which is dynamically composed from a component (say, data-table). data-table has a title, and table-rows to show the data. data-table should load it's data dynamically via an AJAX call. What I want is, loading the data after the page is rendered.
I tried using promises but it does the opposite, ie, aurelia waits until the promise is resolved before attaching the view (explained by Jeremy Danyow as well: "...In this example Aurelia will wait for the Promise returned by the activate method to resolve before binding the view to the viewmodel." (in his post titled "ES7 async/await with Aurelia") This causes the page remain stalled until all data is loaded.
A simple code example is provided below. Here, if you navigate to this page, you won't see anything (or the page wont get attached) until all data is loaded. What I want is, to load the page and show "Table for ..." title, and at the same time start loading data in the background and show the table itself when loading completes. The desired behavior is illustrated in the following "mock" screenshots.
before the ajax requrest is completed
after the ajax request is completed
Additionally, the tables may need to be updated based on user choices (additional data may be loaded and added to the tables) or additional tables may need to be added to the page.
I don't think the desired behavior matches any of the bind/attached/detached etc. life-cycle behaviors (but could be wrong). This could be implemented utilizing a variant of body.onload (or jquery etc.) but I wonder if this is possible to do using aurelia only (or mostly).
Maybe, being able to load data after everything is attached (eg. a "postattached" callback) could help. In that case, I would load all necessary components with their already loaded data (eg. their titles) and show them. Then, in the "postattached" section I will start loading data.
Sample code:
test.ts
export class testPage {
ids: number[] = [1,2,3] // for example 1,2,3; will be dynamically loaded as well
}
test.html
<template>
<h1>Test</h1>
<div repeat.for="id of ids">
<compose view-model="./components/table" model.bind="id"></compose>
</div>
</template>
table.ts
import { Loader } from './loader';
export class table {
id: number
tableData: number[][] = []
activate(model) {
this.id = model
}
attached() {
Loader.LoadData(this.id).then((res)=>{this.tableData = res})
}
}
table.html
<template>
<h2>Table for ${id}</h2>
<div repeat.for="rows of tableData">${rows}</div>
</template>
loader.ts
export class Loader {
static LoadData(tid): Promise<number[][]> { //simple stub to imitate real data loading
let data: number[][] = []
switch (tid) {
case 1:
data.push([11, 12, 13])
data.push([14, 15, 16])
break;
case 2:
data.push([21, 22, 23])
data.push([24, 25, 26])
break;
case 3:
data.push([31, 32, 33])
data.push([34, 35, 36])
break;
}
this.sleep()
return new Promise((resolve, reject) => {
this.sleep()
resolve(data)
})
}
protected static sleep(): boolean { // just to imitate loading time
let miliseconds = Math.floor(Math.random() * (3 - 1 + 1) + 1);
var currentTime = new Date().getTime();
console.debug("Wait for a sec: " + miliseconds)
while (currentTime + miliseconds * 1000 >= new Date().getTime()) {
}
return true
}
}
edit: Corrected code which was miscarried to the example here