Finish Reimbursement UI (MVP) #209
@@ -6,6 +6,7 @@ import { action } from '@ember/object';
|
||||
import { A } from '@ember/array';
|
||||
import { scheduleOnce } from '@ember/runloop';
|
||||
import isValidAmount from 'kredits-web/utils/is-valid-amount';
|
||||
import readFileContent from 'kredits-web/utils/read-file-content';
|
||||
import config from 'kredits-web/config/environment';
|
||||
|
||||
export default class AddReimbursementComponent extends Component {
|
||||
@@ -85,7 +86,22 @@ export default class AddReimbursementComponent extends Component {
|
||||
}
|
||||
|
||||
@action
|
||||
updateContributor(event) {
|
||||
async addExpensesFromFile (evt) {
|
||||
const content = await readFileContent(evt.target.files[0]);
|
||||
const expenses = JSON.parse(content);
|
||||
|
||||
if (expenses instanceof Array) {
|
||||
for (const item of expenses) {
|
||||
this.addExpenseItem(item);
|
||||
}
|
||||
} else {
|
||||
console.warn("Expenses in file must be a list of items:");
|
||||
console.debug(content);
|
||||
}
|
||||
}
|
||||
|
|
||||
|
||||
@action
|
||||
updateContributor (event) {
|
||||
this.recipientId = event.target.value;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,3 +87,9 @@
|
||||
<AddExpenseItem @addExpenseItem={{fn this.addExpenseItem}} />
|
||||
{{/if}}
|
||||
</form>
|
||||
<form id="add-expenses-from-file">
|
||||
<h3>Add expense items from file</h3>
|
||||
<input type="file" multiple="false"
|
||||
onchange={{fn this.addExpensesFromFile}}
|
||||
accept="application/json" />
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function (file) {
|
||||
const reader = new FileReader();
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onload = event => resolve(event.target.result)
|
||||
reader.onerror = error => reject(error)
|
||||
reader.readAsText(file)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user
Async actions are an anti-pattern. While the asynchronous call (here
readFileContent()is still in progress, one might navigate away and the component will be destroyed. After the asynchronous call finishes, any later statement trying to accessthiswill then throw an exception.One solution would be to use an
ember-concurrencytask instead.Thanks! I added a note with a link to your comment for now, since this is kind of an undocumented and hidden feature for someone who knows what they're doing as of now. Mostly just added it so I could easily migrate the existing data, even though it may be useful for other imports in the future. It's basically instant and you wouldn't navigate away while adding a reimbursement, so I think it's OK to leave it as is for now and spend time on more important things.