I am using Aurelia v4.6.1 with Typescript in a.Net core project. I have a component that retrieves data from an external source and displays it in a table using a repeat.for attribute.
Now I want to highlight the row that has been selected (clicked). On my view model I have a documents property of type Document[] that holds all the documents displayed in the grid, and I have a selectedDocument property of type Document that should hold the last selected document. This is set in a click event on the row setSelected.
My view is as follows:
<template>
<require from="../../resources/valueconverters/itemIdFormat"></require>
<require from="./documentData.css"></require>
<h1>Patient Documents</h1>
<p>This component demonstrates fetching data from DME.</p>
<p if.bind="!documents"><em>Loading...</em></p>
<table if.bind="documents" class="table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Patient</th>
</tr>
</thead>
<tbody>
<tr repeat.for="document of documents" click.trigger="setSelected(document)" class="${($document.title == $selectedDocument.title) ? 'selected' : 'notSelected'}">
<td>${ document.itemId | itemIdFormat }</td>
<td>${ document.title }</td>
<td>${ document.patient.lastName}, ${ document.patient.firstName }</td>
</tr>
</tbody>
</table>
My ViewModel class is:
import { HttpClient } from 'aurelia-fetch-client';
import { BindingEngine, inject } from 'aurelia-framework';
@inject(HttpClient, BindingEngine)
export class DocumentData {
public documents: Document[];
public selectedDocument: Document;
public bindingEngine
constructor(http: HttpClient, bindingEngine) {
this.bindingEngine = bindingEngine;
this.selectedDocument = null;
let subscription = this.bindingEngine
.propertyObserver(this, 'selectedDocument')
.subscribe(this.selectedDocumentChanged);
http.fetch('/api/Data/PatientDocuments')
.then(result => result.json() as Promise<Document[]>)
.then(data => {
this.documents = data;
});
}
setSelected(selected: Document) {
this.selectedDocument = selected;
}
selectedDocumentChanged(newValue, oldValue) {
// this.documents.forEach(function (d) {
// d.isCurrent = d === newValue;
// })
}
}
As you can see in the view html, I have set the class attribute on the table row element with:
class="${($document.title == $selectedDocument.title) ? 'selected' : 'notSelected'}"
However for some reason this always returns true so all rows are highlighted.
So I tried this instead:
class="${$document.isCurrent ? 'selected' : 'notSelected'}"
Then I used the binding engine subscribe function on the selectedDocument
property, setting it to run selectedDocumentChanged
method, to set the isCurrent property on each document in the list manually whenever the selectedDocument
property is changed. Notice however that this is commented out. This is because the this
variable is not in scope in the subscriber changed method so I can't use it.
So I am a bit stuck. What is the correct way to highlight the selected row? I guess it might be possible using a nested component repeated for each row, but I would prefer to be able to achieve this all in this component.
Firstly - the scope issue you're getting with the selectedDocumentChanged()
method is because you're using the Binding Engine. If you use the observable decorator instead like below, this
will no longer be out of scope;
import {observable} from "aurelia-framework";
export class DocumentData {
@observable selectedDocument;
// Your other code
selectedDocumentChanged(newVal, oldVal) {
this.documents.forEach(function (d) { // no longer out of scope
d.isCurrent = d === newValue;
})
}
}
Secondly - in your template you don't need to prefix your properties with a $
. This is only used when you want to use string interpolation within your templates. If you're looping through, or comparing properties then you can just use them as normal. For example;
class="${document.isCurrent ? 'selected' : 'notSelected'}"
or;
class="${(document.title == selectedDocument.title) ? 'selected' : 'notSelected'}"