I have a json file, that will eventually be called from a server with an API. Currently am just calling from an object.
I am creating an HTML file with a table navigation, where each header element also has a search box and submit button.
I would like the table to be blank on start, and when one of the header values is searched (id, nickname, email, etc), the json value that contains at least part of that search will populate the table.
I am new to the Angular syntax and am trying to get an idea of how this would even work.
(function() {
var app = angular.module('tool', []);
app.controller('searchController', function() {
this.info = data.people;
this.findId = function(idInput) {
angular.forEach(that.id, function(value, key) {
if (value.contains(idInput)) {
// not sure what to put here.
}
});
};
});
var data = {
"people": [{
"id": "2245231",
"nickname": "heyyman",
"email": "info#gmail.net",
"lastIp": "127.0.0.1",
"regIp": "127.0.0.1",
}, {
"id": "2245232",
"nickname": "heyyman2",
"email": "info2#gmail.net",
"lastIp": "127.0.0.2",
"regIp": "127.0.0.2",
}
};
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<main>
<table ng-controller="searchController as search">
<thead>
<tr id="tableNavigation">
<td></td>
<td>ID
<input type="text" ng-model="idInput">
<input type="submit" ng-click="findId(idInput)">
</td>
<td>Nickname</td>
<td>Login / Email</td>
<td>Last IP</td>
<td>Registration IP</td>
</tr>
</thead>
<tbody id="tableCanvas">
<tr ng-repeat="people in search.info" ng-class-even="'even'">
<td></td>
<td>{{people.id}}</td>
<td>{{people.nickname}}</td>
<td>{{people.email}}</td>
<td>{{people.lastIp}}</td>
<td>{{people.regIp}}</td>
</tr>
</tbody>
</table>
</main>
So far, this is what I've done. Also, I've linked a JSFiddle.
http://jsfiddle.net/ho00cLkk/
Please let me know if what I am asking is confusing or not clear.
It seems to me that your question consists of two separate parts:
How do I filter the displayed items?
How do I hide all results until the first search is made?
As the second question seems uninteresting to me (many possible solutions, no general problem), I'll focus on the first one. You might want to read the documentation: https://docs.angularjs.org/api/ng/filter/filter . Then it's pretty simple and you don't even need any submit buttons:
<table>
<thead>
<tr>
<th>
Name
<input type="text" ng-model="searchProps.name">
</th>
...
</tr>
</thead>
<tbody>
<tr ng-repeat="person in people | filter:searchProps">
...
</tr>
</tbody>
</table>
(where people is your search.info). Table rows will be automatically filtered to contain only the items with properties partially matching the values in searchProps.
CodePen example: http://codepen.io/anon/pen/jEEZaa
Related
I have 2 pages and 2 tables, in page 1(table 1) I want to send selected rows to page 2(table 2) where in table 2 I show the selected rows
This is the first table in page 1:
<table class="src-table">
<tr>
<th>Select</th>
<th>Firstname</th>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>Jill</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>Eve</td>
</tr>
</table>
<br>
<input type="button" value="Submit" id="submit">
Like image below
This is the second table in page 2:
<table class="target-table">
<tr>
<th>Select</th>
<th>Firstname</th>
</tr>
</table>
Like image below
If you really need this. You can use localStorage.
localStorage not working in the sandbox. But you can use it your application as well.
run storeItems when you need to save selected to store (for example on element select)
run appendStoredToAnouther on window event window.onload on page with the target table
function storeItems() {
const selectedItems = document.querySelectorAll("#first-table .selected");
const selectedHtml = nodeListToString(selectedItems);
localStorage.add('selectedTableItems', selectedHtml);
}
function nodeListToString(nodeList) {
let string = '';
nodeList.forEach(function(node) {
string += node.outerHTML;
});
return string;
}
function appendStoredToAnouther() {
const itemsHtml = window.localStorage.get('selectedTableItems');
const targetTable = document.getElementById('target-table');
targetTable.innerHTML = itemsHtml + targetTable.innerHTML;
}
<table id="first-table">
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr>
<td>2</td>
<td>Not Selected</td>
<td>Item</td>
</tr>
</table>
<button type="button" onclick="storeItems()">Send to anouther</button>
<button type="button" onclick="appendStoredToAnouther()">Append stored to anouther</button>
<table id="target-table">
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr>
<td>2</td>
<td>Not Selected</td>
<td>Item</td>
</tr>
</table>
Below I demonstrated how some lines could be carried over from one table to another one on a following page. However, since both pages are probably hosted on the same server it is in most cases more practical to first collect some unique identifiers for the selected table records, transmit them to the next page and then get the actual table contents from the original data source again (in many cases a database table or view). This approach will also make your page safer against unauthorised injections.
In case that the tables are to be shown in two consecutive pages you can do the following:
// shortcut for utility function querySelectorAll():
const qsa=(s,o)=>[...(o||document)['querySelectorAll'](s)];
const log=qsa('#log')[0];
qsa('#submit')[0].addEventListener('click',function(){
var dat="tbl="+JSON.stringify(
qsa('tr',qsa('.src-table')[0]).filter(tr=>qsa('input:checked',tr).length)
.map(tr=>qsa('td',tr).slice(1)
.map(td=>td.innerHTML))
);
log.innerHTML+="<hr>dat:"+dat;
log.innerHTML+="\nwindow.location=\"page2.html?\"+encodeURIComponent(dat)";
// this second part would be placed in the onload section if the next page:
log.innerHTML+='var dat=window.location.search.substr(1)'
var args=dat.split("&").reduce((a,v)=>{
var t=v.split('=');
a[t[0]]=JSON.parse(decodeURIComponent(t[1]));
return a;},
{}
);
qsa('.trg-table')[0].innerHTML+=
args.tbl.map((r,i)=>'<tr><td>'+(i+1)+'</td><td>'+r.join('</td><td>')+'</td></tr>').join('');
})
<h2>page one</h2>
<table class="src-table">
<tr><th>Select</th><th>Firstname</th><th>Familyname</th></tr>
<tr><td><input type="checkbox"></td><td>Jill</td><td>Jones</td></tr>
<tr><td><input type="checkbox"></td><td>Eve</td><td>Adams</td></tr>
</table>
<br>
<input type="button" value="Submit" id="submit">
<h2>this would be the second page</h2>
<table class="trg-table">
<tr><th>no</th><th>Firstname</th><th>Familyname</th></tr>
</table>
<pre id="log"></pre>
As this is a sandbox the last lines had to modified a bit. In your page you should actually redirect your page with the window.location assignment.
On the second page you then need to read the passed information from window.location.search and use that information to append it to your table there.
A table is to be created from taking values from json file where data should be displayed as radio buttons in columns(one value to be selected from each column). The selected values from each column should be displayed below the table. Could anyone please help me with this?
My Json is
"employees":[
{"firstName":"John", "lastName":"Doe" , "manager":"paul", "domain":"digital"},
{"firstName":"Anna", "lastName":"Smith", "manager":"Abraham","domain":"mechanics"},
{"firstName":"Peter", "lastName":"Jones", "manager":"Jonathan","domain":"physics"}
{"firstName":"Anna", "lastName":"carter", "manager":"Bravo","domain":"chemistry"}
{"firstName":"Watson", "lastName":"Daniel", "manager":"Pollock","domain":"biology"}
{"firstName":"James", "lastName":"Smith", "manager":"Tait","domain":"analogy"}
{"firstName":"Angel", "lastName":"Queen", "manager":"Mcgrath","domain":"mathematics"}
{"firstName":"Sana", "lastName":"Elizebeth", "manager":"Waugh", "domain":"english"}
]
Please note that you have to apply the returned value from inside the Observable. What you try is this in the end:
const employeesJSON = Observable<any>;
And then Angular tries to access the contained objects, which is not possible as an Observable<any> is not the same as an array of plain objects.
Try this solution instead:
protected employees: Array<Object>;
ngOnInit() {
this.http.get("api.myjson.com/bins/tshu8").pipe(map(res => {
if (res) {
this.employees = res.json();
}
}));
}
So here is my solution. Please note that this is only a suggestion:
We put 2 tables in the UI.
The first table is for input-purposes. Note the ngFor-loop in the tr of the table-body. Here you loop through your list of objects and with every new object a new tr gets created. Each tr contains 4 td's which contain input-elements of type "radio" and a span-element showing the content of the field. Controlled by the "name"-attribute each COLUMN builds a single unit. This means that a click on a radio button has only an effect on the state of every other radio button in the same column, not row! And each input-element's value gets set in order to provide it when the element is clicked.
The ngModel-attribute is the most important part of the story. Whenever you click a radio-button ngModel pushes the value of the clicked element into the connected variable. The second table then gets directly updated whenever ngModel updates one of the 4 variables (Interpolation). That's why a click on one radio button then prompts its value in the corresponding field of the "Output Table".
Hope this helps.
In your HTML-File:
<h1>Input Table</h1>
<div class="row" id="inputTableSector">
<table id="inputTable" class="table table-striped">
<thead>
<tr>
<td>first name</td>
<td>last name</td>
<td>manager</td>
<td>domain</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees;">
<td><input type="radio" name="firstName" [value]="employee.firstName" [(ngModel)]="currentFirstName"><span>{{employee?.firstName}}</span></td>
<td><input type="radio" name="lastName" [value]="employee.lastName" [(ngModel)]="currentLastName"><span>{{employee?.lastName}}</span></td>
<td><input type="radio" name="manager" [value]="employee.manager" [(ngModel)]="currentManager"><span>{{employee?.manager}}</span></td>
<td><input type="radio" name="domain" [value]="employee.domain" [(ngModel)]="currentDomain"><span>{{employee?.domain}}</span></td>
</tr>
</tbody>
</table>
</div>
<hr>
<h1>Output Table</h1>
<div class="row" id="outputTableSector">
<table id="outputTable" class="table table-striped">
<thead>
<tr>
<td>first name</td>
<td>last name</td>
<td>manager</td>
<td>domain</td>
</tr>
</thead>
<tbody>
<tr>
<td>{{currentFirstName}}</td>
<td>{{currentLastName}}</td>
<td>{{currentManager}}</td>
<td>{{currentDomain}}</td>
</tr>
</tbody>
</table>
</div>
In your TypeScript-File:
protected employees: Array<Object>;
protected currentLastName: string;
protected currentFirstName: string;
protected currentManager: string;
protected currentDomain: string;
ngOnInit(): void {
const employeesJSON = '[' +
'{"firstName":"John","lastName":"Doe","manager":"paul","domain":"digital"},' +
'{"firstName":"Anna","lastName":"Smith","manager":"Abraham","domain":"mechanics"},' +
'{"firstName":"Peter","lastName":"Jones","manager":"Jonathan","domain":"physics"},' +
'{"firstName":"Anna","lastName":"carter","manager":"Bravo","domain":"chemistry"},' +
'{"firstName":"Watson","lastName":"Daniel","manager":"Pollock","domain":"biology"},' +
'{"firstName":"James","lastName":"Smith","manager":"Tait","domain":"analogy"},' +
'{"firstName":"Angel","lastName":"Queen","manager":"Mcgrath","domain":"mathematics"},' +
'{"firstName":"Sana","lastName":"Elizebeth","manager":"Waugh","domain":"english"}' +
']';
this.employees = JSON.parse(employeesJSON);
}
I am completely new to angular, I need to create a table. The data array is as-follows:-
data = [{rollno: 1,name: 'abc',subject: 'maths'},
{rollno: 4,name: 'xyz',subject: 'history'},
{rollno: 2,name: 'pqr',subject: 'history'}
];
I want to create a table with some summary rows based on this data and then when I click the expand button the sub-rows should appear beneath that summary-row indicating the actual data.
For example:-
Expand/Collapse | No of Students | Subject
click here 1 Maths
click here 2 History
When I toggle the expand/collapse button on the second row for example I want actual rows to appear like this beneath it:-
Expand/Collapse | No of Students | Subject
click here 1 Maths
click here 2 History
RollNo | StudentName
4 xyz
2 pqr
How Can I achieve this?
1) Grouping the data by subject
First you need to group the data by subject and then count the items in each group.
You can use the angular.filter module's groupBy filter to do this.
1a) Add a dependency on that module as follows:
var app = angular.module("yourModuleName", ["angular.filter"]);
1b) You can then use the groupBy filter in an ng-repeat directive on a <tbody> tag like this:
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
1c) You're now dealing with the data in the format below. This is an object of key/value pairs where "maths" and "history" are both keys, and the arrays are the values
{
"maths": [
{
"rollno": 1,
"name": "abc",
"subject": "maths",
}
],
"history": [
{
"rollno": 4,
"name": "xyz",
"subject": "history",
},
{
"rollno": 2,
"name": "pqr",
"subject": "history",
}
]
}
2) Displaying the grouped data and counting the items in each group
Use key and value to display the grouped data in a table as follows:
<table>
<thead>
<tr>
<th>Subject</th>
<th>Number of Students</th>
<th>Expand/Collapse</th>
</tr>
</thead>
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
<tr>
<td>{{ key }}</td>
<td>{{ value.length }}</td>
<td>
<button>
Expand/Collapse
</button>
</td>
</tr>
<tr>
<td colspan="3">
<table>
<thead>
<tr>
<th>Roll Number</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in value">
<td>{{ student.rollno }}</td>
<td>{{ student.name }}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
Note the extra <tr> and nested table with another ng-repeat for displaying the student data. Currently all nested student data will display, the next step is to conditionally show/hide the nested tables based on which expand/collapse button was clicked.
3) Showing/Hiding the nested student data
3a) Add an ng-click directive on the button so that it passes in the key to an onExpandClicked function on your controller:
<button ng-click="onExpandClicked(key)">
Expand/Collapse
</button>
3b) Create the onExpandClicked function in your controller:
$scope.onExpandClicked = function(name){
$scope.expanded = ($scope.expanded !== name) ? name : "";
}
This sets a value on the $scope that can be used in the view to decide whether to show/hide a section of student data. The key is passed into the function as the name parameter and $scope.expanded will either be set to name or reset to "" depending on whether the passed in name is the same as the current $scope.expanded value or not.
3c) Finally, use the $scope.expanded variable in an ng-if directive on the second <tr> tag of <tbody> to show or hide the nested student data:
<table>
<thead>
<tr>
<!-- Omitted for brevity -->
</tr>
</thead>
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
<tr>
<!-- Omitted for brevity -->
</tr>
<tr ng-if="expanded === key">
<!--
Omitted for brevity
-->
</tr>
</tbody>
</table>
Demo
CodePen: How to show/hide grouped data created by the angular.filter module's groupBy filter
Design a table with html and iterate through your data object with ng-repeat loop to display the data.
ngRepeat
W3Schools has some basic examples on how to display tables with AngularJS
Angular Tables
First you should replace the actual table by a div structure, because it is not possible to mix two kinds of table like you are planning (when I get you right).
You could toggle every row with a ng-click with the corresponding expanded content like this (pseudo code, I hope the idea gets clear):
<div class="row-header">
<span>RollNo</span>
<span>StudentName</span>
</div>
<div class="row-content" ng-if="!row_4_expanded" ng-click="row_4_expanded = !row_4_expanded">
<span>4</span>
<span>xyz</span>
</div>
<div ng-if="row_4_expanded">
<div class="row-expanded-header">
<span>No of Students</span>
<span>Subject</span>
</div>
<div class="row-expanded-content">
<span>1</span>
<span>Math</span>
</div>
<div class="row-expanded-content">
<span>2</span>
<span>History</span>
</div>
</div>
<div class="row-content" ng-if="!row_2_expanded" ng-if="row_2_expanded" ng-click="row_2_expanded = !row_2_expanded">
<span>2</span>
<span>pqr</span>
</div>
<div ng-if="row_2_expanded">
<div class="row-expanded-header">
<span>No of Students</span>
<span>Subject</span>
</div>
<div class="row-expanded-content">
<span>1</span>
<span>Math</span>
</div>
<div class="row-expanded-content">
<span>2</span>
<span>History</span>
</div>
</div>
When you now click on a row, it toggle presens with the corresponding expanded one.
Here is a working example which resolves your problem.
var indexCtrl = ['$scope', function($scope){
$scope.num = 0;
$scope.test = [{rollno: 1,name: 'abc',subject: 'maths'},
{rollno: 4,name: 'xyz',subject: 'history'},
{rollno: 2,name: 'pqr',subject: 'history'}
];
$scope.changeShow = function(index){
$scope.num = index;
};
}];
<!DOCTYPE html>
<html ng-app>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller='indexCtrl'>
<table>
<tr>
<td>Expand/Collapse</td>
<td>No of Students</td>
<td>Subject</td>
</tr>
<tr ng-repeat='i in test' ng-class="num == $index ? red : none">{{}}
<td ng-click='changeShow($index)'>click here</td>
<td>{{$index +1}}</td>
<td >{{i.subject}}</td>
</tr>
</table>
<table>
<tr>
<td>RollNo</td>
<td>StudentName</td>
</tr>
<tr ng-repeat='i in test'>
<td ng-show='num == $index'>{{i.rollno}}</td>
<td ng-show='num == $index'>{{i.name}}</td>
</tr>
</table>
<style>
table tr td{
border: 1px solid black
}
</style>
</body>
</html>
Hope it helps you.
Need to display a text area for displaying the details, once the details button is clicked.
It gives an output similar to this table structure
<table>
<th>ID</th>
<th>Title</th>
<tr *ngFor = "let row of indexes">
<td *ngFor = "let id of row>{{id}}</td>
<td><button (click)="showDetails()">DETAILS</td>
</tr>
</table>
<tr *ngIf="isClicked"><td>{{id.details}}</td></tr> ---> This needs to be displayed very next to the row where the button is clicked.
In angular 1.x we can achieve this using ng-repeat-start/ng-repeat-end. In angular 2 I have an clue of including </template ngFor let-row [ngForOf]="indexes">But not sure where to include this. Please suggest.
If i understand your problem correctly, you want to insert two table-rows for each of your indexes.
So if you have something like this in your component class:
rows: any[] = [
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false }
];
toggleDetails(row: any) {
row.detailsVisible = !row.detailsVisible;
}
You can do it by using ng-container:
<table>
<ng-container *ngFor="let row of rows">
<tr>
<td (click)="toggleDetails(row)">show details</td>
</tr>
<tr *ngIf="row.detailsVisible">
<td>only visible after click on details cell</td>
</tr>
</ng-container>
</table>
Hope this helps.
I am writing my first non-tutorial angular.js web app. I am using two smart-tables and checklist-model. Here is the first one that uses a st-safe-src of all_types that is an array of json objects that look like this ...
[
{
"_id": "56417a9603aba26400fcdb6a",
"type": "Beer",
"__v": 0
},
{
"_id": "56456140cb5c3e8f004f4c49",
"type": "Skiing",
"__v": 0
},
...
Here is the html for the table I use to display this data:
<table st-table="displayedCollection" st-safe-src="all_types" class="table table-striped">
<thead>
<tr>
<th st-sort="type">Types</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in displayedCollection">
<td><input type="checkbox" checklist-model="vendor.types" checklist-value="x.type">{{x.type}}</td>
</tr>
<tr><td>id ({{curid}}) {{vendor.types}}</td></tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
This table looks like this when I load data into it. The checkboxes get checked to match the data from my model.
But when I try to do the same thing in a second smart table with more complete json objects that look like this ...
[
{
"_id": "569f047dd90a7874025b344e",
"product_title": "Plugs",
"product_img_001": "p001.jpg",
"product_img_002": "p002.jpg",
"product_vid_001": "bp.mov",
"__v": 0,
"product_sizes": [
"Large",
"Med.",
"Small",
"10.5"
],
"product_types": [
"Running Shoe"
]
},
{
"_id": "569f3958b108a7d70201b89a",
"product_title": "Back Scratcher",
"product_img_001": "http://itchy.png",
"product_img_002": "http://relief-at-last.png",
"product_vid_001": "my-itch",
"__v": 0,
"product_sizes": [
"Large"
],
"product_types": [
"Rocks"
]
}
]
Here's the html I am using to display this data in a smart table:
<table st-table="prodCollection" st-safe-src="all_products" class="table table-striped">
<thead>
<tr>
<th st-sort="type">Products</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in prodCollection">
<td><input type="checkbox" checklist-model="vendor.products" checklist-value="x">{{x.product_title}}</td>
</tr>
<tr><td>{{vendor.products}}</td></tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
This table looks like this when I load data into it:
I had hoped that the checkbox would be checked, but they do not get checked.
If make this change ...
<td><input type="checkbox" checklist-model="vendor.products" checklist-value="x.product_title">{{x.product_title}}</td>
... the correct checkboxes get checked but just the product's title will be posted. What do I need to do to get the checkboxs to display checked and be able to post the whole product data?
I have added a plunker example: http://plnkr.co/edit/aPCEBV5e9Pb5np9iaY2l
You can use as checklist-value the id instead of the Object as say section on doc Array of objects (pick id)
<input type="checkbox" checklist-model="vendor.products" checklist-value="x._id">
Or if you need use the Object as selected products (checklist-value="x") you need use checklist-comparator because in your plunker the selected array and full product list don't have the same references. The comparator must match the equal objects by _id. Try this
<input type="checkbox" checklist-comparator="._id" checklist-model="vendor.products" checklist-value="prod" />
There is an option called ng-true-value="1". This will check with the values of the ng-model. It will work like if(ng-model(Name) == 1). Try this out.
<input type="checkbox" name="checkbox_demo_4" id="checkbox_demo_4"
ng-model="temp.taxable_type"
ng-true-value="1"
/>
In this, 1 is the true value that I stored in the DB. So it is checking automatically if the ng-model value contains 1.
All the best.
After reading carefully your question, and supossing that you want the same behaviour as the first smart table, the problem I think you had can be solved by
setting the product_title as the checklist-value of the input field, something like this :
<input type="checkbox" checklist-model="vendor.products" checklist-value="prod.product_title" />
Here the plunkr associated
http://plnkr.co/edit/5an1Fx?p=preview