Here is my simple code:
in app.js:
$http.get($rootScope.config.app_ws+'jsons/json.json').success(function(response) {
$rootScope.config.app_genres = response;
});
the json.json:
{
"genres":{
"Ambient":{},
"Alternative":{},
"Blues":{},
"Chillout":{},
"Classical":{},
"Country":{},
"Dance":{},
"Dubstep":{},
"Electronic":{},
"Folk":{},
"Grunge":{},
"Hip Hop":{},
"House":{},
"Indie":{},
"Jazz":{},
"Latin":{},
"Metal":{},
"Opera":{},
"Pop":{},
"Punk":{},
"R&B":{},
"Reggae":{},
"Rock":{},
"Ska":{},
"Soul":{},
"Vocal":{},
"Funk":{},
"Lounge":{},
"Geek":{}
}
}
the HTML:
<div ng-repeat="i in config.app_genres.genres">
<span class="span3x3" ng-if="$first">Others</span>
<span class="span3x3">{{i}}</span>
<div class="row-fluid" ng-if="($index%3) == 1"></div>
i'm just trying listing in html all the json genres names , so for example Alternative, Ambient, Pop, Rock, etc but actually it prints {} instead of the genre Name.
What's happening?
Genres is an object, not a array. This means it doen't have a list of elements, but a list of (key, value) pairs. In your case, the keys are the names of the genres and the values are empty objects.
What happens is that ng-repeat iterates by default on the values. So you have several options:
If you only need the genre names, try a JSON array instead:
{
"Genres": [
"Pop",
"Rock",
...
"Jazz"
]
}
If you need further details, iterate on both keys and values:
<div ng-repeat="(genre, details) in config.app_genres.genres">
With a JSON like:
{
"Genres": {
"Jazz": {
"songs": 50
},
"Pop": {
...
}
}
}
Now that I look at the JSON file it actually prints the right value.
If you see, you are using an "object literal" while you should have an array instead
"Genres": ["Ambient", "Alternative", .., "Jazz"]
At the very moment, the value of every genre is actually { } which is the value that is printed in your HTML.
Related
This is the original data:
const myArray = [
'id': 'stuff', 'location': 'stuff2',
'phone_numbers'['stuff3','stuff4'], "address" : 'stuff5'
]
// so far, I'm getting the correct values back in an array from an API.
console.log(myReturnedArray) = [
{
"id": "10001",
"brand": "Best Brand",
"center_name": "Best Brand Texas",
"status": "Active",
"emails": [
{
"CreateDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"emailAddress": "test#test.com",
"firstName": "Test",
"lastName": "Tester",
"id": "0eac8839-6e61-42f5-b55f-e2fa17b0262b",
"licenseType": "Unlimited",
"updateDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"userType": "STORE"
}
],
"licenses": [
{
"CreateDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"licenseType": "STORE",
"status": "ASSIGNED",
"updatedBy": "SYSTEM",
"updateDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"renewDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"expirationDate": "October 12, 2022 at 10:59:09 AM UTC-5",
"id": "0eac8839-6e61-42f5-b55f-e2fa17b0262b"
}
]
}
]
*** whenever i try a map function i get the error "Objects are not valid as a React child (found: object with keys {id, brand, center_name, status, emails, licenses}). If you meant to render a collection of children, use an array instead." ***
I would like to display each value from my returned array in a div or span, but I keep getting errors like cannot get properties of undefined reading map, or a unique keys error whenever I try something.
// I would like to have each value from the object inside of myReturnedArray to use in my return statement.
return (
<span>your email is : "test#test.com", </span>
<div> your first name is : "Test" </div>
<span> your last name is : "lastName": "Tester", </span>
);
Anytime I try and use a map function like this in my return statement:
{ myReturnedArray.map(item => <div>{item.name}</div>) }
It returns myReturnedArray is undefined. But when I do:
{ JSON.stringify(myReturnedArray) }
It renders returnedArray as a string correctly. So I know the return method has access to the array; I just can't display it.
You don't have a name property in your array items.
Here is an example of code with your data that displays its content properly:
https://codesandbox.io/s/vigorous-euclid-nzbcp0?file=/src/App.js:24-283
Final solution: codeSandbox
Explantion to the errors you got:
1: The data structure you updated is very complex and nested with object inside array and arrays inside this object and so on. So, of course it will throw an error "Objects are not valid as a React child".
You can't put an entire object between div tags, You need to map this object fields one by one to have single values between each div.
2: About the "unique key" issue - this is a warning, not an error. It's always showing this warning when you map object items to elements without adding the key attribute to each item.
So in order to get rid of this warning, Just add the key attribute to the divs that are returned inside a map function, and choose some uniqe key.
How can you map it like this?
As I understood, The structure you have is one big array - myReturnedArray, and in this array you will have many(now you have just one) objects when each one contains:
An emails array which contains object(s).
An licenes array which contains object(s).
Some other key:value pairs.
In order to map myReturnedArray correctly - You'll need a ForEach loop that will map every big object inside of it, and then return all the mapped objects together.
First, I created an helper function that maps an array of objects(without nested arrays or something) to HTML div tags.
And I will use this function in order to map the emails and licenes arrays.
Helper function:
const mapArrayOfObjects = (array) => {
const arrayAsDivs = array.map((obj, index) => {
const mappedObj = Object.entries(obj).map(([key, value]) => {
return <div key={key}>{key + ' : ' + value}</div>;
});
return (
//Returns a div with one object of the array and space after it.
<div key={'nestedObj' + index}>
{mappedObj}
<br />
</div>
);
});
return arrayAsDivs; //The function returns ALL the mapped objects in the array.
};
It looks complex, but all it does is going through the array, and mapping each object to a big div tag with all the key:value pairs as nested divs. (And I added the br to have some space between the objects. (In your case you have just one object in the emails, but I assume that there would more.)
Using forEach
In this function, I map all the big objects that are inside myReturnedArray:
const allObjectsHTML = [];
myReturnedArray.forEach((obj) => {
const emailsAsDivs = mapArrayOfObjects(obj.emails);
const licensesAsDivs = mapArrayOfObjects(obj.licenses);
const otherFields = Object.entries(obj).map(([key, value]) => {
if (!Array.isArray(value)) {
return <div key={key}>{key + " : " + value}</div>;
}
});
const objectAsHTML = (
<div>
<div>{otherFields}</div>
<div>
<h3>Emails:</h3>
{emailsAsDivs}
</div>
<div>
<h3>Licenses:</h3>
{licensesAsDivs}
</div>
</div>
);
allObjectsHTML.push(objectAsHTML);
});
What this function does in each iteration:
Use the helper function to map the emails,licenses arrays.
Map the rest of the key:value pairs in the object.
Push the mapped object to the allObjectsHTML array that will contain all the mapped objects.
And for the last step
Now all you need to do is that in your JSX code you return:
return (
<>
{allObjectsHTML.map((obj,index) => (
<div key={"bigObj"+index}>{obj}<hr/></div>
))}
</>
);
This returns a mapped version of the allObjectsHTML.
This is the codeSandbox of my code.
And in case you want to check if it works on many objects in your array: second codeSandbox.
Note: If you know for sure that you have just one object in the myReturnedArray - then you don't need to use forEach. See this code.
The initial format of your data is flawed, it's not a JS syntax. To get it done properly first you have to make it a proper data container. For this kind of task you don't need arrays, you have an object for that.
const data = {
id: 'stuff',
location: 'stuff2',
address: 'stuff5',
phone_numbers: ['stuff3','stuff4']
}
Then in your React Components return section you may ve it done like this:
const {id, location:dataLoc, address, phone_numbers:phNums} = data
const [num1, num2] = phNums
return (
<div>{dataLoc}</div>
<span>{address}</span>
//etc
)
I also reformatted yopur code a bit to meet the JS naming conventions. We use camelCase here, so no phone_numbers (snake_case is for Py)but phNum.
I have a rather simple JSON file that I'm trying to access the items inside of. I had this working but for some reason it no longer works and it is making my brain hurt. Here is the JSON:
[
{
"firstItem":
{
"message": "firstItem is working",
"value": ["hello", "world"]
}
},
{
"secondItem":
{
"message": "secondItem is working",
"value": ["hello", "aliens"]
}
}
]
Note that there is an array of two objects (firstItem and secondItem) which have objects within them (message and value). I am trying to access these items like so:
// the json from above is equal to json
json.map(items => {
console.log(items.firstItem.message)
console.log(items.firstItem.value.join(", "))
console.log(items.secondItem.message)
console.log(items.secondItem.value.join(", "))
})
As always, any help would be appreciated.
Edit: I put the closing parenthesis on the joins. This was not part of my issue. I guess I'm too used to VSCode doing it for me. :)
You were missing a closing parentheses on your console.log join statements. The second and larger problem was that with map you are looping through firstItem first and then secondItem, so in the first iteration map doesn't know what secondItem is. I changed the names of each object to "item" and halved the code inside map to solve the problem:
const json = [
{
"item":
{
"message": "firstItem is working",
"value": ["hello", "world"]
}
},
{
"item":
{
"message": "secondItem is working",
"value": ["hello", "aliens"]
}
}
]
json.map(items => {
console.log(items.item.message);
console.log(items.item.value.join(", "));
})
Note that there is an array of two objects (firstItem and secondItem) which have objects within them (message and value). I am trying to access these items like so:
The example you show, shows an array containing two objects, where the first object has a property called firstItem and the second property has a property called secondItem. The properties in both the objects are objects themselves and both have two properties called message and value.
I hope that makes sense.
I have to say, I am new to the whole Vue framework. I have created a selectable table. The data selected from this table is stored in an object. This function should run in the back. So, I think I should run it the computed section. My object looks like this. I am trying to retrieve only the ids. This data is stored in the variable selected.
[ { "id": 5, "name": "CD", "box": "A5", "spot": 1 }, { "id": 2, ""name": "DVD", "box": "A2", "spot": 1 } ]
I would like to only retrieve the values from the key id. These values should be stored in an array. The array should be pushed when the submit button is clicked. I will later use this array to store this information in this array into a database.
You can find my code below. I guess I am missing something because it doesn't seem to work. How should I refer to this function that it runs automatically and that the array can be called in my controller to put it into the database?
Thanks for the help.
Template
<div class="row">
<div class="col-2">
<b-form-input v-model="date" class="form-control" type="date" value="getDate" ></b-form-input>
<pre class="mt-3 mb-0">{{ date }}</pre>
</div>
<div class="col-6">
<b-form-input v-model="description" placeholder="Enter some text"></b-form-input>
<pre class="mt-3 mb-0">{{ description }}</pre>
</div>
<!-- Submit data to db -->
<div class="col-4">
<button class="btn btn-primary custom-button-width" center-block>Request antibody pool</button>
</div>
</div>
JavaScript
data() {
return {
// data from db
data: this.testerdata,
// selected is the object where my selected data is stored.
selected: [],
// here should my id end up
selectedids: {},
description: '',
date: '',
}
},
computed: {
tester() {
var array = [];
for (var test in this.selected) {
if (test == "id") {
array += this.selected[test];
}
}
console.log(array);
}
},
methods: {
storeData: async function() {
axios.post('/panel', {
description: this.description,
date: this.date,
selectedids: this.tester(selectedids)
}).then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
}).finally(function() {
// always executed
});
},
}
The problem is with this line:
array += this.selected[test];
This the way to add an item to an array:
array.push(this.selected[test]);
Additionally, you are trying to loop over properties of this.selected, but it is an array which contains objects and has no properties. Instead, you want to loop over each object item in the array and push the id property.
And, since this.selected is an array, not an object, you should not use a for...in loop-- which is for objects-- because the order will not be guaranteed. You can read more about that in the MDN for...in docs.
You could use a normal for loop or the array's forEach loop. I'll show the forEach:
this.selected.forEach(obj => {
array.push(obj.id);
});
I understand that you might be looking for vanilla Javascript answer here.
However a lot of code can be saved if you can use RamdaJS library to solve such issues.
It has an excellent set of functions like pluck, which will do the same thing in 1 line.
let array = R.pluck('id')(this.selected);
I have a list of data displayed on my page that is broken down into divs. Each div represents an array of data in my object, pretty common.
I am trying to add a text box to my page where I can filter out the data and it will narrow down the results shown on the page as more data is entered into the text box.
For that, I added a filter on my ngFor like so: *ngFor="let x of data | filter: filterString".
My text-box then uses ngModel to filter that data down:
<input type="text" class="form-control" placeholder="Filter..." name="ruleFilter" id="ruleFilter" [(ngModel)]="filterString" (keyup)="onFilter($event)">
The issue I am having is that the filter seems to only be working with the top layer of data in my object. For example, the data below is what one of the results looks like in my ngFor loop. I can search Omaha just fine since its in the top level and it filters it down correctly.
However, If I look for something like Campus which is nested inside Attribute, it doesn't find it in the filter and no results are shown.
{
"RuleParentID": "618",
"RuleVersionID": "18",
"MappedValue": "1",
"ProcessingOrder": 1,
"KeyID": "1",
"Value": "Omaha",
"IsRuleRetired": "0",
"UserImpactCount": "0",
"Attribute": [
{
"AttributeID": "6",
"AttributeName": "Campus",
"Operator": {
"OperatorID": "3",
"OperatorName": "In List",
"SqlOperator": "IN"
},
"AttributeValue": [
{
"AttrValue": "1",
"Value": "Omaha",
"IsValueRetired": "0",
"disabled": "False"
}
]
},
{
"AttributeID": "14",
"AttributeName": "Grade",
"Operator": {
"OperatorID": "1",
"OperatorName": "Greater Than",
"SqlOperator": ">"
},
"AttributeValue": [
{
"AttrValue": "14",
"Value": "14",
"IsValueRetired": "0",
"disabled": "False"
}
]
}
]
}
Is there any way to have the model look at all layers of the object for my binding instead of just the top layer (which I only assume its doing at this time) ?
Update: Here is a plunker of what my basic setup is like: https://plnkr.co/edit/eywuWmPRseUkmVPbTEOf?p=preview
You will see the data model that searches by the top level properties just fine, but when I search for something nested, I don't get any results back.
If I understand well the question, I think that to flat the data will help you:
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;
};
let newData = flattenObject(data);
Code source: https://gist.github.com/penguinboy/762197
To achieve expected result , use below option
1.In your component below variable
jsonVal:any=JSON; // for using JSON.stringify and indexOf
Use *ngIf to filter value from input with indexOf
<input type="text" [(ngModel)]="filterString">
<div *ngFor="let data of result">
<div *ngIf="jsonVal.stringify(data).indexOf(filterString)!= -1">{{data| json}}</div>
</div>
code sample for reference - https://stackblitz.com/edit/angular-ht2afv?file=app/app.component.html
Just for testing , I have added another Object with Campus2 and Omaha2
When filtering on a nested property of data you can use the map function or similar.
This will be in your component and not the template. Filtering using pipes in the template is discouraged by the Angular team for performance reasons.
Instead I would do something like this:
const data = [{//your data}]
let filteredData = [];
data.map(val => {
if (val.Attribute.filter(name => name.AttributeName === "Foo").length > 0) {
filteredData.push(val)
}
});
I am assuming your data is an array of objects.
Beware I am mutating my data object. To avoid this you do this:
const data = [{//your original data}]
const dataToFilter = JSON.Parse(JSON.stringify(data))
This will make copy of your data without references to your original object. Useful if you want to clear your filter. Not useful if your data object contains functions.
On re-reading your question I think this is not the solution you were looking for but rather a method to look anywhere in the data. For this you should probably flatten your data as suggested by Zelda7. Another approach would be to extend a filtering method to explicitly filter on all relevant fields.
HTML code:
<li ng-repeat="obj in objects">{{obj.name}} <a ng-click="remove($index)">x</a></li>
JavaScript code:
$scope.remove = function(index){
$scope.objects.splice(index, 1);
}
JSON data:
{
"0": { "name": "name1" },
"1": { "name": "name2" }
}
When remove() is called, I get TypeError: $scope.objects.splice is not a function, here I know $scope.objects is not an array and so splice() will not work.
Is there any method to remove the selected index??
Thanks in advance...
Since you're using a json object and not an array you can use ng-repeat like this
<li ng-repeat="(key,value) in objects">{{value.name}} <a ng-click="remove(key)">x</a></li>
So that the remove method can delete current list element by key:
$scope.remove = function(key) {
delete $scope.objects[key];
}
Here's a plunker.
$index is quite confusing in cases like this as it is dynamic whereas the keys are not.