Dropdown column in smart-table (angularjs) - html

I need to configure one of the columns in my smart-table to be a dropdown. The possible values for the 'Status' column are OK and PENDING. (These values are retrieved from a rest api) I am looking to initialize the value in the dropdown to OK/PENDING based on the value retrieved from the api.
I posted what I've tried so far,the status are all set to OK regardless of the actual value.
I'm just starting out with smart-table and javascript so any help is appreciated.
For reference, here is a sample json object being returned from my rest api (other fields removed):
[
{
comments: [
{
comment: "Test comment",
userId: "test_user",
timestamp: 1473282222280
}
],
status: "PENDING"
}]
Here is the smart-table html code:
<tbody>
<tr ng-repeat="row in rowCollection" ng-class="{danger: (row.status=='PENDING'),success:(row.status=='OK')}">
<td cs-select="row"></td>
<td align="center">
<select st-search="status">
<option value="">OK</option>
<option value="">PENDING</option>
<!-- <option ng-repeat="row in rowCollection | unique:'status'" value="{{row.status}}">{{row.status}}</option> -->
</select></td>
<td align="center">{{row.comments[0].comment}}</td>
</tbody>
and a screenshot of the table:

You can try with the ng-selected directive in this way:
<select>
<option ng-selected="row.status == 'PENDING'">PENDING</option>
<option ng-selected="row.status == 'OK'">OK</option>
</select>

Related

How to data-bind an array element with Angular

I am using Angular.
I have a table with two columns. Each cell of 1st column is a series number. Each cell of second column is a drop down of serial numbers. I am binding to a data source in my component and it is working fine.
My data source is a simple hard coded array in my component (I have as many elements as I like, here I show only one):
toolSeries = {
"Series": "12345678A",
"Serials" : ["123321", "123321", "54654", "357", "386758"],
}
and my binding is like so:
<tr *ngFor='let tools of toolSeries' height="50px">
<td class="tableEntryText">{{tools.Series}}</td>
<td class="tableEntryText">
<select class="noBorder additionalSelectStyle" name="filter_for" (change)="OnSerialChanged($event.target)">
<option *ngFor = 'let serial of tools.Serials'>
{{serial}}
</option>
</select>
</td>
<tr>
All good, works fine.
Now I need to add the following: each Tool Serial can have a "state". So I modify my data structure like so:
toolSeries = [
{
"Series": "12345678A",
"Serials" : ["199921", "123321", "54654", "357", "386758"],
"State": ["OK","BAD","BAD","UNKNOWN", "OK"]
},
So, I add another column to my HTML table and now my code becomes:
<tr *ngFor='let tools of toolSeries' height="50px">
<td class="tableEntryText">{{tools.Series}}</td>
<td class="tableEntryText">
<select class="noBorder additionalSelectStyle" name="filter_for" (change)="OnSerialChanged($event.target)">
<option *ngFor = 'let serial of tools.Serials'>
{{serial}}
</option>
</select>
</td>
<td class="tableEntryText">
<label class="labelAdjust">{{series.State[0]}}</label>
</td>
</tr>
Now as it stands, {{series.State[0]}} will mean that the the new field always shows the value of the first state for each serial.
Question: How should I write the binding so that the State of the field will reflect the state of the Serial selected in the drop down? So for example, when the user selects "123321" from the dropdown my new column's cell should show "BAD" instead of "OK".
Thanks for any help.
There are some other considerations here but the quick and dirty way to do what you want is this,
<select [(ngModel)]="selectedValue" class="noBorder additionalSelectStyle" name="filter_for" (change)="OnSerialChanged($event.target)">
<option *ngFor = 'let serial of tools.Serials' [ngValue]="serial">
{{serial}}
</option>
</select>
<td class="tableEntryText">
<label class="labelAdjust">{{tools.State[tools.Serials.indexOf(selectedValue)]}}</label>
</td>
You have to bind a value to your select so that you know what the selected option is and then you need to find the index of that value in the tools.Serials array and use that index in tools.State to get the corresponding state. You will also need to declare the selectedValue variable in your .ts file,
public selectedValue: number;
Keep in mind this assumes that tools.Serials and tools.State have the same number of elements. If they ever become out of sync you will likely have errors.
The better solution is likely to rethink your data structure and associate the states with their serials in a more direct way.
Stackblitz
My approach is quite similar to #DKidwell answer here, but instead of saving and looking up the value of selected tool.serial. I'm saving the index of selected tool.serial in ngModel & retrieving the tool.State value on same index if exists else there is no error and no value in tool.State.
So, even if the length of these arrays mismatched your code will work, but still probably its a good idea to rethink your data structure if this is going to be get more complex.
<select class="noBorder additionalSelectStyle" name="filter_for" [(ngModel)]="selectedSerial">
<option value="" disabled>Select a serial</option>
<option *ngFor = 'let serial of tools?.Serials; let i = index' [value]="i">
{{serial}}
</option>
</select>
</td>
<td class="tableEntryText">
<label class="labelAdjust">{{(tools?.State[selectedSerial]) || ''}} </label>
</td>
</tr>
Thank you both very much for taking the time to answer, I very much appreciate it. Your answers taught me a lot. In the end I did indeed change the data structure to one which enabled me to use less data-binding code.

How can I get details for each item from an array of objects?

I have an array of objects from which I take a name to create a list. When I click on one element of the list, a modal window should open to me in which detailed information about this element will be shown (you need to display a certain object), you can suggest how this can be implemented in Angular ?
You might want to try with a custom div modal, if you are using none of the ui kits or frameworks.
If you are using kits like material, you can use the material modal to display on clicking that item. To know which data to be passed, you can pass the id of the row data on clicking and then populate the required data from the array using filter().
for example, if you are using angular you are going to use *ngFor for array of objects
array of objects
eg:
ArrayObjects= [
{
"id": 1,
"place": "Sweden"
}, {
"id": 2,
"place": "USA"
}, {
"id": 3,
"place": "England"
}
]
if you are going to use to show data in table
<table>
<tr>
<th> Sl.No </th>
<th> Place </th>
<th> Popup Action </th>
</tr>
<tr *ngFor="let group of ArrayObjects; index as i">
<td> {{i+1}} </td>
<td> {{group.place}} </td>
<td (click)="ClickMeToGetParticularData(group, i)" > </td>
</tr>
</table>
In .ts part
ClickMeToGetParticularData(data, index){
console.log("Required data", data) ////////// use this to get data to show in popup
console.log("Required index", index) ///// if you want index
}

Vue v-bind - access array object from another array loop

I'm working on a project where I created a table component which is used on multiple pages with different configuration. Every table has it's configuration in a separate file where I store keys, titles and size classes for each column.
Data for each table body come from REST calls and they are loaded dynamically, paginated and then displayed.
<template slot="thead">
<tr>
<th v-for="item in headers" :key="item.id" :class="item.classes">{{item.title}}</th>
</tr>
</template>
<template slot="tbody">
<tr v-for="skill in paginatedSkills"
:key="skill.id"
v-on:click="selectRow(skill)"
v-bind:class="{selectedRow: selectedSkill === skill}"
>
<td class="cell-l">{{skill.name}}</td>
<td class="cell-m">{{skill.owner}}</td>
<td class="cell-s">{{skill.complexity}}</td>
<td class="cell-full">{{skill.description}}</td>
</tr>
</template>
What I want to do is to avoid writing size class for every single cell in the tbody loop. I was hoping to get index of looped object and use it to retrieve the class from config object which is used to populate cells in thead.
<tr v-for="(skill, index) in paginatedSkills" ...>
<td class="{headers[index].classes}">{{skill.name}}</td>
Using index on headers will return the correct item but as a string so obviously classes are not accessible. Any idea how to tweak it?
This options are no go, failing on compile
<td :class="{JSON.parse(headers[index]).classes}">{{skill.name}}</td>
<td :class="{JSON.parse(headers)[index].classes}">{{skill.name}}</td>
<td :class="{{JSON.parse(headers[index]).classes}}">{{skill.name}}</td>
To set class from a variable/property you have two options:
<td v-bind:class="headers[index].classes">{{skill.name}}</td>
<td :class="headers[index].classes">{{skill.name}}</td>
No need for curly braces here since v-bind already expects JS expression.
Update:
What you can also do, is to associate keys of skill object (name, owner, complexity, description) with their header, so each item of headers array will also have for example key property used to access value from skill object:
headers: [
{ id: 1, classes: 'cell-l', title: 'title', key: 'name' },
{ id: 2, classes: 'cell-s', title: 'title', key: 'owner' },
...
]
Thus, your code can be simplified the following way:
<tr v-for="skill in paginatedSkills" ...>
<td v-for="header in headers" v-bind:class="header.classes">{{skill[header.key]}}</td>
</tr>

Thymeleaf template: concatenate Strings

I have this piece of code, but the option value is not concatenating {item.id}-${driver.id} and instead I got "-2"
<tr th:each="item: ${devices}" >
<td class="col_id" th:text="${item.id}" ></td><!-- ID -->
<td class="col_name" th:text="${item.description}"></td><!-- NAME -->
<td class="col_name" th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}" th:text="${item.application.name}"></td><!-- NAME -->
<td class="col_name" >
<select id="selectAuthorizedDriverId" >
<!-- option value="0">Please select the driver</option-->
<option th:each="driver : ${drivers}"
th:value="${item.id}-${driver.id}"
th:text="${driver.firstName}"
th:selected="${driver.id==item.driverDevices[0].driver.id}">
</option>
</select>
Thymeleaf is able to do mathematical operations, in your case:
th:value="${item.id}-${driver.id}"
will generate a single result from two integers.
Try
th:value="|${item.id}-${driver.id}|"
instead as this should make sure the given values are concatenated.

How do I filter Html table on default dropdown value on page laod in angularjs

This is my Select
<select class="viewcombobox" ng-model="search.view_nm">
<option ng-repeat="tab in CompetRx | unique:'view_nm'" value="{{tab.view_nm}}">{{tab.view_nm}}</option>
</select>
Dropdown value are Weekly & Monthly
This is my table
<table>
<tr> <th> Brand </th> </tr>
<tr ng-repeat="tab in CompetRx | filter:search.view_nm >
</table>
On intial page load how do I set Weekly as default value in Dropdown at same time it should filter the table accordingly
Immediate help is appreciated!!
It looks like you didn't initialised your search object.
$scope.CompetRx =[{view_nm: "Weekly"}, {view_nm: "Monthly"}]
$scope.search = {view_nm: "Weekly"}
I created small working plunker. Hope it will help.