How can I display my element dynamically based on data's fields? - javascript

I have my data in below:
fakeData = [
{
"option_name" : 'optionA',
"display_line_num" : 1
},
{
"option_name" : 'optionB',
"display_line_num" : 2
},
{
"option_name" : 'optionC',
"display_line_num" : 2
},
{
"option_name" : 'optionD',
"display_line_num" : 3
},
{
"option_name" : 'optionE',
"display_line_num" : 4
},
{
"option_name" : 'optionF',
"display_line_num" : 4
}
];
and I am trying to make my html look like desired look
The display_line_num represent which row the element should be placed.
I am pretty lost on how to approach to this.
my try here
I tried to create a hashmap where key is the line number, and value is how many options are in that line number. However, I am still lost.
I am stuck for a long time, and I wonder what are some good ways to approach this?
I know that we may need to use *ngFor, for example , <li *ngFor="let item of items;"> ....
but there will be a nested for loop which one row can have several options, and sometimes a row only has a option. How can I handle these circumstances?
Can someone give me a hint?
Thank you!

I have updated your mapDatas function:
mapDatas( data: any ){
const dataObj:any = {}
this.fakeData.forEach(data => {
const {display_line_num: key, option_name: value} = data;
dataObj[key] = dataObj[key] ? [...dataObj[key], value] : [value]
});
return Object.values(dataObj);
}
Here we are iterating through the fakeData array and pushing each item's option_name value in a nested array and the index of each array is the display_line_num of each item. So, when more than one element have the same display_line_num, they will be put in the same array.
And finally, we are returning the object converted to an array. Now you can iterate through each item using ngFor in your template.

Related

Selecting specific field(child field?) from firebase in react Native

Here is my firebase:
"Locations" : {
"location01" : {
"image" :
"https://www.senecacollege.ca/content/dam/projects/seneca/homepage-assets/homepage_intl.jpg",
"instructorName" : " OSMAN H.",
"place" : "Seneca, Scarborough",
"timing" : "TBA"
},
"location02" : {
"image" : "https://media-exp1.licdn.com/dms/image/C561BAQHrVTRjljcYnw/company-background_10000/0?e=2159024400&v=beta&t=fp0LWqyEnnXvxjzzdfuCHhX2jflJyhAkS0lMLXsPFw0",
"instructorName" : "AIYAZ NOOR",
"place" : "UTSC, Scarborough",
"timing" : "4 PM - 6 PM"
}
},
I know that if I get the data like this, then I can select/filter the specific field I want.
let locationsRef = db.ref('/Locations');
locationsRef.once('value', snapshot => {
let data = snapshot.val()
let locationsList = Object.values(data)
console.log(locationsList);
})
This unfortunately will give all the data as an array and displays each object. If the /locations branch had many records, it would take up space and in my opinion not best practice. Is there any way to select the 'place' field ONLY. The keys 'location01' and 'location02' can be anything. So I can't do something like (location/location01), this would take me into specific branch then. I want to get the 'place' field from all the branches.
I researched alot and had no luck. Any ideas/help are much appreciated!
Thank you in advance
I think what you are looking for is the .map function in JavaScript.
You can map over your locations object like this:
const locationsRef = db.ref('/Locations');
locationsRef.once('value', snapshot => {
let data = snapshot.val()
let locationsList = Object.values(data)
locationsList.map((location, index) => {
console.log(location.place);
// here you can do whatever you want with every single location object
// for example return a View that displays only the place
return <View><Text>{location.place}</Text></View>
});
});
You can read more about the .map function in the MDN docs here.
P.S.
I changed your use of let to const in case your DB data is a constant that you are not changing in this particular View.

how to filter ng-reapet values according to my ng-model?

Beginner in Angular, so it might sound a little silly question, but couldn't find an answer yet.
I have two select boxes -
one which describes a module which I use as ng-model=module ([x,y,z]).
The second one is an array which in each index I have an array with 3 attributes - id, name and module( [1, "first", x])
I am using ng-repeat for my second select box and I want to filter according to the module and the third index.
Basically, it's something like that: "option in options | filter: module === secondbox[2]", but obviously I'm doing something wrong, maybe by syntax.
Please assist me to execute it right. Thanks!
I think it would be best to write a custom filter for this:
.filter('moduleMatch', function() {
return function(items, module, itemIndex, moduleIndex) {
let out = [];
// make sure a filter value was supplied
if (module) {
items.forEach(i => {
if (i[itemIndex] === module[moduleIndex]) {
out.push(i);
}
});
// return the items that matched the filter value
return out;
}
// no filter value was supplied - return the unfiltered collection
return items;
}
})
Then use it in the second select:
"option in options | moduleMatch: module:2:2"

Iterate over object properties

In my aurelia app, I need to display a list of elements. For performance reasons, these elements are stored as a javascript object instead of an array. That is what the object looks like :
var x = {
0 : {...},
3 : {...},
5 : {...},
}
Here is the part of the template where I display these elements :
<template>
<ul>
<li repeat.for="property of object | ObjectToArray">
${ property.text }
</li>
</ul>
</template>
As you can see, I'm currently using a value converter to be able to iterate over my object properties. The value converter simply converts the object to an array :
export class ObjectToArrayValueConverter {
toView(data : {[key : string] : any}) : any[] {
var array = [];
for (var key in data) {
array.push(data[key]);
}
return array;
}
}
This solution works very well as long as properties do not get removed or added to the object after the list has been rendered for the first time. The reason is that the value converter only gets called once.
In my case, however, I need my list to stay up to date whatever happens.
I know I could create a function that could be manually called every time the object is modified, but that would add some unwanted complexity in business logic.
Is there an aurelia functionality that coud help me achieve what I want ? I could not find any help in the docs. Thank you !
You can get the keys using Object.prototype.keys and call Array.prototype.map on it to get an array every time you want to list it out.
var obj={...}; //Object with many keys
var genArray = Object.keys(obj).map(function(key){return obj[key];})
//Using as a function
function getKeysAsArray(obj){
if(!obj){
return [];
}
return Object.keys(obj).map(function(key){return obj[key]});
}
There's an easier strategy, as shown in the docs:
export class KeysValueConverter {
toView(obj) {
return Reflect.ownKeys(obj);
}
}
Usage:
<p repeat.for="greeting of friends | keys">${greeting}, ${friends[greeting].name}!</p>

Angular 2 Filter array and append data

Quick one, I've 2 arrays/ objects. One contains all items the other contains selected ID's from the first array.
My question is, what is the best way to loop through both arrays find selected items from the second array and if they are true append data to first array. What I'm trying to do is append true to the first array if the ID's match.
For example something like this:
this.categories.filter(
category => {
this.user.category_ids.filter(
selected => {
if(selected == category._id) {
var data = {'selected': true};
category.push(data);
}
}
);
console.log(category);
}
);
At the moment I'm looping through categories object then through user.category_ids and if the ID's match I want to append selected: true to first array object, if this makes sense. I get error:
core.es5.js:1084 ERROR TypeError: category.push is not a function
Which I don't understand why. I've also tried splice.
Also to me this doesn't seem like best approach, because I've 12 items in first array. And if all 12 are selected, second array will have 12 items. So looping through 12 * 12 to me is little expensive, memory wise.
You can try something like this:
this.categories.map(category => {
category.selected = this.user.category_ids.indexOf(category._id) !== -1;
return category;
});
if (selected == category._id) {
category['selected'] = true;
/* you can build a interface for category
* or declare category as any
* then you can write it as the below
*/
// category.selected = true;
}
push is to add a new item to an array.
Kindly clarify if categories is an array of objects? If yes then you cant use push to add a new object since each element in the categories is an object and object don't have push method. Add new property to object instead using
category.selected=true or
category['selected']=true;
Assumptions:
this.user.category_ids is a 'array of objects' as you are using 'category._id'.
if ids match you want to add a key 'selected' whose value is true , to that object whose id is matched .
Solution:
- You are getting this error category.push is not a function because category is an object not an array ,push only works with array.
if(selected == category._id) {
category['selected']=true;
}

Loop through object keys and add tags to values

I am trying to render an HTML page from my database using JavaScript and JSON data. However, some of the data should be rendered as images while others should be rendered as links. How can I change or add HTML tags?
In the database I store the content without any HTML attributes:
{
"projects" : {
"one" : {
"a" : "E-Network",
"b" : "/images/projects/unfccc/logo.gif",
"c" : "/unfccc",
"d" : "The E-Network"
},
"two" : {
"a" : "User Experience Design",
"b" : "/images/projects/rex/logo.gif",
"c" : "/rex",
"d" : "Rejseplanen Experience"
}
}
}
Using React I then pass down an Object with key value pairs to a message component. Then I render the contents of project.one inside individual tags using Object.keys in a callback method:
class Message extends React.Component {
render(){
function mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
});
}
return (
<div>
{mapObject (this.props.data.one, function(key, value) {
return <h1 key={key}>{value}</h1>
})}
</div>
)
}
}
export default Message;
which outputs:
But how can I control what tag each key value pair is rendered with? The only solution I see is nesting four Object.keys inside each other and flattening the database structure, but I don't think this is a very good solution, can you tell me a better way to render my data inside HTML tags?
You are always returning a <div><h1></div>.
If you detect an image or link use the appropriate tag image or a href

Categories