I have one json file which contains multiple objects inside another object. I want to fetch data but not using key of that value. I want to iterate there key and values and want to print them dynamically in angular 6.
{
"name" : "abc",
"tags" : "def",
"updated-by" : "ijk",
"property" : {
"description" : "abcd",
"type" : "string"
},
"sources" : {
"input" : {
"type" : "lmn",
"properties" : {
"key" : "opq"
}
}
}
}
Can we iterate objects like we iterates array. If anyone can help?
I would suggest referring this StackOverflow question,
As far as I know, *ngFor can be used not only for arrays but also for Objects.
Hope the above link helps.
Also for the keys whose values contain objects, you could check if the corresponding value of the key is an object.
For instance,
if( (typeof A === "object") && (A !== null) )
where A is the corresponding value of the key. If A is indeed an object, use *ngFor again to iterate over the object.
I haven't tested the following code but I hope you get an overview of what I am trying to say,
#Component({
selector: 'app-myview',
template:
`<div *ngFor="let key of objectKeys(items)">{{key + ' : ' + items[key]}}
<div *ngIf="checkFunction(items[key])">
<div *ngFor="let key2 of objectKeys(items[key])">
{{key2 + ' :' + items[key][key2]}}
</div>
</div>
</div>`
})
export class MyComponent {
objectKeys = Object.keys;
items = { keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3' };
constructor(){}
checkFunction(obj){
if( (typeof obj === "object") && (obj !== null) )
{
return true;
}
else{
return false;
}
}
}
var temp = {
"name" : "abc",
"tags" : "def",
"updated-by" : "ijk",
"property" : {
"description" : "abcd",
"type" : "string"
},
"sources" : {
"input" : {
"type" : "lmn",
"properties" : {
"key" : "opq"
}
}
}
};
flatten the object
var flattenObject = function(ob) {
var toReturn = {};
for (var i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if ((typeof ob[i]) == 'object') {
var flatObject = flattenObject(ob[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + '.' + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
var flat = flattenObject(temp)
Iterate over object just like array
Object.entries(flat).forEach(entry => {
console.log(entry[0] + " => " + entry[1])
})
Related
I have created a form which consist of JSON array and according to that, I am generating Validation,formControlName and generating output through formGroup.
this.ELEMENT_DATA_UPDATE = [
{
first_name : 'abc',
last_name : 'xyz',
phone : 8888888888
}
];
Here, I am using Angular material so converted this key value pair to another array consists of
{"key" : "first_name" , "value" : "abc" , "name" : "First name :"}
This is when the input JSON array is fixed. But my project consists of data manipulation on a large scale in which input JSON array contents will change many times. There is no problem in generating UI modules, but when I try to apply Validation and forms modules to this dynamically generated contents, whole flow collapse ,
this is my code
var jsonArray : any = [{}];
export class UpdateContactFieldDialog {
matcher = new MyErrorStateMatcher();
updateForm: FormGroup;
formString : string = null;
ELEMENT_DATA_UPDATE : any[] = [];
keyArray : any[] = [];
myJobFunctionControl = new FormControl();
optionsJobFunction: string[] = ['Operations', 'Production', 'Manufacturing'];
myTitleControl = new FormControl();
optionsTitle: string[] = ['HR', 'Developer', 'Tester', 'MD', 'CEO', 'Director'];
constructor(private formBuilder: FormBuilder,private dialogRef: MatDialogRef<AddContactFieldDialog> ) {
}
ngOnInit() {
//dumy randomly geneated fields
this.ELEMENT_DATA_UPDATE = [
{
first_name : 'abc',
last_name : 'xyz',
job_function : 'Production',
title : 'Developer',
email : 'abc#abx.com',
phone : 7945612304
}
];
for (let obj of this.ELEMENT_DATA_UPDATE) {
console.log("object length:", this.ELEMENT_DATA_UPDATE.length);
for (let key in obj) {
this.keyArray.push({'key' : key , 'value' : obj[key] , 'name': (key.charAt(0).toUpperCase() + key.substr(1).toLowerCase()).replace(/_/g, ' ')});
}
break;
}
console.log(this.keyArray);
console.log(this.ELEMENT_DATA_UPDATE[0]);
for(let formModule of this.keyArray){
var keyData = formModule.key;
if(this.formString != null && keyData == 'email' || keyData.includes('mail')){
this.formString = this.formString +''+keyData+':[this.ELEMENT_DATA_UPDATE[0].'+keyData +',[Validators.required,Validators.email]], ';
}
else
if(this.formString != null && keyData == 'phone' || keyData.includes('number') || keyData.includes('no') || keyData.includes('num') ){
this.formString = this.formString +''+keyData+':[this.ELEMENT_DATA_UPDATE[0].'+keyData +',[Validators.required,Validators.minLength(10),Validators.maxLength(10),Validators.pattern("[0-9]*")]], ';
}
else
if(this.formString == null && keyData != 'email' && keyData != 'phone'){
this.formString = ''+keyData+':[this.ELEMENT_DATA_UPDATE[0].'+keyData +',Validators.required], ';
}
else{
this.formString = this.formString + ''+keyData+':[this.ELEMENT_DATA_UPDATE[0].'+keyData +',Validators.required], ';
}
}
console.log(this.formString);
jsonArray = (this.formString);
this.updateForm = this.formBuilder.group(jsonArray);
}
// convenience getter for easy access to form fields
get f() { return this.updateForm.controls; }
submitContact() {
this.submitted = true;
// stop here if form is invalid
if (this.updateForm.valid) {
// alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.updateForm.value))
console.log(this.updateForm.value);
this.dialogRef.close();
}
else{
return;
}
}
}
My code is generating following code as String
first_name: ['', Validators.required],
last_name: ['', Validators.required],
job_function: ['', [Validators.required]],
title: ['', [Validators.required]],
email: ['', [Validators.required, Validators.email]],
phone : ['',[Validators.required, Validators.minLength(10), Validators.maxLength(10), Validators.pattern('[0-9 ]*')]]
I want this to be used inside of formGroup so i can dynamically generate formControls , assign them validation and values.
updateForm = this.formBuilder.group(jsonArray);
Complementary my comment, NOTE:you need add the validators (I can't see them in your code)
this.updateForm=new FormGroup({}) //<--create the formGroup
for(let formModule of this.keyArray){
this.updateForm.addcontrol(formModule.key,new FormControl(formModule.Value))
}
Moreover, if our objects in keyArray was like
{ "key" : "key_name" ,
"value" : "value" ,
"name" : "Key name" ,
validators:[Validators.required, Validators.email]
}
We can improve our loop
for(let formModule of this.keyArray){
this.updateForm.addcontrol(formModule.key,
new FormControl(formModule.Value,formModule.validators))
}
Well, if difficult that our object becomes from a service like I showed, but it's possible our services serve us object like:
{ "key" : "key_name" ,
"value" : "value" ,
"name" : "Key name" ,
validators:[{type:"required"},{type="min",args:3}]
}
Before make the loop we can use
this.keyArray.forEach(x=>
{
if (x.validators)
{
conts validators=[];
validators.forEach(v=>{
switch (v.type)
{
case "required":
validators.push(Validators.required);
break;
case "min":
validators.push(Validators.min(v.arg);
break;
...
}
})
x.validators=validators;
}
}
About to show the errors we must take account that our controls in a form is
updateForm.get(keyArray.key)
So, e.g.
<div class="col-lg-6" *ngFor="let keyArray of keyArray">
<mat-form-field>
<input type="text" [formControlName]="keyArray.key" matInput
[placeholder]="keyArray.name"
[ngClass]="{ 'is-invalid': submitted && updateForm.get(keyArray.key).errors }"
autocomplete="off" />
<mat-error *ngIf="submitted && updateForm.get(keyArray.key).hasError('required')">
{{keyArray.name}} is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
UPDATE
As developer003 suggested, I did this:
It works, but not at all. If I add an other property as c.details.author (string[]) or c.details.index (number), it doesn't work, and the function doesn't return nothing (error).
Here an extract of my JSON database:
[
{
"index": 1,
"name": "ad dolor ipsum quis",
"details": {
"author": ["Wallace Stephens", "Steve Ballmer"],
"game": {
"short": "tdp",
"name": "Thief: The Dark Project"
},
"newdark": {
"required": true,
"version": "1.20"
},
"firstreleasedate": "2007/04/27",
"lastupdatedate": "2017/01/28"
}
}
]
So I can look for another details properties than strings. Any idea?
ORIGINAL POST
I created a function which, when I call it as a (keyup) event, filters an HTML datatable when I type something in an input.
return c.name.toLowerCase().indexOf(input.target.value.toLowerCase()) != -1;
I want to be able to filter, not only by name, but also by details.author, details.game.name, details.firstrelease... etc.
How can I change c.name to apply these properties? Do I need to create a loop? Should I use .map()?
Right now I can think in 2 approaches:
1st:
º Create a function to parse (toLowerCase()) the property value and handle if it contains the value or not:
containsVal(property, value) {
return property.toLowerCase().indexOf(value) !== -1;
}
filterFunc(c) {
return this.containsVal(c.name, VALUE_TO_SEARCH) ||
c.details && (
this.containsVal(c.details.author, VALUE_TO_SEARCH) ||
this.containsVal(c.details.firstrelease, VALUE_TO_SEARCH) ||
(c.details.game && this.containsVal(c.details.game.name, VALUE_TO_SEARCH))
);
}
2nd:
º Map the only needed properties and filter them.
arr.map(item => {
return {
name: item.name,
author_details: item.details && item.details.author,
firstrelease_details: item.details && item.details.firstrelease,
game_name: item.details && item.details.game && item.details.game.name
};
}).filter(item => {
return Object.keys(item).some(key => {
const value = item[key];
return value && value.toLowerCase().indexOf(VALUE_TO_SEARCH) !== -1;
});
});
I have a sap.ui.table.TreeTable with several columns, one of which contains a custom control defined as a template for the column.
The TreeTable renders correctly, and the column with the custom control also renders and functions correctly ... that is until I expand an unexpanded node.
When I expand a node the Renderer throws an error
Error: adding element with duplicate id '__link0-col1-row0',
which is the column associated with the custom control.
I can workaround the problem by adding attachToggleOpenState event handler which destroys this columns template and adds it again. Expensive but it works!
this.oTable = new sap.ui.table.TreeTable("myTable", {
columns : [ new sap.ui.table.Column( {
label : "Query",
template : new sparqlish.control.queryClause({
clausePath : {
path : "viewModel>path"
}
}),
visible : true,
width : "500px"
}), ],
}).attachToggleOpenState(function(oEvent) {
// TODO workaround as expanding Tree causes duplicate id
var queryColumn = oEvent.getSource().getColumns()[0];
queryColumn.destroyTemplate();
queryColumn.setTemplate(new sparqlish.control.queryClause({
clausePath : {
path : "viewModel>path"
}
}))
});
sparqlish.control.queryClause is the custom control.
I am using openui5 version 1.28.15
Any other, less expensive, suggestions?
The custom control takes this form, however this control invokes other controls and so on.
jQuery.sap.require("sparqlish.control.conceptClause");
jQuery.sap.require("sparqlish.control.propertyClause");
jQuery.sap.require("sparqlish.control.conjunctionPropertyClause");
sap.ui.core.Control.extend("sparqlish.control.queryClause", {
metadata : {
properties : {
clausePath : {
type : "string"
}
},
events : {},
aggregations : {
_conceptClause : {
type : "sparqlish.control.conceptClause",
multiple : false
},
_propertyClause : {
type : "sparqlish.control.propertyClause",
multiple : false
},
_conjunctionPropertyClause : {
type : "sparqlish.control.conjunctionPropertyClause",
multiple : false
}
}
},
init : function() {
var self = this;
self.setAggregation("_conceptClause", new sparqlish.control.conceptClause());
self.setAggregation("_propertyClause", new sparqlish.control.propertyClause());
self.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause());
},
renderer : function(oRm, oControl) {
if (oControl.getClausePath() != undefined) {
// TODO no point if path not yet defined
oRm.write("<div ");
oRm.writeControlData(oControl);
oRm.writeClasses();
oRm.write(">");
var currentModel = oControl.getModel("queryModel");
// Set binding context, rather than just the binding path etc, as this seems essential for satisfactory binding of
// aggregations
oControl.setBindingContext(new sap.ui.model.Context(oQueryModel, oControl.getClausePath()), "queryModel")
var currentCtx = oControl.getBindingContext("queryModel");
var currentContext = oControl.getModel("queryModel").getProperty("", currentCtx);
if (currentContext != undefined) {
var sClass = currentContext._class;
if (sClass == "Query") {
oControl.setAggregation("_conceptClause", new sparqlish.control.conceptClause().setBindingContext(oControl.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_conceptClause"));
} else if (sClass == "Clause") {
oControl.setAggregation("_propertyClause", new sparqlish.control.propertyClause().setBindingContext(oControl.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_propertyClause"));
} else if (sClass == "ConjunctionClause") {
oControl.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause().setBindingContext(oControl
.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_conjunctionPropertyClause"));
} else {
jQuery.sap.log.fatal("Incorrect class of provided query clause");
}
}
oRm.write("</div>");
} else {
jQuery.sap.log.fatal("clausePath not defined");
}
}
});
An example of one of the dependent controls is below.
jQuery.sap.require("sparqlish.control.conceptMenu");
jQuery.sap.require("sparqlish.control.conceptFilters");
jQuery.sap.require("sparqlish.control.addClause");
sap.ui.core.Control.extend("sparqlish.control.conceptClause", {
metadata : {
properties : {
concept : "object"
},
events : {},
aggregations : {
_concept : {
type : "sparqlish.control.conceptMenu",
multiple : false
},
_conceptFilters : {
type : "sparqlish.control.conceptFilters",
multiple : false
},
_addClause : {
type : "sparqlish.control.addClause",
multiple : false
}
}
},
init : function() {
var self = this;
var conceptSelect =function(oEvent){
var currentModel = self.getModel("queryModel");
var currentContext = self.getBindingContext("queryModel");
var currentModelData = currentModel.getProperty("", currentContext);
currentModelData.conceptFilters = [];
currentModelData.clauses = {};
var sConcept = oEvent.getParameter("concept");
var oMetaModel = self.getModel("metaModel");
var oConcept=oMetaModel.getODataEntitySet(sConcept);
self.setConcept( oConcept);
self.oEntityTypeModel = new sap.ui.model.json.JSONModel();
self.oEntityTypeModel.setData(oMetaModel.getODataEntityType(oConcept.entityType));
self.setModel(self.oEntityTypeModel, "entityTypeModel");
self.getAggregation("_conceptFilters").setModel(self.oEntityTypeModel, "entityTypeModel");
self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
currentModel.refresh();
self.rerender();
};
self.setAggregation("_concept", new sparqlish.control.conceptMenu({
selected : function(oEvent) {
self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
},
changed : conceptSelect
}).bindElement("queryModel>"));
self.setAggregation("_conceptFilters", new sparqlish.control.conceptFilters().bindElement("queryModel>"));
self.setAggregation("_addClause", new sparqlish.control.addClause({
pressed : function(oEvent) {
alert("concept");
}
}).bindElement("queryModel>"));
},
renderer : function(oRm, oControl) {
oRm.addClass("conceptClause");
oRm.write("<div ");
oRm.writeControlData(oControl);
oRm.writeClasses();
oRm.write(">");
oRm.write(sap.ui.getCore().getModel("i18nModel").getProperty("conceptClauseFind"));
oRm.renderControl(oControl.getAggregation("_concept"));
oRm.renderControl(oControl.getAggregation("_conceptFilters"));
oRm.write(" ");
oRm.renderControl(oControl.getAggregation("_addClause"));
oRm.write("</div>");
}
});
I have json array in the following format,
$scope.data = [{
"values" : [["2 Day", 103.89], ["NextDay", 107.41], ["Ground", 428.75]],
"key" : "FedEx"
}, {
"values" : [["Ground", 117.8], ["NextDay", 10], ["2 Day", 15]],
"key" : "UPS"
}]
I need to sort it in to the following format :
$scope.data = [{
"values" : [["2 Day", 103.89], ["NextDay", 107.41], ["Ground", 428.75]],
"key" : "FedEx"
}, {
"values" : [["2 Day", 15], ["NextDay", 10], ["Ground", 117.8]],
"key" : "UPS"
}]
How can I do it using Angularjs?
A similar data set for which I want similar sorting to be applied, but here I have time (in long format) instead strings.
$scope.data1 = [{
"values" : [[1359072000000, 103.89], [1365116400000, 107.41], [1357516800000, 428.75]],
"key" : "FedEx"
}, {
"values" : [[1357516800000, 117.8], [1359072000000, 100], [1365116400000, 15]],
"key" : "UPS"
}];
To be formatted as
$scope.data1 = [{
"values" : [[1359072000000, 103.89], [1365116400000, 107.41], [1357516800000, 428.75]],
"key" : "FedEx"
}, {
"values" : [[1359072000000, 100],[1365116400000, 15], [1357516800000, 117.8], ],
"key" : "UPS"
}];
Natural sorting can be applied in js like this. Natural sorting is required since strings in your array contains numbers.
function strcmp(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
function natcmp(a, b) {
var x = [], y = [];
a[0].replace(/(\d+)|(\D+)/g, function($0, $1, $2) { x.push([$1 || 0, $2]) })
b[0].replace(/(\d+)|(\D+)/g, function($0, $1, $2) { y.push([$1 || 0, $2]) })
while(x.length && y.length) {
var xx = x.shift();
var yy = y.shift();
var nn = (yy[0]-xx[0]) || strcmp(yy[1],xx[1]);
if(nn) return nn;
}
if(x.length) return -1;
if(y.length) return +1;
return 0;
}
Apply sorting in your array using javascript sort function as shown below.
$scope.data = $scope.data.map(function(d){ d.values = d.values.sort(natcmp); return d; });
Natural sorting is not needed for the second dataset. To sort the array in descending order by time, try this.
$scope.data1 = $scope.data1.map(function(d) {
d.values = d.values.sort(function(a, b) {
return new Date(b[0]) - new Date(a[0])
});
return d;
});
for displaying the array with some ng-repeat, you could use a filter
https://docs.angularjs.org/api/ng/filter/filter
There is orderBy filter but you can create your own filters.
for the data model to be sorted and not only for presentation, you could use the javascript sort function for arrays and give it a sorting implementation which compares 2 elements.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
I am trying to filter my JSON object by a specific property value set to Log: true
If an object has this property set to false, I want to filter it out. Here is an example of the JSON structure:
$scope.Main =
{
"MyBook" :
{
"Title": "The Road",
"Type" : "Text",
"Log" : false
},
"MyCat":
{
"Name" : "Penny",
"Type" : "Pet",
"Log" : true
},
"Car":
{
"Make" : "Toyota",
"Model" : "Camry",
"Type" : "Vehicle",
"Log" : false
}
}
As you can see, the objects themselves are not similar, but they all contains a log property.
Online Demo
This is how I would filtered an object while searching for a property value equals true
var sampleObj = {/* you sample object*/};
var filtered = Object.keys(sampleObj).reduce(function(arr,prop){
if(Object.keys(sampleObj[prop])
.filter(function (p) {return p === "Log";})){
if(sampleObj[prop].Log==true){
arr.push(sampleObj[prop]);
}
}
return arr;
},[]);
console.log(filtered);
Since you are using angular probably you would want to use a custom filter instead:
Something close to:
custom filter:
angular.module('myApp', []).filter('myFilter', function() {
return function(sampleObj, param1) {
return Object.keys(sampleObj).reduce(function(arr,prop){
if(Object.keys(sampleObj[prop])
.filter(function (p) {return p === "Log";})){
if(sampleObj[prop].Log==param1){
arr.push(sampleObj[prop]);
}
}
return arr;
},[]);
};
});
and in your html
<li ng-repeat="item in sampleObj | myFilter: true">
try using the underscorejs library.
you can use some of their functions like _.filter and _.has to filter the list.
here's an example of how i would try to implement that object:
var filtered = _.filter($scope.Main, function(obj) {
return _.has(obj, "Log") && obj.Log;
}
Use a custom Angular filter:
.filter('filterLog', function(){
return function(items){
for (var item in items) {
if (items[item].Log === false) delete items[item];
}
return items;
}
})
Then, in your view, you could output the filtered list like so:
<li ng-repeat="(key, value) in Main | filterLog">{{value}}</li>
If you need to use it in a controller, you could:
$scope.filtered = $filter('filterLog')($scope.Main);
Demo