Switching columns and rows with ng-repeat - html

I am trying to make a series of rows based off of an array of objects. i am trying to produce a series of columns displaying each property of the objects (each has the same properties), with rows showing the property values.
I am currently using the following code.
<div ng-controller="HomepageController" >
<!-- form holding the name -->
<form td-ui-grid-row ng-controller="HomepageController"
ng-submit="vm.addData()" class="form-inline party-form" ng-repeat="dataEntry in vm.dataList">
<div td-ui-grid-col="3xs" ng-repeat="dataContent in dataEntry">
dataContent:{{dataContent}}
</div>
</form>
</div>
vm.data is the following series of data: [{"name":"davis","phone":"11111111111"},{"name":"graeham","phone":"222222222222"},{"name":"eric","phone":"33333333333"}]
and i want to produce the table of vales to look like:
davis graeham eric
11111111111 222222222222 33333333333
however, I am getting the inverse:
davis 11111111111
graeham 222222222222
eric 33333333333
I simplified the code so that it is easier to look and work with:
<div ng-controller="HomepageController" >
<!-- form holding the name -->
<form td-ui-grid-row ng-controller="HomepageController" ng-submit="vm.addData()" class="form-inline party-form">
<table>
<tr>
<td ng-repeat="person in vm.dataList">
<input ng-model="vm.newData.name" type="text" class="form-control" placeholder="{{person.name}}" required>
</td>
</tr>
<tr>
<td ng-repeat="person in vm.dataList">
<input ng-model="vm.newData.name" type="text" class="form-control" placeholder="{{person.phone}}" required>
</td>
</tr>
</table>
</form>
</div>
This code is now showing the proper output format, however i need to be able to cycle through any number of object attributes
.directive('tdUiGridRow', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.addClass('td-row');
if (attrs.tdUiGridRow) {
element.addClass('td-row-type-'+attrs.tdUiGridRow);
}
}
};
})

Please do below. You would create two loops, one for name and one for phone.
<div><span ng-repeat="contact in vm.data">{{contact.name}}</span></div>
<div><span ng-repeat="contact in vm.data">{{contact.phone}}</span></div>

This simply requires looping through the data multiple times, and extracting one property each time.
<table>
<tr>
<td ng-repeat="person in data">{{person.name}}</td>
</tr>
<tr>
<td ng-repeat="person in data">{{person.phone}}</td>
</tr>
</table>
http://plnkr.co/edit/tXmaTmPuwbsN62cQLJuF?p=preview

Related

Posting row model data from a table within a form (no JS)

I ran into an almost identical issue before and posted a question about it here: Posting data of a single table row when table is in one form We were able to solve it but it felt pretty hacky. To summarize that post, I was attempting to use AJAX and JS to POST values from a table row to a controller. The problem was that every row would be serialized within the form. To solve this, I serialized only the row I needed.
This time I am running into the same issue, the only difference being that I'm not using AJAX and am submitting the model data from the table row directly to the controller action.
I tried to use both the foreach and for loops to generate table rows.
The foreach loop always POSTs the first row in the table, even if I click submit button on the second row.
The for loop doesn't POST anything, or at least I get all null values in my controller action's "shipment" parameter.
I also tried to encase the row in a form, but HTML does not allow to have a <form> in <tbody>.
What I want is to be be able to POST a single row (generated by looping through a list for models) directly to the "UpdateShipment" controller action. All of this without using AJAX.
HTML code:
#model OrderTrackingContract.SalesOrder
#foreach (var lineItem in Model.LineItems)
{
<table class="lineItemTables">
//line items table
</table>
#if (lineItem.Shipments.Count > 0)
{
<form method="post">
<table class="table shipmentTable">
<thead>
<tr>
<th>
ShipmentID
</th>
<th>
Qty Shipped
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < lineItem.Shipments.Count; i++)
{
<tr>
<td>
<input class="shipmentID" asp-for="#lineItem.Shipments[i].ShipmentID" />
</td>
<td>
<input class="qtyShipped" asp-for="#lineItem.Shipments[i].QtyShipped" value="#lineItem.Shipments[i].QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" asp-action="UpdateShipment" asp-controller="Shipments" />
<input class="deleteButton" type="submit" value="Delete" asp-route-shipmentID="#lineItem.Shipments[i].ShipmentID" asp-action="DeleteShipment" asp-controller="Shipments" />
</td>
</tr>
}
</tbody>
</table>
</form>
}
}
Controller action:
[HttpPost]
public IActionResult UpdateShipment(Shipment shipment)
{
_orderTrackingService.UpdateShipmentByID(shipment.ShipmentID, shipment.QtyShipped);
return NoContent();
}
EDIT: I used fiddler and I'm starting to understand what's going on. Both for and foreach loop submit the whole form because the whole table is in it.
Using a for loop I am forced to write asp-for="#lineItem.Shipments[i].ShipmentID", which means the controller must accept a parameter of LineItem lineItem.
Using foreach loop I am able to write asp-for="#shipment.ShipmentID, but because the generated attributes of the <input> are identical, the 'Shipment shipment' parameter binds to the first values POSTed.
What I want is to be be able to POST a single row (generated by looping through a list for models) directly to the "UpdateShipment" controller action. All of this without using AJAX.
To achieve above requirement, you can try to generate <form> and <table> for each Shipment item, like below.
#foreach (var lineItem in Model.LineItems)
{
<table class="table shipmentTable">
<thead>
<tr>
<th>
ShipmentID
</th>
<th>
Qty Shipped
</th>
<th>
Actions
</th>
</tr>
</thead>
</table>
#if (lineItem.Shipments.Count > 0)
{
foreach (var Shipment in lineItem.Shipments)
{
<form method="post">
<table class="table shipmentTable">
<tbody>
<tr>
<td>
<input class="shipmentID" asp-for="#Shipment.ShipmentID" />
</td>
<td>
<input class="qtyShipped" asp-for="#Shipment.QtyShipped" value="#Shipment.QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" asp-action="UpdateShipment" asp-controller="Shipments" />
<input class="deleteButton" type="submit" value="Delete" asp-route-shipmentID="#Shipment.ShipmentID" asp-action="DeleteShipment" asp-controller="Shipments" />
</td>
</tr>
</tbody>
</table>
</form>
}
}
}
Test Result

Filling all input fields

There is a markup:
<div class="scroll">
<table class="table table-striped">
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Author</th>
<th>Year</th>
<th>ISBN</th>
<th>Price</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of bookService.bookList">
<td><img src="../../{{book.ImagePath}}" width="100" height="150"></td>
<td>{{book.Name}}</td>
<td>{{book.Author}}</td>
<td>{{book.Year}}</td>
<td>{{book.ISBN}}</td>
<td>{{book.Price}}</td>
<td>{{book.Count}}</td>
<td>
<input type="text" name="Count" [(ngModel)]="Count">
<button class="btn btn-block btn-outline-success" (click)="onAdd(book, Count)"><i class="fa fa-plus"></i></button>
</td>
</tr>
</tbody>
The last column looks like this:
The problem is the following: when filling one TextBox, all the TextBoxes in the column are filled.
How can I solve this problem? Tried to give unique names to text fields and to thrust this cell in form, but all the same did not work.
You need to give unique name to the input field using angular template
<input [name]="'count' + i" >
Where i is the index from the *ngFor
But I think the major issue you have is that you need to bind book.Count instead of just Count
In latter case you'll have one variable called Count and you bind the same variable to all of the input fields. You need to attach the variable to the book itself so it is unique.
All your inputs have the same [(ngModel)]="Count" and the same name so if you update one, all of them will be updated
You can fix that if you have an array of count instead. So it will be something like
<tr *ngFor="let book of bookService.bookList; let i = index">
...
<input type="text" [name]="'count' + i" [(ngModel)]="count[i]">
People are giving you the HTML way, I'm giving you the Angular way : trackBy functions.
*ngFor="let book of bookService.bookList; trackBy: book.Name"
This one should work but I've never tested it.
The one should work in any case :
*ngFor="let book of bookService.bookList; trackBy: customTB"
customTB(item, index) {
return `${index}-${item.Name}`;
}
You can't use the same name for your inputs.
To fix this, you can add an id populate by the index of the loop *ngFor
Try this :
<tr *ngFor="let book of bookService.bookList; let i = index">
<input type="text" name="Count_${i}" [(ngModel)]="count">

Angular JS-Storing the row wise table values into the database

I have this situation: I need to bring-up a table structure into a HTML page.One of the columns in this table has to be brought from a table stored in the database.This column value along with two check boxes values needs to be stored in a database and next time these stored values should be brought in ,next time we open that page
HTML code:
<label class="col-md-3" for="Documents">Test:</label>
<div class="col-md-8" ng-controller="checklistController" ng-init='populateChecklist()'>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Not Required</th>
<th>Completed</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr ng-repeat = "x in list">
<td>{{x.name}}</td>
<td><input type="checkbox" ng-model='notRequiredCheckList' ng-disabled="completedCheckList" ng-disabled="linkForCheckList"/></td>
<td><input type="checkbox" ng-disabled="notRequiredCheckList" ng-model="completedCheckList" /></td>
<td><input type="text" ng-model="linkForCheckList" size="30" ng-disabled="notRequiredCheckList"/></td>
</tr>
</tbody>
</table>
<button ng-click="Save()">Save</button>
</div>
<div>
Javascript code:
app.controller("checklistController",function($scope,$http){
$scope.populateChecklist = function(a){
$http.get("commonAppGet.jsp?sqlStr=select name from test where val="+a+").then(function(resp){
$scope.list = resp.data;
});
}
});
Here I am getting name from a table and populating the first column when the page is getting loaded.Now along with these names ,I have two checkboxes along with a text box.I have added few validations.Now my challenge is,how to store these values row wise into a new table in the database and load these stored values next time.
Could someone help me?

Angular directive nested in table body renders above table

Okay so, I'm using Angular 1.5.7, and I'm trying to do some table rendering with ng-repeat and stuff. This is what my table markup looks like:
<table class="table table-hover">
<thead>
<tr>
<td>Property name</td>
<td>Property value</td>
</tr>
</thead>
<tbody>
<adm-preset-property
ng-repeat="(propertyName, definition) in
componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></adm-preset-property>
</tbody>
</table>
The <adm-preset-property> directive has a replace: true property and is rendered from a root <tr></tr> tag.
Now the loop works fine, BUT, instead of the table rows being rendered inside the table body, where they are nested, they are rendered ABOVE the table. I end up with
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<table>...</table>
What's worse, I can't seem to reproduce this on JSFiddle. What am I doing wrong?
EDIT: As requested, here's the template for the <adm-preset-property>
Template:
<tr>
<td>
<span data-toggle="tooltip" ng-attr-title="{{ ::propertyDefinition.description }}">{{ ::propertyDefinition.name }}</span>
</td>
<td ng-switch="propertyDefinition.editor_type">
<div ng-switch-when="select">
<ui-select append-to-body="true" ng-model="propertyValue[propertyName]" theme="bootstrap">
<ui-select-match placeholder="Select option...">{{ $select.selected.value }}</ui-select-match>
<ui-select-choices repeat="option.key as (key, option) in propertyDefinition.editor_properties.options | filter:{'value':$select.search} track by $index"
ng-value="key">
{{ option.value | highlight: $select.search }}</ui-select-choices>
</ui-select>
</div>
<div ng-switch-when="boolean">
<input type="checkbox" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="float">
<input type="range" step="0.01" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> {{ propertyValue[propertyName] }}
</div>
<div ng-switch-when="color">
<input type="color" style="width: 75%;" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="int">
<input type="number" style="width: 75%;" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> <br/>
<small>{{::propertyDefinition.editor_properties.min}} - {{::propertyDefinition.editor_properties.max}}</small>
</div>
<div ng-switch-default>
<input type="text" style="width: 75%;" ng-bind="propertyValue[propertyName]" />
</div>
</td>
</tr>
Directive:
(function() {
"use strict";
angular
.module('adomee.admin')
.directive('admPresetProperty', admPresetProperty);
/* #ngInject */
function admPresetProperty($log)
{
return {
restrict: 'E',
replace: true,
scope: {
propertyName: '=',
propertyValue: '=',
propertyDefinition: '='
},
templateUrl: 'app/preset/components/preset-property.tmpl.html',
link: function($scope, $element, $attributes) {
if ($scope.propertyDefinition.editor_type === 'select' && typeof($scope.propertyDefinition.defaultValue) === 'number') {
$scope.propertyValue[$scope.propertyName] = String($scope.propertyDefinition.defaultValue);
}
}
}
}
})();
Okay, after some research and consulting with my boss, we have concluded that Angular, naturally, compiles the directive's template into the DOM after the primary markup has been loaded.
Since the table expects a <tr> tag followed by <td>, but it runs into my custom tag instead, it removes the custom tag and places it outside of the table, which then compiles to my template afterwards, resulting in table rows on top of the actual table.
What I did was:
Change restrict: 'E' to restrict: 'A' in the directive.
Remove the replace property from it.
Remove the root <tr> tag and leave two <td> tags.
Place the directive into the table onto a <tr> and ng-repeat it.
Here's what it looks like now.
<tr adm-preset-property
ng-repeat="(propertyName, definition) in componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></tr>

How to filter the object values when an input is given?

HTML
<input type="search" placeholder="Filter" ng-model="searchstr" ng-change="details()">
<table style="width:831px;overflow:auto">
<tr ng-repeat="d in details" ng-if="$index%3==0">
<td style="height:232px;width:164px;" ng-if="details[$index]">{{details[$index].CourseName}}<br>{{details[$index].Professor}}<br>{{details[$index].CourseDuration}}</td>
<td style="height:232px;width:164px;" ng-if="details[$index+1]">{{details[$index+1].CourseName}}<br>{{details[$index+1].Professor}}<br>{{details[$index+1].CourseDuration}}</td>
<td style="height:232px;width:164px;" ng-if="details[$index+2]">{{details[$index+2].CourseName}}<br>{{details[$index+2].Professor}}<br>{{details[$index+2].CourseDuration}}</td>
</tr>
</table>
js file
$scope.data=function()
{
$scope.details=$filter("filter")($scope.details,$scope.searchstr);
}
I have tried like above but only for the first time its displaying
I got it.I just changed the slight logic in js file.I am posting it as it may be helpful to somebody else.
First i just copied the $scope.details to d and filtered with the temp variable d.
$scope.searchstr="";
var d=$scope.details;
$scope.data=function()
{
$scope.details=$filter("filter")(d,$scope.searchstr);
}
You don't need the $scope.data function. You use the builtin angular filter like
<tr ng-repeat="d in details | filter:searchstr" ng-if="$index%3==0">