I have data which is represented as column names, row names and values for a row/column pair. It looks like this:
cols = ['A', 'B', 'C', 'D'];
rows = ['1', '2', '3', '4'];
data = [
{row: '1', col:'A', value: 'a'},
{row: '2', col:'C', value: 'b'},
{row: '3', col:'B', value: 'c'}
];
How do I make this appear in table like this? I am using Angular 1.4.
A B C D
1 a
2 b
3 c
4
My data size is limited, don't expect it to be more than 50-100 entries in data.
Change your data object to a dictionary:
data = [
{row: '1', col:'A', value: 'a'},
{row: '2', col:'C', value: 'b'},
{row: '3', col:'B', value: 'c'}
];
becomes:
data = {'A1' : 'a', 'C2' : 'b', 'B3' : 'c'}..
then do a double ng-repeat loop:
<div ng-repeat="col in cols">
<div ng-repeat="row in rows">
{{ data[row + col] == null ? " " : data[row + col] }}
disclaimer: this is untested, and my angular syntax may be incorrect, but I hope you get the idea.
Since I was limited to not being able to reformat the data and the actual data was a somewhat more complicated than this I ended up writing a filter. My html looks like this:
<div>
<table>
<tr><td></td>
<td ng-repeat="col in cols">{{ col }}</td>
</tr>
<tr ng-repeat="row is rows">
<td>{{ row }}</td>
<td ng-repeat="col in cols">
<div ng-repeat="obj in data | findRowCol:row:col">
{{ obj.value }}
</div>
</td>
</tr>
</table>
</div>
I borrowed from KayakDave's answer here for my filter.
app.filter('findRowCol', function() {
return function(data, row, col) {
result = [];
data.forEach(function(el) {
if(el.row === row && el.col === col)
result.push(el);
});
return result;
}
});
Since data is not expected to be ordered in any particular way this can have a O(N^3) performance, so not very nice solution.
Related
i have the json array which i get from api:
items:[{
Name: "Status"
Value: "3"
},{
Name: "Status"
Value: "2"
}, {
Name: "Status"
Value: "1"
}]
than i get data
if (dataStatus.Name == 'Status') newDataStatus.Status = dataStatus.Value;
and insert value to my mat table
<ng-container matColumnDef="Status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
<td mat-cell *matCellDef="let row"> {{row.Status}} </td>
</ng-container>
But i see only numbers in status row, how i can change Value:"3" to "complete", Value: "2" to "end" and so on
if (dataStatus.Name == 'Status') {
newDataStatus.Status = ['new', 'active', 'stuck', 'end', 'complete'][+dataStatus.Value];
}
['new', 'active', 'stuck', 'end', 'complete'] - array of text values for interger statuses. you can consider extracting it into a variable of a property.
plus sign before dataStatus.Value - converts a string to a number
-1 - deducts one
Since you tagged Typescript you can use an enum and actually get the reverse value (enums exist at runtime!)
enum Status {
NEW = '1',
END = '2',
COMPLETE = '3',
}
// later
if (dataStatus.Name == 'Status') newDataStatus.Status = Status[dataStatus.Value];
newDataStatus.Status now has the type keyof typeof Status.
You have to type the dataStatus correctly, of course, or cast it as any: Status[(dataStatus.Value as any)] to shut up the TS compiler.
I want to set up a checkbox structure and I want to handle it dynamically. I have an object response returned from my service. However, when I use this function in context, I get a string return and I cannot use ngFor.
form.demo.component.ts
getElementChoicesByNKey(nkey:string):choiceModel[]{
var element = this.findInComponentsByNKey(nkey);
var res = element.Choices;
return res;
}
this function gives the correct return.
form.demo.component.html
...
<ng-container *ngIf="item.Type == 'Choice'">
<ng-container *ngTemplateOutlet="choiceTheme; context:{ Id: item.Id, Label: item.Label, Choices: getElementChoicesByNKey(item.Id) }"></ng-container>
</ng-container>
...
<ng-template #choiceTheme let-Id="Id" let-Label="Label" let-Choices="Choices">
<nz-form-item>
<nz-form-label nzFor="Id">{{Label}}</nz-form-label>
<nz-form-control [formControlName]="Id" nzErrorTip="{{ 'form.requiredText' | translate }}">
<p>{{Choices.length}}</p>
<div *ngFor="let item of Choices">
<p>test</p>
</div>
</nz-form-control>
</nz-form-item>
</ng-template>
...
here Choices appears as string and won't let me use ngFor. I am getting an error as below.
Cannot find a differ supporting object '[
{ label: 'Apple', value: 'Apple', checked: true },
{ label: 'Pear', value: 'Pear', checked: false },
{ label: 'Orange', value: 'Orange', checked: false } ]' of type 'string'. NgFor only supports binding to Iterables such as Arrays.
What is the point I missed? Thank you for your help.
Use
*ngFor="let item of getChoices(Choices)" in template
and in component.ts
getChoices(choiceStr) {
return JSON.parse(choiceStr);
}
Since you are getting Choices as a string, parse the string to an array inorder for ngFor to work
I want to filter the values of a <select>.
I have a table with first column <select> .
For eg: object for the <select> is
JSON:
json1 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}, {id: 3, name: 'XYZ'}, {id: 4, name: 'ASD'}, {id: 5, name: 'QWE'}]
json2 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}]
My requirement is: We need to show values from json1 in ng-options but which object should not be there in json2.
For eg: First 2 rows will be filled with json2. So we need to provide options 'XYZ' 'ASD' and 'QWE' in the following rows.
Suppose if name 'XYZ' is selected in the dropdown of the third row. then 4th row <select> should show only 'ASD', and 'QWE'. Similarly what ever object selected in other rows shouldn't be shown in option of other rows dropdown.
I have tried something like this
<select ng-model="obj"
ng-options="obj.id as obj.name for obj in json1 | myFilter:json2">
</select>
myApp.filter('myFilter', function(json2) {
return function(json1) {
var filtered = [];
json1.forEach((d) => {
var exists = false;
json2.forEach((ad) => {
if(ad.id == d.id) {
exists = true;
}
});
if(!exists) filtered.push(d);
});
console.log(filetered);
return filtered.length > 0 ? filtered : json1;
};
});
In filter console.log() values are filtered correctly as expected. However in ng-options all options from json1 are still available not updated with filtered values.
What's wrong?
Is this what you are looking for?
For example:
<table>
<thead>
<tr>
<th>Unique</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products">
<td>
<select ng-model="product.unique" ng-options="obj.id as obj.name for obj in (json1 | myfilter:products:$index)">
<option value="">No select</option>
</select>
</td>
<td ng-bind="product.name"></td>
</tr>
</tbody>
</table>
vm.products = [{name: 'Product 1'}, {name: 'Product 2'}, {name: 'Product 3'}, {name: 'Product 4'}, {name: 'Product 5'}];
vm.json1 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}, {id: 3, name: 'XYZ'}, {id: 4, name: 'ASD'}, {id: 5, name: 'QWE'}];
App.filter('myfilter' , [function() {
return function(json1, products, index) {
// Filter all products, except the mime
productsFilter = products.filter(function(product, i) {
return i !== index;
});
// filter those that have not yet been selected
var json1Filter = json1.filter(function(item) {
// ask if there is the id in a product
return !productsFilter.some(function(product) {
return item.id == product.unique;
});
});
return json1Filter;
};
}]);
Example Codepen: https://codepen.io/anon/pen/vMJyLz
I think you have the parameters to your filter slightly wrong. The filter function takes a first parameter as the value to be filtered and the other parameters are the ones after myFilter:.
I'm not 100% sure what you want to happen here, but your filter function is called for each value in the dropdown list and should return a replacement for the given value.
However, your code is returning an array of items. This is not how a filter works in AngularJS.
Your filter needs to be updated to check if the item being passed into the filter should be shown or not, and if not, return false.
You can look at the script example from the AngularJS docs to see how they show to do this, or this other Stack Overflow question/answer.
Here is a small example of my html using ng-repeat:
<div ng-repeat="item in vm.templateList | filter: vm.myFilter">
<h3>{{item.Code}}</h3>
</div>
In Js file the vm.templateList is as followed(as an example):
vm.templateList = [{Code: 'a', ID: 1},
{code: 'a', ID: 2},
{code: 'b', ID: 3},
{code: 'c', ID: 4}];
Imagine I want to filter this list for all items that have ID 1 and also items that have ID 2.
What I originaly was doing was like this:
vm.filter = {ID: 1};
But this was I can only filter the list on 1 ID. Can anyone suggest a way?
You can add the following AngularJS filter to your application :
// add a custom filter to your module
angular.module('MyModule').filter('myFilter', function() {
// the filter takes an additional input filterIDs
return function(inputArray, filterIDs) {
// filter your original array to return only the objects that
// have their ID in the filterIDs array
return inputArray.filter(function (entry) {
return this.indexOf(entry.ID) !== -1;
}, filterIDs); // filterIDs here is what "this" is referencing in the line above
};
});
You then declare your filter array in the controller as such :
vm.IDs = [1, 2];
Then your view should look like this :
<div ng-repeat="item in vm.templateList | myFilter: vm.IDs">
<h3>{{item.Code}}</h3>
</div>
You can use something like:
html:
<section>
<div ng-repeat="item in vm.templateList | filter:checkFilterOptions">
<h3>{{item.Code}}</h3>
</div>
</section>
Js:
$scope.vm = {};
$scope.vm.templateList = [
{Code: 'a', ID: 1},
{Code: 'a', ID: 2},
{Code: 'b', ID: 3},
{Code: 'c', ID: 4}
];
$scope.filterOptions = [1,2,3];
$scope.checkFilterOptions = function(value, index) {
return value.ID && $scope.filterOptions.indexOf(value.ID) !== -1;
}
Nervous to ask this question.. HATE getting downvoted.. but it is what it is, I've searched and can't find the solution.
What I ended up doing is adding a loop that goes through my searchResults and reassigns the value for the column after the service returns inside the success block (PSEUDO CODE HERE, I can't copy and paste my actual code, there is an airgap):
var myNumberMap = {
1: "Number ONE!!",
2: "Number TWO!!",
3: "Number THREE!!!"
}
$scope.getSearchResults = function() {
$q.all({
resultSet : searchService.getSearchResults()
}).then(function(resultData) {
searchResults = resultData.resultSet;
for(var i = 0; i < searchResults.length; i++) {
searchResults[i].number = myNumberMap[searchResults[i].number];
}
}
}
I was really hoping there was some slick way I could just assign the data result value inside the grid config to be the value in the map?
Something like:
$scope.myCoolGridConfig = NgGridConfig.getConfig(
NgGridConfig.getDefaultConfig(), {
data: 'searchModel.searchResults.list',
columnDefs: [
field: 'number',
displayName: 'Number',
value: myNumberMap[searchModel.searchResults.list.number]
]
}
)
There are a few methods that you could take here:
Create a custom filter that you apply to your ng-repeat to transform the values based on your map.
Store your value map in your angular controller and bind the mapped value to the DOM.
// Controller
$scope.myMap = {
1 : "String One",
2 : "String Two",
3 : "String Three"
}
// something.html
<div ng-repeat='num in numList'>
{{myMap[num]}}
</div>
If I interpenetrated the question correctly your looking for something along these lines.
myMap = {
1 : "String One",
2 : "String Two",
3 : "String Three"
};
If the col number is 1 display String One instead of one in the table
Use myMap and look for the prop of col in it to pull the string value
<table>
<tr ng-repeat="col in tempCols">
<td>{{col}}</td>
<td>{{myMap[col]}}</td>
</tr>
</table>
If you need to do it towards an object that has no defining index such as the object below.
$scope.objectData = [{
name: "test1",
},
{
name: "test1",
},
{
name: "test1",
},
{
name: "test1",
},
{
name: "test1",
},
]
You can track it by $index + 1
<table>
<tr>
<td> Column Converted</td>
<td> Object name value</td>
<tr ng-repeat="col in objectData">
<td>{{myMap[$index + 1]}}</td>
<td>{{col.name}}</td>
</tr>
</table>
Heres a plunker for a better visual