Ember.js {{#each}} table searching, partial viewing and sorting - javascript

Thanks in advance for the help. I am pretty new to ember.js, but have setup a basic site that has a few pages with tables that loop through my data. These tables are going to be over 100 rows and have images in each row, so I would like some sort pagination or a partial view so as they scroll it loads (preferred solution). I also want the ability to search the table.
I have used datatables.js before, and that would solve most of my problems, but I am not sure if that would be the best solution with ember because it is not able to just grab the table like it normally does.
I am kind of looking for a direction and what other people may think is the best solution. Any links, or plugins, or example code or whatever would be very helpful. Here are some snippets of my code, just a super basic site:
Index Table
<table class="prdtTable table-striped table-bordered table-hover">
<thead>
<tr>
<th>Title</th>
<th>Demo</th>
<th class="tdLeft">Price</th>
<th>Description</th>
<th># of Columns</th>
</tr>
</thead>
<tbody>
{{#each}}
<tr>
<td class="noWrap">{{#link-to 'theme' this}}{{title}}{{/link-to}}</td>
<td>{{#link-to 'theme' this}}<img {{bind-attr src="image"}} \>{{/link-to}}</td>
<td class="tdCenter">{{price}}</a></td>
<td>{{description}}</td>
<td class="tdCenter">{{columns}}</td>
</tr>
{{/each}}
</tbody>
</table>
Free Page Table
<table class="prdtTable table-striped table-bordered table-hover">
<thead>
<tr>
<th>Title</th>
<th>Demo</th>
<th>Description</th>
<th># of Columns</th>
</tr>
</thead>
<tbody>
{{#each}}
{{#if free}}
<tr>
<td class="noWrap">{{#link-to 'theme' title}}{{title}}{{/link-to}}</td>
<td>{{#link-to 'theme' this}}<img {{bind-attr src="image"}} \>{{/link-to}}</td>
<td>{{description}}</td>
<td class="tdCenter">{{columns}}</td>
</tr>
{{/if}}
{{/each}}
</tbody>
</table>
Index Route
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.PRODUCTS;
}
});
Product Route
App.ProductRoute = Ember.Route.extend({
model: function(params) {
return App.PRODUCTS.findBy('title', params.title);
}
});
Product Array
App.PRODUCTS = [
{
title: 'Title 1',
price: '$0',
free: true,
description: 'long description',
columns: 1,
image: 'images/image.jpg'
},
{
title: 'Title 2',
price: '$0',
free: true,
description: 'Description 2',
columns: 3,
image: 'images/image.jpg'
},
...
Thank you very much for the help! I really appreciate it!
David B

Related

How can I make Vue Sortable work on table?

I have an issue with this library. First of all, to test the library, I made a simple example work with ul and li tags. It was straightforward. Then, I needed to do a table, and when converting my example to a table it didn't work.
Table is showed but I cannot move any row.
I'm using the cdn way.
I think is something what I missing.
html
<div id='app-example-drag' >
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Sport</th>
</tr>
</thead>
<draggable v-model="list" tag="tbody">
<tr v-for="item in list" :key="item.name">
<td scope="row">{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.sport }}</td>
</tr>
</draggable>
</table>
</div>
js
Vue.component('draggable',vuedraggable);
applicationExample = new Vue({
el: '#app-example-drag',
display: "Table",
order: 8,
data() {
return {
list: [
{ id: 1, name: "Abby", sport: "basket" },
{ id: 2, name: "Brooke", sport: "foot" },
{ id: 3, name: "Courtenay", sport: "volley" },
{ id: 4, name: "David", sport: "rugby" }
]
};
}
});
https://jsfiddle.net/0Luhd694/3/
Thanks in advance
I just ran into this same problem. I think it has something to with recent version. Replace the draggable element with a tbody and make is='draggable'.
<div id='app-example-drag' >
<table class='table'>
<thead>
<tr><th scope='col'>description</th></tr>
</thead>
<tbody tag='tbody' v-model='lista1' is='draggable' group='items'>
<tr v-for='item in lista1' :key='item.id'>
<td scope='row'>{{item.description}}</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/oqf64kdx/

Javascript pivoting data and generating dynamic table from object

I am receiving a data object of results that have been pivoted and I am unable to change the structure that is returned to me. My only option at this point is to manipulate the data to parse it how I need to.
Here is what my results look like:
// Object from service
let dataObj = [{
CostCenter: '12345',
HasLevel1Access: 'No',
HasLevel2Access: 'No',
HasLevel3Access: 'Yes',
FirstName: 'Bob',
LastName: 'Jones',
UID: '12345'
},
{
CostCenter: '555',
HasLevel1Access: 'Yes',
HasLevel2Access: 'No',
HasLevel3Access: 'Yes',
FirstName: 'Tommy',
LastName: 'C',
UID: '6789'
},
{
CostCenter: '51112',
HasLevel1Access: 'Yes',
HasLevel2Access: 'No',
HasLevel3Access: 'Yes',
FirstName: 'Smithson',
LastName: 'J',
UID: '8888'
}];
From this data, I need to make a table. The trick here is that I need to use some of the property names as the column headers but exclude others.
My table is very basic, it contains the persons name and then all of the dynamic column names:
<table class="table table-condensed" border="1">
<thead>
<tr>
<th>Employee Name</th>
<th *ngFor="let m of columnNames">{{ m }}</th>
</tr>
</thead>
<tbody>
<!-- Example Record 0 -->
<tr>
<td>Bob Jones</td>
<td>No</td>
<td>No</td>
<td>Yes</td>
</tr>
<!-- Example Record 0 -->
</tbody>
</table>
The first thing I did in order to get the column names is create an array of the IgnoreColumns which is the property names I wan't to exclude from being its own column in the table.
// Hold all of the column names
private columnNames = [];
// Ignore these columns, they dont get printed as headers
private ignoreColumns = ['CostCenter', 'FirstName', 'LastName', 'UID'];
I then looped over the first record in the result set and pushed all of the property names to an array that are not in our ignoreColumns array. This leaves me with a unique array of the dynamic columns.
// Find all the keys in our first result
for (var p in dataObj[0]) {
// Get the key names
if (dataObj[0].hasOwnProperty(p)) {
// If this key name doesnt already exist in the array AND its not in our ignored list push them to our array
if (!_.includes(this.columnNames, p) && !_.includes(this.ignoreColumns, p)) {
this.columnNames.push(p);
}
}
}
I am stuck at this point. I was able to create the table structure with the headings in place but I don't know how I should proceed to get the data aligned under the correct columns in the table.
This is what I am going for in my final output:
<table class="table table-condensed" border="1">
<thead>
<tr>
<th>Individual</th>
<th>HasLevel1Access</th>
<th>HasLevel2Access</th>
<th>HasLevel3Access</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob Jones</td>
<td>No</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Tommy C</td>
<td>Yes</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Smithson J</td>
<td>Yes</td>
<td>No</td>
<td>Yes</td>
</tr>
</tbody>
</table>
Here is a plunkr of where I am at: http://plnkr.co/edit/9WygBXsQaDaxTMudb7ZB?p=preview
Any advice on how to approach this?
What about
<tr *ngFor="let item of dataObj">
<td>{{item.FirstName}} {{item.LastName}}</td>
<td *ngFor="let m of columnNames">{{item[m]}}</td>
</tr>
Working demo
You meant something like this?
<tr *ngFor="let data of dataObj">
<td>{{data.FirstName+" "+data.LastName}}</td>
<td>{{data.HasLevel1Access}}</td>
<td>{{data.HasLevel2Access}}</td>
<td>{{data.HasLevel3Access}}</td>
</tr>
http://plnkr.co/edit/EHsVFaiaK1UUcWrbm6zX?p=preview

ngTable dynamic with columns doesn't show column titles

In this plunk I have two tables, one regular ng-table and an ng-table-dynamic with columns. Both point to the same data.
The second table doesn't show column titles, how to fix that?
HTML
<br/> Table 1
<table ng-table="tableParams" class="table table-bordered table-hover">
<tbody>
<tr ng-repeat="u in data" ng-dblclick="alert('double click')">
<td title="'User ID'">{{ u.uid }}</td>
<td title="'Group'">{{ u.ugr }}</td>
</tr>
</tbody>
</table>
<br/> Table 2
<table ng-table-dynamic="tableParams with cols" class="table table-bordered table-hover">
<tr ng-repeat="row in data">
<td title="col.nm" ng-repeat="col in cols">{{row[col.nm]}}</td>
</tr>
</table>
Javascript:
var app = angular.module('app', ['ngTable']);
app.controller('myCtl', function($scope,NgTableParams) {
$scope.cols = [ {nm:'uid'}, {nm:'ugr'} ];
$scope.data = [
{ uid: 'User 1',ugr: 'Group 1'},
{ uid: 'User 2', ugr: 'Group 2'}
];
$scope.tableParams = new NgTableParams({dataset: $scope.data});
});
You're missing the title property within your cols:
$scope.cols = [ {nm:'uid', title: 'User ID'}, {nm:'ugr', title: 'Group ID'} ];
See this plunkr.

Data Table hangs sporadically (No data available in table) when data sorted or loaded with Knockout binding

I am binding data to a table using Knockout JS and the JQuery/Bootstrap based; Data Table API. The table becomes unresponsive sporadically when sorted or loaded. There are no errors in the log.
It also shows 0 of 0 data as illustrated in the screenshot below:
I have seen similar errors/issues but could not get a solutions for them, E.g. This post:
Code:
function viewModel(){
var self = this;
self.Data = ko.observableArray([]);
$.getJSON('https://restcountries.eu/rest/v1/all', function(data){
self.Data(data);
});
}
ko.applyBindings(viewModel());
$(document).ready(function() {
$('#example').dataTable();
});
HTML:
<div class="table-responsive">
<table id="example" cellspacing="0"
class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th scope="col">Country</th>
<th scope="col">Capital</th>
<th scope="col">Population</th>
<th scope="col">Region</th>
</tr>
</thead>
<tfoot>
<tr>
<th scope="col">Country</th>
<th scope="col">Capital</th>
<th scope="col">Population</th>
<th scope="col">Region</th>
</tr>
</tfoot>
<tbody data-bind="foreach: Data">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: capital"></td>
<td data-bind="text: population"></td>
<td data-bind="text: region"></td>
</tr>
</tbody>
</table>
</div>
Here is a full working example (JSFiddle) utilizing a REST API so that the exact problem is accurately replicated:
I think the problem with your example may be with how you're dealing with your data when you get it back from the API call.
I've put together a quick example that achieves what I think you're trying to achieve and the sorting and searching work quickly for me.
When I get the JSON data back from the API, I use the Knockout arrayMap utility function to create an array of "Country" objects that have observable properties that I have mapped the JSON data to. I've bound the table to my observableArray of Country objects.
Initialising the data table in the same way you have works fine for me in this case.
The full working solution is here: http://plnkr.co/edit/eroIox6zqBFOVnf86Mdk?p=preview
script.js
var ViewModel = function(jsonData) {
var countries = ko.utils.arrayMap(jsonData, function(item) {
return new Country(item)
});
this.Countries = ko.observableArray(countries);
};
var Country = function(jsonItem) {
this.Name = ko.observable(jsonItem.name);
this.Capital = ko.observable(jsonItem.capital);
this.Population = ko.observable(jsonItem.population);
this.Region = ko.observable(jsonItem.region);
};
window.onload = function() {
$.getJSON('https://restcountries.eu/rest/v1/all', function(data) {
ko.applyBindings(new ViewModel(data));
$("#example").dataTable();
});
}
index.html
<table id="example" cellspacing="0" class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th scope="col">Country</th>
<th scope="col">Capital</th>
<th scope="col">Population</th>
<th scope="col">Region</th>
</tr>
</thead>
<tfoot>
<tr>
<th scope="col">Country</th>
<th scope="col">Capital</th>
<th scope="col">Population</th>
<th scope="col">Region</th>
</tr>
</tfoot>
<tbody data-bind="foreach: Countries">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: Capital"></td>
<td data-bind="text: Population"></td>
<td data-bind="text: Region"></td>
</tr>
</tbody>
</table>
Tables are fairly slow to render within Knockout, and if your table is based on a computed, I could see how you could have some issues with redrawing time. But that's not happening here.
Apart from loading the data and binding it to the table rows, there's no data manipulation going on in your viewmodel. All the data manipulation is done by the dataTable plug-in, which you initialize with a single jQuery call. Properly, that should be done within a binding handler. You also need to know what is going on within the plug-in when it sorts, filters, or whatever it does, because you may need to mediate those changes back to your observableArray within your binding handler.
Bottom line: you need a binding handler for the dataTable. There may be one already written; I haven't Googled for it. Give that a try.

Repeating a set of <td>s in the same table row using AngularJS

I want to create a table that, in its header, contains the names of each player for two teams in each . The scope has a variable teams which is a list of Team objects, each one having list of Player objects as an attribute.
This is turning out to be way harder than I expected.
<table>
<thead>
<tr>
<div ng-repeat="team in teams">
<th ng-repeat="player in team.players">{[ player.name ]}</th>
<th>Partial Score</th>
<th>Total Score</th>
</div>
</tr>
</thead>
</table>
is the easiest thing I have in mind - but this doesn't work. can't be placed inside a according to W3C (from what I understand), so the browser just takes the div and places it outside the table.
I've tried directives as well - but these don't help. This is an example of my directive file.
<th ng-repeat="player in team.players">{[ player.name ]}</th>
<th>Partial Score</th>
<th>Total Score</th>
For example, if I have a <player-initials> directive with replace: 'false', the browser expels the non-<th> element out of the table. Setting replace: 'true', fixes that, but then I get an error because there's more than one root element in the directive. I can't wrap the elements in the directive with anything because that would result in invalid HTML (as above).
I've tried all combinations of element and attribute directives and replace, nothing gives me what I want. Any help?
Created a fiddle here.
Hope this is what you are looking for, if not let me know the format that you are looking for. Your HTML should look something like below.
<table ng-repeat="team in teams">
<tr>
<td colspan="3">
{{team.name}}
</td>
</tr>
<tr>
<td>Player Name</td>
<td>Partial Score</td>
<td>Total Score</td>
</tr>
<tr ng-repeat="player in team.players">
<td>{{player.name}}</td>
<td>{{player.PScore}}</td>
<td>{{player.TScore}}</td>
</tr>
</table>
You should add a method to your scope that do the logic for you.
Something like this should work:
$scope.getAllPlayers = function() {
var players = [];
this.teams.forEach(function(team){
players = players.concat(team.players);
});
return players;
}
Then in your dom:
<table>
<thead>
<tr>
<th ng-repeat="player in getAllPlayers()">{{ player.name }}</th>
<th>Partial Score</th>
<th>Total Score</th>
</tr>
</thead>
</table>
I'm not using Angular so I may be wrong, but I'm pretty sure this is the way to go. This kind of logic have to be in your controller, as simple is a nested loop.
EDIT: Referring to the comments, you could make a function that return your header cells the way you want them
$scope.getHeaderCells = function() {
var cells = [];
this.teams.forEach(function(team){
cells = cells.concat(team.players.map(function(player){
return { team: team, value: player.name };
})).concat([
{team: team, value: 'Partial Score'},
{team: team, value: 'Total Score'}
]);
});
console.log(cells);
return cells;
}
with
<th ng-repeat="cell in getHeaderCells()">{{ cell.value }}</th>
here is a fiddle
I figured it out using the ng-repeat-start and ng-repeat-end directives. I really didn't want to write functions to generate HTML for me, because that's why I switched to Angular in the first place.
Here's a screenshot: http://i.imgur.com/e5LBvQB.png.
This is the (stripped-down to relevant part) code just for the header, everything else is similar. The only 'hacky' part I don't like is the ng-show attributes for B and T to reverse it properly, but it works for me right now. I think I can get clean it up with directives.
<table class="table table-condensed table-striped table-hover table-responsive">
<thead>
<tr>
<th ng-repeat-start="team in game.getTeams()" ng-hide="true"></th>
<th ng-repeat="player in team.getPlayers()" ng-class="$parent.$first && 'text-info' || 'text-danger'">
<u>
{[ player.name | truncate ]}
</u>
</th>
<th ng-repeat-end ng-show="$first">
#
</th>
</tr>
</thead>
</table>

Categories