how to set an int value to a string in angular - javascript

I have a value in a table that is currently an int and I want to pass those variables to a table on a webpage but I want to change those values to actual names instead of numbers is there a way to do that by setting a scope in the controller and then pushing that string to the html?
for example:
This would be the html code for the table row and head and then the table data.
<thead>
<tr>
<th>Foo</th>
</tr>
</thead>
<tbody>
<tr ng-repeat= "x in names">
<td>{{ x.bar }}</td>
</tr>
</tbody>
I don't know the syntax for creating a scope for this in angular and then pushing that to the html or where it would go.
The table currently shows a 1 in the place of bar. I want it to actually say bar but register as 1. I don't know if that makes any sense.
I've seen some where they put
app.filter('thisfilter', function() {
return function (bar)
if((bar) || bar < 1){
return status_options;
}else{
var lastdigit = bar % 100;
if(lastdigit === 1){
return 'audi'
} else if(lastdigit === 2){
return 'bmw'
} else if(lastdigit === 3){
return 'mercedes'
} else if(last digit === 4){
return 'lexus'
}
}
}
and then they plug that back into the html
<td>{{ x.foo | thisfilter }}</td>

Here is a very contrived example to show the use of a filter to display text in place of a numeric value.
angular.module('app', [])
.filter('valueToText', () => {
return (input) => {
switch (input) {
case 1:
return 'bar';
case 2:
return 'foo'
default:
return input;
}
}
})
.controller('ctrl', ($scope) => {
$scope.names = [{
value: 1
}, {
value: 2
}, {
value: 2
}, {
value: 'foobar'
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="x in names">{{x.value | valueToText}}</li>
</ul>
</div>

Related

How to generating table that will have 3 columns per row

I use Vue 2.
I generate table from the collection:
<table class="table table-striped table-bordered">
<tbody>
<tr v-for="(type, typeIndex) in getAllUpgradeTypes" :key="typeIndex">
<td>
<imageDesc
style="margin-left: 10px"
:title="type"
:text="'Select an Option'"
:zoneId="selectedZone.id"
/>
</td>
</tr>
</tbody>
</table>
Currently, the table created with multiple rows, each row has only one column and shows one item inside the column.
My question how can I generate a table which will contain 3 columns per row?
There at least a couple of different patterns you could use for this. One involves rearranging your data before iterating it in the template, another doesn't. I'll show you both.
In both cases, use a data property to hold the number of columns:
data: () => ({
numCols: 3
})
Reorganize the data into a 2D array
Instead of one array with all your items, use a computed to reorganize the data into a multi-dimensional array of rows and columns:
computed: {
arrangedData() {
const arrangedData = [];
this.getAllUpgradeTypes.forEach((item, index) => {
if (index % this.numCols === 0) {
arrangedData.push([])
}
arrangedData[arrangedData.length - 1].push(item);
});
return arrangedData;
}
}
Iterate the data like this:
<table class="table table-striped table-bordered">
<tr v-for="(row, rowIndex) in arrangedData" :key="rowIndex">
<td v-for="(type, typeIndex) in row" :key="`${rowIndex}:${typeIndex}`">
{{ type }}
</td>
</tr>
</table>
Here's a demo:
new Vue({
el: "#app",
data: () => ({
getAllUpgradeTypes: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'],
numCols: 3
}),
computed: {
arrangedData() {
const arrangedData = [];
this.getAllUpgradeTypes.forEach((item, index) => {
if (index % this.numCols === 0) {
arrangedData.push([])
}
arrangedData[arrangedData.length - 1].push(item);
});
return arrangedData;
}
}
});
td { width: 40px }
<div id="app">
<table class="table table-striped table-bordered">
<tr v-for="(row, rowIndex) in arrangedData" :key="rowIndex">
<td v-for="(type, typeIndex) in row" :key="`${rowIndex}:${typeIndex}`">
{{ type }}
</td>
</tr>
</table>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
-or-
Calculate the number of rows
The other way involves using a computed to calculate the number of rows. It's less code, but then type gets an ugly syntax in the template:
computed: {
numRows() {
return Math.ceil(this.getAllUpgradeTypes.length / this.numCols);
}
}
Iterate the data like this:
<table class="table table-striped table-bordered">
<tr v-for="(row, indexRow) in numRows">
<td v-for="(col, indexCol) in numCols">
{{ getAllUpgradeTypes[(indexRow * numCols) + indexCol] }}
</td>
</tr>
</table>
Here's a demo:
new Vue({
el: "#app",
data: () => ({
getAllUpgradeTypes: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'],
numCols: 3
}),
computed: {
numRows() {
return Math.ceil(this.getAllUpgradeTypes.length / this.numCols);
}
}
});
td { width: 40px; }
<div id="app">
<table>
<tr v-for="(row, indexRow) in numRows" :key="indexRow">
<td v-for="(type, indexCol) in numCols" :key="`${rowIndex}:${typeIndex}`">
{{ getAllUpgradeTypes[(indexRow * numCols) + indexCol] }}
</td>
</tr>
</table>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
There are probably some variations on these options but I think the first is probably the best for the template clarity.
Here is my solution to a similar issue. I needed to generate a printable page and display information from a table in our database. The appearance of the page required me to have 3 fixed-size rows and 3 fixed-size columns per row.
I added styling to give it a grid-like appearance, but here is how I generated the table with page breaks after 3 rows:
This is where I have the table setup in the HTML (cshtml) file:
<table id="employee-table" class="m-auto">
</table>
Here is my JavaScript function which is located in an external JS file (don't forget to reference the file in your HTML if that's how you're setting it up)
function GeneratePrintablePDF(employeeData) {
document.getElementById('loader').classList.add('hidden'); //this is just the "loading" spinner for when the page is loading
dataContainer = document.getElementById('employee-table');
numberOfRows = employeeData.length / 3;
if (employeeData % 3 != 0) {
numberOfRows = Math.trunc(numberOfRows);
numberOfRows += 1;
}
var emp = 0;
do
{
for (var row = 1; row <= numberOfRows; row++) {
var tableRowDiv = document.createElement('div');
dataContainer.appendChild(tableRowDiv);
var tableRow = document.createElement('tr');
tableRowDiv.appendChild(tableRow);
for (var col = 1; col < 4; col++)
{
if (emp < employeeData.length) {
var tableCol = document.createElement('td');
tableCol.className = 'pdf-employee-box';
tableRow.appendChild(tableCol);
var empName = document.createElement('div');
empName.className = 'fw-bold';
empName.innerHTML = `${employeeData[emp].firstName} ${employeeData[emp].lastName}`;
tableCol.appendChild(empName);
var empPhoto = document.createElement('img');
var employeePhotoUrl = employeeData[emp].azurePhotoURL;
if (employeePhotoUrl == null) {
empPhoto.src = '/Images/lg_unavailable.jpg';
} else {
empPhoto.src = employeePhotoUrl;
}
var empPhotoOnError = document.createAttribute('onerror');
empPhotoOnError.value = "this.onerror=null;this.src='/Images/lg_unavailable.jpg';";
empPhoto.setAttributeNode(empPhotoOnError);
empPhoto.alt = 'Employee Photo';
empPhoto.className = 'employee-photo-pdf pdf-image';
tableCol.appendChild(empPhoto);
var empTitle = document.createElement('div');
empTitle.innerHTML = employeeData[emp].title != null ? `Title: ${employeeData[emp].title}` : 'Title: Unknown';
empTitle.className = 'pdf-details';
tableCol.appendChild(empTitle);
var empDept = document.createElement('div');
empDept.innerHTML = employeeData[emp].department != null ? `Department: ${employeeData[emp].department}` : 'Department: Unknown';
empDept.className = 'pdf-details';
tableCol.appendChild(empDept);
var empEmail = document.createElement('div');
empEmail.innerHTML = (employeeData[emp].emailAddress != null && !employeeData[emp].emailAddress.toLowerCase().includes('no ')) ? `Email: ${employeeData[emp].emailAddress}` : 'Email: Unknown/None';
empEmail.className = 'pdf-details';
tableCol.appendChild(empEmail);
var empStartDate = document.createElement('div');
empStartDate.innerHTML = employeeData[emp].startDate != null ? `Start Date: ${employeeData[emp].startDate}` : 'Start Date: Unknown';
empStartDate.className = 'pdf-details';
tableCol.appendChild(empStartDate);
var empLocation = document.createElement('div');
empLocation.innerHTML = employeeData[emp].location != null ? `Location: ${employeeData[emp].location}` : 'Location: Unknown';
empLocation.className = 'pdf-details';
tableCol.appendChild(empLocation);
if (col == 2) tableCol.className = tableCol.className + ' pdf-grid-lines';
emp++;
}
}
if (emp % 9 == 0) {
tableRowDiv.className += 'page-break-after';
}
}
} while (emp < employeeData.length)
}
I decided to calculate the number of rows I need based on how many employee records we have. So, if we have 436 employees, then I'm determining that I need 146 rows. So I'm programming it to generate the precise number of rows I need, and only put 3 items (columns) in each row. The 146th row in this case only has 1 record.
As you can see, I have the code in a loop only running until it reaches the last employee, so that it doesn't throw an index out of range exception.
This particular code is where I am telling the table to break to the next page, so that employee records aren't running over onto another page and splitting up the information.
if (emp % 9 == 0) {
tableRowDiv.className += 'page-break-after';
}
Here is the CSS that I'm referencing in an external CSS file:
#media print {
.page-break-after {
break-after: page;
}
}
I understand that I am by no means the ultimate programming kingpin, so there could be better or easier ways of doing this. However, this is how I made it work with my environment and it does exactly what I need it to do without using some framework or library.
I found in my research that many PDF generator frameworks, libraries, etc. now require licensing. So if generating a printable document is your goal, then this could work for you too. Anyways, good luck and I hope I have helped someone.

AngularJS ngtable not displaying data correctly

I have an ngTable that is loaded with data that proceeds from a "Get" call to a webapi. Then I reload the table, but the data it is not being displayed.
This is the *.js file
rdapp.controller('scoringTableCtrl', ['$location', '$scope', '$http', 'ngTableParams', '$filter',
function($location, $scope, $http, ngTableParams, $filter) {
$scope.teamList = [];
$http({
method: 'GET',
url: 'http://localhost:34592/api/scoringTable',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).then(function(success) {
$scope.teamList = success.data;
addFieldsForSorting();
$scope.dataTable.reload();
}, function(error) {
console.log(error);
});
$scope.resolveHTML = function(position) {
if (position == 1 || position == 2 || position == 3) return 'champions';
if (position == 4) return 'champions-prev';
if (position == 5 || position == 6) return 'europa-league';
if (position == 18 || position == 19 || position == 20) return 'decline';
return '';
}
function addFieldsForSorting() {
// Add named properties for every stat value in recipients array, used for sorting the grid.
// Take value from vitalStats array, use alertIds to match between alpha keys and numeric keys.
$scope.teamList.forEach(function(team) {
for (var property in team) {
if (!team.hasOwnProperty(property)) {
continue;
}
if (property == 'name') {
continue;
}
if (property == 'position') {
continue;
}
var prop = 'sort_' + property;
team[prop] = -(team[property]);
}
team['sort_name'] = team.name;
team['sort_position'] = team.position;
});
}
$scope.dataTable = new ngTableParams({
page: 1, // show first page
count: 20, // count per page
sorting: {
sort_position: 'asc' // initial sorting
}
}, {
counts: [],
total: $scope.teamList.length, // length of data
getData: function($defer, params) {
// use build-in angular filter
var requestData = $scope.teamList;
var orderedData = params.sorting() ? $filter('orderBy')(requestData, params.orderBy()) : requestData;
params.total(orderedData.length); // set total for recalc pagination
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
}
]);
This is my html:
<div class="position-view" style="position:relative; top:100px;">
<table ng-table="dataTable" class="table table-bordered table-border-important" ng-class="rec_spinner">
<tbody class="text-center">{{$data.length}}
<tr ng-repeat="team in $data">
<td class="{{resolveHTML(team.position)}}" data-title="''" sortable="'sort_position'">
{{team.position}}
</td>
<td data-title="'Clasificación'" sortable="'sort_name'">
{{team.name}}
</td>
<!--Total Stats-->
<td data-title="'PJ'" sortable="'sort_pj'">
{{team.pj}}
</td>
<td data-title="'PG'" sortable="'sort_pg'" >
{{team.pg}}
</td>
<td data-title="'PE'" sortable="'sort_pe'" >
{{team.pe}}
</td>
<td data-title="'PP'" sortable="'sort_pp'" >
{{team.pp}}
</td>
<td data-title="'GF'" sortable="'sort_gf'">
{{team.gf}}
</td>
<td data-title="'GC'" sortable="'sort_gc'">
{{team.gc}}
</td>
<td data-title="'PT'" sortable="'sort_pt'">
{{team.pt}}
</td>
</tr>
</tbody>
</table>
$data has no any data, but if I try {{dataTable}} I have all the data correctly loaded. Can anybody help me with this?, maybe there is something obvious that I am missing, but the point is that the table is creating the amount of rows and columns anyway but empty, it is very weird.
I think you are not storing $data anywhere. I cannot find $scope.data in your js file.
After long search I figured out the problem was pretty easy, was about the CamelCase notation. The problem is when you send info from a web api, if you don´t set custom notation for the parameters, they will be sent with the first letter capitalized.
So, we have two chooses here, establish custom notation or just use in our HTML the first letter capitalized. I hope this will help somebody in the future!

Issue with filtering an Angular Table td

I have a table created in angularJS.
HTML:
<div class="widget-content" ng-controller="getAllBenchersController">
<table ng-table="talentPoolList" show-filter="true"
class="table table-striped table-bordered">
<tr ng-repeat="employee in $data" | filter: testFilter">
<td data-title="'#'">{{$index + 1}}</td>
<td data-title="'Employee ID'" sortable="'employee.employeeNo'"
filter="{ 'employee.employeeNo': 'text' }">
{{employee.employee.employeeNo}}
</td>
<td data-title="'Current State'" sortable="'state.state'" filter="{ 'state.state': 'text' }"
ng-class="{ red: employee.state.state == 'Available', blue: employee.state.state == 'Blocked'}">
{{employee.state.state}}
</td>
</tr>
</table>
</div>
In the <td data-title="'Current State'">I want to apply a filter. I want to display only states returning values 'Available' and 'Blocked'.
My controller is :
myApp.controller('getAllBenchersController', ['$scope', 'employeeTalentPoolServices', 'dataTable', '$window', '$timeout', function ($scope, employeeTalentPoolServices, dataTable, $window, $timeout) {
employeeTalentPoolServices.getAllBenchers().then(function (result) {
$scope.data = result.data;
$scope.testFilter = function (item) {
return (item.state.state.toLowerCase() === 'available' || item.state.state.toLowerCase() === 'blocked');
}
});
The states generally return values -'Available', 'Blocked', 'Billed' and 'Rejected'. I want the table to only display states - 'Available' and 'Blocked'.
Please have a check and point out what I'm doing wrong.
If you want to display only the items with the state 'available' and 'blocked' i think the best choice is to apply an angular filter in the ng-repeat. By this way, the ng-repeat only computes the items needed for the table.
You can add a new filter, for example:
myApp.filter('filterState',
[], function() {
return function(items) {
return items.map(function(obj) {
if (obj.state === 'available' || obj.state === 'blocked')
return obj;
});
});
After, your div element should be:
<tr ng-repeat="employee in $data" | filter: filterState">
return (item.state.state.toLowerCase() === 'available' || item.state.state.toLowerCase() === 'blocked');
Above code returns a boolean value, also if you have array list of values , here only one value is considered.
You should write a for loop , then check the values.
Refer below code:
var filteredOutput=[];
for(var i=0;i<item.length; i++){
if(item[i].state.state.toLowerCase() === 'available' || item[i].state.state.toLowerCase() === 'blocked')
{
filteredOutput.push(item[i]);
}
}
return filteredOutput;

Get data from a key value structure and compare it with value from an ng-repeat in AngularJS

Two basic questions,
1) I have a controller where I define a list with a key, value structure and I want to access to the event name based on the eventID and log it in the console
$scope.events = [
{eventID:3200, eventName:"Event One"}
{eventID:3201, eventName:"Event Two"}
];
I tried this:
if($scope.events.eventID == 3200) {
console.log("Ok");
}
but it didnt worked :(
2) Then in my view I have a table where i print data coming from a WS, what I want is to print the description on the table depending on the eventID from my $scope.events list, I have a field on my ng-repeat named event that will let me compare with the eventID from the list but I dont know how to do this.
<tr ng repeat d in data>
<td>d.Description</td>
<td>d.anyValue</td>
</tr>
Any help is welcome, I really need it I'm new to Angular :)
Add method in controller
$scope.search=function(obj){
return obj.eventID === 3200;
}
add it to view
<tr ng-repeat="d in data | filter:search">
<td>d.Description</td>
<td>d.anyValue</td>
</tr>
for question number 1, try with this
if($scope.events[0].eventID == 3200) {
console.log("Ok");
}
for looping using this
angular.forEach($scope.events, function (value) {
if (value.eventId === 3200) {
{
console.log('OK');
}
});
for question number 2, create helper function to return eventName base on the ID.
$scope.getEventName = function (eventId) {
var result = '';
angular.forEach($scope.events, function (value) {
if (value.eventID === eventId) {
result = value.eventName;
return false;
}
});
return result;
}
<tr ng-repeat="d in (exportDataEventDesc.result.slice(5, exportDataEventDesc.result.length-2))">
<td>{{d.Description}}</td>
<td>{{d.anyValue}}</td>
<td ng-bind="getEventName(d.merchMetrics.EVENT_ID)" style="text-align: left"></td>
</tr>

Create grid with knockoutjs

Let's say we have an observable array of N+1 elements. What i would like to achive is to build 3 by X grid with buttons from these elements. Like phone keypad or something like this.
What i have done so far is created a table with foreach and if in it:
<table>
<tbody data-bind="foreach: tableList">
<!-- ko if: isEof($index()) -->
<tr>
<!-- /ko -->
<td><button data-bind="text: name"></button></td>
<!-- ko if: isEof($index()) -->
</tr>
<!-- /ko -->
</tbody>
</table>
isEof() function should determine by list index wether we have already 3 elements rendered. If yes, then it will render tags. Also if index is 0, then it also renders elements.
This is the code of the function:
function TableItem(data){
this.id=ko.observable(data.id);
this.name=ko.observable(data.name);
this.isEof = function(index){
if(index ==0){
return true;
}else{
if((index+3) % 3 === 0){
return true;
}else{
return false;
}
}
}
}
But i'm facing two problems with this setup.
1) With these if blocks enabled, button name binding does not work. When i remove ko if blocks, it will be rendered correctly.
2) ko if statements does not seem to work correctly. It will render out only those lines, where is also allowed to render.
I've made JSFiddle sample of my solution: http://jsfiddle.net/kpihus/3Lw7xjae/2/
I would create a ko.computed that converts your list of table items into an array of arrays:
var TableItem = function TableItem(data) {
this.id = ko.observable(data.id);
this.name = ko.observable(data.name);
};
var Vm = function Vm() {
this.tableItems = ko.observableArray([]);
this.columnCount = ko.observable(3);
this.columns = ko.computed(function() {
var columns = [],
itemCount = this.tableItems().length,
begin = 0;
// we use begin + 1 to compare to length, because slice
// uses zero-based index parameters
while (begin + 1 < itemCount) {
columns.push( this.tableItems.slice(begin, begin + this.columnCount()) );
begin += this.columnCount();
}
return columns;
// don't forget to set `this` inside the computed to our Vm
}, this);
};
vm = new Vm();
ko.applyBindings(vm);
for (var i = 1; i < 15; i++) {
vm.tableItems.push(new TableItem({
id: i,
name: "name: " + i
}));
}
This way, you can display your table by nesting two foreach bindings:
<table>
<tbody data-bind="foreach: { data: columns, as: 'column' }">
<tr data-bind="foreach: column">
<td>
<button data-bind="text: name">A</button>
</td>
</tr>
</tbody>
</table>
JSFiddle
If you haven't worked with ko.computed before, they track whatever observables are accessed inside of them — in this case, this.tableItems and this.columnCount —, and whenever one of them changes, they run again and produce a new result.
this.columns takes our array of table items
[TableItem, TableItem, TableItem, TableItem, TableItem, TableItem]
and groups them by this.columnCount into
[[TableItem, TableItem, TableItem], [TableItem, TableItem, TableItem]]
We can achieve this by simply passing $root which will have tableList and do the looping in simple way .
View Model:
function TableItem(data) {
var self = this;
self.id = ko.observable(data.id);
self.name = ko.observable(data.name);
self.pickarray = ko.observableArray();
self.isEof = function (index, root) {
if (index === 0) {
self.pickarray(root.tableList().slice(index, index + 3));
return true;
} else if ((index + 3) % 3 === 0) {
self.pickarray(root.tableList().slice(index, index + 3));
return true;
} else {
return false;
}
}
}
var vm = function () {
this.tableList = ko.observableArray();
for (var i = 1; i < 15; i++) {
this.tableList.push(new TableItem({
id: i,
name: "name: " + i
}));
}
}
ko.applyBindings(new vm());
View :
<table data-bind="foreach:tableList">
<tr data-bind="if:isEof($index(),$root)">
<!-- ko foreach: pickarray -->
<td>
<button data-bind="text: id"></button>
</td>
<!-- /ko -->
</tr>
</table>
Trick here is very simple we just need to conditionally fill pickarray which does the job for us .
Working Fiddle here

Categories

Resources