How to make a table with selectable rows accessible - angularjs

I have a web app with a view that renders a list of items, where the user is intended to select multiple rows, by means of a checkbox at the start of each row, and then subsequently invokes an action on those rows. My question is: what is the best practice for making this accessible? My first-draft Html looks something like this (it uses Angular.js directives, but that is not of concern here).
<table>
<tbody>
<tr ng-repeat="item in collection.items">
<td class="checkbox">
<input type="checkbox">
</td>
<td tabindex="0">
{{item.title}}
</td>
</tr>
</tbody>
</table>
(My decision to use table, despite there being just one column other than the checkboxes, is because I also need a multi-column 'table' view of the same information. That is not my main concern here).
When I ran an accessibility-checker tool over this, it complained that the checkboxes had no label. But it doesn't strike me as sensible to add an artificial label (e.g. a row number) to it. Should I be using row-scope, perhaps, to make the check-box into a for the row?

You could add an unique id to each input and create a label for that input. $index is a ng-repeat iterator offset of the repeated element (0..length-1).
<table>
<tbody>
<tr ng-repeat="item in collection.items">
<td class="checkbox">
<input type="checkbox" id="{{'item-' + $index }}">
</td>
<td>
<label for="{{'item-' + $index}}">
{{item.title}}
</label>
</td>
</tr>
</tbody>
</table>

Related

Vuetify: Table rows of checkboxes

I'm using vuetify and trying to use either a data-table, or simple table to display rows of checkboxes and a couple of text fields.
Like this:
However, when I use v-simple-table all of the checkboxes and fields have their own lines; breaking the table.
<v-form>
<h3>Shifts:</h3>
<v-simple-table>
<thead>
<tr>
<th>Su</th><th>M</th><th>T</th><th>W</th><th>Th</th><th>F</th><th>Sa</th><th>From:</th><th>To:</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-checkbox></v-checkbox>
</td>
<td>
<v-text-field value="9:00"></v-text-field>
</td>
<td>
<v-text-field value="22:00"></v-text-field>
</td>
</tr>
</tbody>
</v-simple-table>
</v-form>
Changing v-simple-table to just table gives me the correct layout.
I wouldn't mind using a data-table, but I didn't see a way to have a row of checkboxes instead of a row of data text.
The v-simple-table component is a simple wrapper component around the
element. Inside the component you can use all the regular
table elements such as <thead>, <tbody>, <tr>, etc.
v-simple table give you some extra options which you don't have to program like fixed headers, or a dense format.
So when you use v-simple-table u still have to define table

Change in one row of drop-down list reflects all rows

I am new to front end programming so hoping to get some guidance. I am developing an application using HTML AngularJs which uses ng-repeat and ng-model and populates multiple rows based on the data in the database for a user. Each row has static data coming from DB (returned as object via restAPI) and dynamic select option for user selection. Select option is hardcoded in app.js and linked to model on HTML for DB update upon selection using update button. Issue is when I select any value from list in one row, this is reflected in the select option on other row. Each row has its own button and i can see update function is working at row level.
Below is HTML and js files
<body>
<h1 align="center">User Tracker</h1>
<div ng-controller="MainController as main">
<div>
<p>Please Enter ID </p>
<p>
<input type="text" ng-model="main.model.userName"></input>
<button ng-click="main.model.getOrgList()">List Org List</button>
</p>
</div>
<hr ng-show="main.model.formSubmitted"></hr>
<div>
<table class="table table-bordered" border="1" ng-show="main.model.formSubmitted">
<thead>
<tr>
<th>ID</th>
<th>User Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="org in main.model.orgList" id="{{org.id}}">
<td>{{org.id}}</td>
<td align="center">{{org.user}}</td>
<td align="center">
<select ng-model="main.model.selectedRecord.appCurrentStateSelected ">
<option ng-repeat=" option in main.model.appCurrentStateList " value ="{{option.value}}" id="{{option.id}}"> {{option.name}} </option>
</select>
</td>
<td>
<button ng-click="main.model.updateAppDetailsList({id:org.id,userName:org.name,appCrntState:main.model.selectedRecord.appCurrentStateSelected})">Update</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
Full HTML and JS file is here - https://jsfiddle.net/4jbtL0cj/
You are updating the same object which you have set as ng-model for each row (appCurrentStateSelected). This will update the same object and hence affect all rows as the same is set as ng-model for all rows. The ng-model should be an array or array of objects and then it should be set to array[$index], so that the ng-model is seperate for each row ($index is the index of ng-repeat). This is demonstrated in below plunker. You can do the same changes to your code.
http://plnkr.co/edit/FdWJyzNfbISa9NKFsCqt?p=preview
<td><select ng-model="selected[$index]" ><option ng-repeat="d in dropd" value="{{d.value}}" id="{{d.id}}">{{d.name}}</option></select></td>

Filter (ngRepeat) in a table not working on all columns

I want to filter my table generated with a ng-repeat.
<tbody>
<tr ng-repeat="x in contact.listeContacts | filter:contact.searchText track by $index">
<td>
<b>{{x.gd$name.gd$fullName.$t}}</b>
</td>
<td>
<p>{{contact.listeContacts[$index].gd$name.gd$familyName.$t}}</p>
</td>
<td>
<p>{{contact.listeContacts[$index].gd$name.gd$givenName.$t}}</p>
</td>
<td>
<p>{{contact.listeContacts[$index].gd$email[0].address}}</p>
</td>
<td>
<p>{{contact.listeContacts[$index].gd$phoneNumber[0].$t}}</p>
</td>
<td>
<p>{{contact.listeContacts[$index].gd$organization[0].gd$orgTitle.$t}}{{contact.listeContacts[$index].gd$organization[0].gd$orgName.$t}}</p>
</td>
</tr>
</tbody>
My issue is that my filter seems to be applied only on the first column.
Imagine I have these three objects to display :
obj1 = {
'name':'obj1',
'age':10
}
obj2 = {
'name':'obj2',
'age':1000
}
obj3 = {
'name'='obj3',
'age':100000
}
If my searchText is "obj3", the table will display something like this obj3 / 10
instead of obj3 / 100000
I don't understand why behaving like this.
EDIT: John Joseph, Xun Chao & tanmay helped me to resolve my first problem, check their answers.
Now, I don't understand why my search filter is not working as expected. Please see this Plunker : https://plnkr.co/edit/SfaYdgVkSfhOdI5enpOd?p=preview
While all provided answers are correct, none of them has explained why this happened. When you filter with "obj3", it displays only one item in the ng-repeat but your contact.listeContacts array still has length say 3. So, while searching for third item's text (here obj3), it tries to show contact.listeContacts[0] because filtered array has only one element.
You have two ways to solve this:
As mentioned by all other answers, put x instead of contact.listeContacts[$index]
You can have a reference to filtered array like this:
<tr ng-repeat="x in mycontacts = (contact.listeContacts | filter:contact.searchText) track by $index">
And, instead of contact.listeContacts[$index] you can then use mycontacts[$index] which points to filtered array and not actual array
You are directly accessing the contact.listeContacts for the rest of the columns instead of the looped variable x. That should be the problem. Try the following, replace contact.listeContacts[$index] with x.
<tbody>
<tr ng-repeat="x in contact.listeContacts | filter:contact.searchText track by $index">
<td>
<b>{{x.gd$name.gd$fullName.$t}}</b>
</td>
<td>
<p>{{x.gd$name.gd$familyName.$t}}</p>
</td>
<td>
<p>{{x.gd$name.gd$givenName.$t}}</p>
</td>
<td>
<p>{{x.gd$email[0].address}}</p>
</td>
<td>
<p>{{x.gd$phoneNumber[0].$t}}</p>
</td>
<td>
<p>{{x.gd$organization[0].gd$orgTitle.$t}}{{x.gd$organization[0].gd$orgName.$t}}</p>
</td>
</tr>
</tbody>
You have used contact.listeContacts[$index] instead of x...
<tbody>
<tr ng-repeat="x in contact.listeContacts | filter:contact.searchText track by $index">
<td>
<b>{{x.gd$name.gd$fullName.$t}}</b>
</td>
<td>
<p>{{x.gd$name.gd$familyName.$t}}</p>
</td>
<td>
<p>{{x.gd$name.gd$givenName.$t}}</p>
</td>
<td>
<p>{{x.gd$email[0].address}}</p>
</td>
<td>
<p>{{x.gd$phoneNumber[0].$t}}</p>
</td>
<td>
<p>{{x.gd$organization[0].gd$orgTitle.$t}}{{x.gd$organization[0].gd$orgName.$t}}</p>
</td>
</tr>
</tbody>
Your problem lies in the $index you use for accessing your items. Whenever your list in ng-repeat is filtered, the index property is looping on your filtered list.
You just have to use your local variable, named x in your case.
See this codepen for a live example:
<td>{{contact.name}}</td>
<td>{{contacts[$index].name}}</td> <!-- may not be the same -->

angular ng-repeat dynamic generate checkbox and string

I have a directive that host a template that takes in a JSON (that contain some data for building a table, and some method to manipulate the data) and generate a table.
relevant code in the template:
<table class="table" id="table_{{table.name}}">
<thead>
<tr>
<th ng-repeat="header in table.headers track by $index">{{header}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in table.rows track by $index">
<td ng-repeat="item in row track by $index"
ng-Click="table.selectedRow(row)">
{{item}}
</td>
</tr>
</tbody>
</table>
an example of a json being fed into this template
$scope.searchTable = {
headers: ["title", "deleted"],
rows: [ ["ABC", true],
["DEF", false] ],
[...]
}
My question is:
sometimes the table require a column with checkboxs, depending on the data. Is there any way to change my template so that when necessary it will generate a column of checkbox instead of just string?
For example: For the JSON example above, let's say I want the column of deleted to be checkboxes. So if an entry is deleted that row will have a unchecked checkbox under the deleted column.
Does this make any sense? Everything is generated dynamic, so I have no idea which column will be checkboxes. It all depends on the data the page gets from the server. So in the example: the server will tell client which entry is readable, then I will generate that JSON and feed it to the template. How should I structure my JSON and the template
Is this even doable? Any tips?
<tr ng-repeat="row in table.rows track by $index">
<td ng-repeat="item in row track by $index">
<div data-ng-if="item.type!=='checkbox'" ng-Click="table.selectedRow(row)"></div>
<div data-ng-if="item.type==='checkbox'"> <input type="checkbox" /></div>
{{item}}
</td>
</tr>

Displaying icon in a table using AngularJS depending on data from database?

I am trying to display an icon in a table according to data from a database. I have a function that calls a query and returns a boolean if that data exists, if it does exist I would like to display an icon on a table. The problem I'm running into is that when I call that function, the function is executed infinite times and I'm not really sure why this is happening. Any help would be highly appreciated!
This is the table that will be displaying the icon:
<table class="table table striped">
<thead>
<tr>
<th> ... </th>
<th> Exists? </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in myList">
<td> ... </td>
<td>
<i ng-show="callFunctionThatReturnsBoolean(item)" class="glyphicon glyphicon-thumbs-up" aria-hidden="true">
</i>
</td>
</tr>
</tbody>
</table>
You can use ng-show. When the ng-show condition satisfies based on the data then show the icon else don't.
I have written small piece of code based on your requirement.
In view,
<td>
<span ng-show='item.flag'><i class="glyphicon glyphicon-user"></i></span>
{{item.flag}}
</td>
Update:
Updated plunker to use method for ng-show.
Working Plunker
The problem is that ng-show should not be bound to a function call as it calls your function in every digest cycle: https://stackoverflow.com/a/20915253/2419215.
But I guess the best approach would be to send back the model from the backend in its final version and don't make queries like this for certain list elements.

Resources