I have this variable in my environment.ts:
featureToggle: {"feature1": true, "feature2: false}
In a service of mine, I want to give these values to a component with a getAll-method, just like:
getAll() {
return environment.featureToggle;
}
In a component I'm having an array and call the servicemethod in my ngOnInit, where I assign the values to my array. Through *ngFor im iterating through the array.
Then I get an ERROR NG0901 IterableDiffers.find.
Yes, it might be, because it is an Object Array, so I would have to convert it in my service first to a normal Array, or assign the values to an interface to work with it?
like
interface FeatureInterface {
feature: string,
isActive: boolean;
}
But I can't even .map through my environments variable nor does forEach work. I also tried Object.keys(environment.featureToggle). Is there any way to access and iterate my properties in my environment.ts and work with them in any component?
Component:
features: FeatureInterface[] = [];
ngOnInit(): void {
this.features = this.featureToggleService.getAllFeatures()
Html:
<div *ngFor="let item of features">
{{item.feature}}
...
Check this out!
let featureToggle = { "feature1": true, "feature2": false, "feature3": true};
const result = Object.entries(featureToggle).filter((e) => e[1]).map((i) => i[0]);
console.log(result);
UPDATE 1:
Based on the requirement(as mentioned in the comments), try this:
let featureToggle = { "feature1": true, "feature2": false, "feature3": true };
const result = Object.entries(featureToggle).map((i) => {
return {
feature: i[0],
isAvailable: i[1]
}
});
console.log(result);
[ { feature: 'feature1', isAvailable: true },
{ feature: 'feature2', isAvailable: false },
{ feature: 'feature3', isAvailable: true } ]
Related
In React's this.state I have a property called formErrors containing the following dynamic array of objects.
[
{fieldName: 'title', valid: false},
{fieldName: 'description', valid: true},
{fieldName: 'cityId', valid: false},
{fieldName: 'hostDescription', valid: false},
]
Let's say I would need to update state's object having the fieldName cityId to the valid value of true.
What's the easiest or most common way to solve this?
I'm OK to use any of the libraries immutability-helper, immutable-js etc or ES6. I've tried and googled this for over 4 hours, and still cannot wrap my head around it. Would be extremely grateful for some help.
You can use map to iterate the data and check for the fieldName, if fieldName is cityId then you need to change the value and return a new object otherwise just return the same object.
Write it like this:
var data = [
{fieldName: 'title', valid: false},
{fieldName: 'description', valid: true},
{fieldName: 'cityId', valid: false},
{fieldName: 'hostDescription', valid: false},
]
var newData = data.map(el => {
if(el.fieldName == 'cityId')
return Object.assign({}, el, {valid:true})
return el
});
this.setState({ data: newData });
Here is a sample example - ES6
The left is the code, and the right is the output
Here is the code below
const data = [
{ fieldName: 'title', valid: false },
{ fieldName: 'description', valid: true },
{ fieldName: 'cityId', valid: false }, // old data
{ fieldName: 'hostDescription', valid: false },
]
const newData = data.map(obj => {
if(obj.fieldName === 'cityId') // check if fieldName equals to cityId
return {
...obj,
valid: true,
description: 'You can also add more values here' // Example of data extra fields
}
return obj
});
const result = { data: newData };
console.log(result);
this.setState({ data: newData });
Hope this helps,
Happy Coding!
How about immutability-helper? Works very well. You're looking for the $merge command I think.
#FellowStranger: I have one (and only one) section of my redux state that is an array of objects. I use the index in the reducer to update the correct entry:
case EMIT_DATA_TYPE_SELECT_CHANGE:
return state.map( (sigmap, index) => {
if ( index !== action.payload.index ) {
return sigmap;
} else {
return update(sigmap, {$merge: {
data_type: action.payload.value
}})
}
})
Frankly, this is kind of greasy, and I intend to change that part of my state object, but it does work... It doesn't sound like you're using redux but the tactic should be similar.
Instead of storing your values in an array, I strongly suggest using an object instead so you can easily specify which element you want to update. In the example below the key is the fieldName but it can be any unique identifier:
var fields = {
title: {
valid: false
},
description: {
valid: true
}
}
then you can use immutability-helper's update function:
var newFields = update(fields, {title: {valid: {$set: true}}})
I'm trying to use lodash's find method to determine an index based on one attribute. In my case this is pet name. After that I need to change the adopted value to true using setState. The problem is however; I do not understand how to combine setState and _.find()
As of right now I have this written. My main issue is figuring out how to finish this.
adopt(petName) {
this.setState(() => {
let pet = _.find(this.state.pets, ['name', petName]);
return {
adopted: true
};
});
}
This does nothing at the moment as it is wrong, but I don't know how to go from there!
In React you usually don't want to mutate the state. To do so, you need to recreate the pets array, and the adopted item.
You can use _.findIndex() (or vanilla JS Array.findIndex()) to find the index of the item. Then slice the array before and after it, and use spread to create a new array in the state, with the "updated" item:
adopt(petName) {
this.setState(state => {
const petIndex = _.findIndex(this.state.pets, ['name', petName]); // find the index of the pet in the state
return [
...state.slice(0, petIndex), // add the items before the pet
{ ...state[petIndex], adopted: true }, // add the "updated" pet object
...state.slice(petIndex + 1) // add the items after the pet
];
});
}
You can also use Array.map() (or lodash's _.map()):
adopt(petName) {
this.setState(state => state.map(pet => pet.name === petName ? ({ // if this is the petName, return a new object. If not return the current object
...pet,
adopted: true
}) : pet));
}
Change your adopt function to
adopt = petName => {
let pets = this.state.pets;
for (const pet of pets) {
if (!pet.adopted && pet.name === petName) {
pet.adopted = true;
}
}
this.setState({
pets
});
};
// sample pets array
let pets = [
{
name: "dog",
adopted: false
},
{
name: "cat",
adopted: false
}
]
I have an array in my state called columnToFilterOut. Let's say this is my array in my state: columnToFilterOut = ["first_col", "second_col", "third_col"]
I also have another array in state called rows that contains a list of dicts where there is a key called id that is corresponding to the values in columnToFilterOut. Here is an example of rows:
rows: [
{
id: "first_col",
numeric: false,
disablePadding: true,
label: "1"
},
{
id: "second_col",
numeric: true,
disablePadding: false,
label: "2"
},
{
id: "third_col",
numeric: true,
disablePadding: false,
label: "3"
},
{
id: "fourth_col",
numeric: true,
disablePadding: false,
label: "4"
}
]
As you can see, there is an extra element in there. The extra value is the one with id = "fourth_col". I want to delete all elements to make sure that both arrays match up.
Here is my delete function:
removeFromRowsById(id) {
console.log("IN REMOVE FUNCTION");
const filteredValues = this.state.rows.filter((_, i) => i["id"] !== id);
this.setState({ rows: filteredValues });
}
So I pass in an id and it should remove the value with the given id. Inside my render function, I use it like this:
Object.keys(rows).map(
(key, index) =>
!(columnToFilterOut.indexOf(rows[index]["id"]) > -1) //meaning the value doesn't exist inside of columnToFilterOut
? this.removeFromRowsById.bind(this, rows[index]["id"])
: console.log("Not deleting")
);
This isn't working. The value in the rows array is never removed. I print it to make sure. I also notice that my print statement inside of removeFromRowsById never logs to the console as though the function never actually gets called. Any help is great. Thanks!
try to change your this.state.rows.filter, to be like below. and see if it works
removeFromRowsById(id) {
console.log("IN REMOVE FUNCTION");
// 1. the original code should be === not !==, because you want to delete when the id matches.
// 2. the "i" should be on first parameter not second
const filteredValues = this.state.rows.filter((i, _) => i["id"] === id);
this.setState({ rows: filteredValues });
}
I changed the removeFromRowsById to look like this:
removeFromRowsById = index => {
console.log("IN REMOVE FUNCTION");
const newList = [...this.state.rows1];
newList.splice(index, 1);
this.setState(state => ({
rows1: newList
}));
};
And I call it regularly in the render function (without the binding). I think you needed to use the arrow function which fixed the issue and I revamped the way it deletes from the list in case what #vdj4y said was true
I'm working with angular2 ( version 5).
I make an http request an get back json.
I know how to access and use value but not the array.
and I don't find how to extract the two array inside element.
here my json:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
},
{
"nomeImpianto":"MFL2",
"descrImpianto":"Multifilo 2",
"posizione":"Place2",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 12.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
}
]
}
In the data.service.ts I have the http request and it store values on :
stream$: Observable<ImpiantoModel[]>;
here my definition of the model:
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel[];
orario: OrariModel[];
}
#orari.model.ts
export class OrariModel {
orario: string;
}
#velocita.model.ts
export class VelocitaModel{
vel : number;
}
is it the right why to define my object?
How can I use the array "vel" and "orario"?
How can I print (access) the array "vel" of machine with "nomeImpianto" = "MFL1" ?
and how can I copy the array "vel" on new array?
thank you very much!
Here is what I understood of what you want to do : get the item in your json resp and put it in your object , so the best way is to create a static method directly when you get the json response, before returning the value create this adapter adaptImpiant(jsonObj) which will do something like :
adaptImpiant(jsonObj) {
let impiantTab = [];
jsonObj.ImpiantiProva.forEach((item) => {
let impiantoModel = {};
// impiantoModel = item if the model (below) match the item;
// if not manually set all your var like your velocita
let velocita = [] // is an array or an object with an array
// if class velocita = {}
velocita = item.vel.slice(0);
// if class velocita.valuesTab = item.vel.slice(0);
impiantoModel.velocita = velocita;
impiantTab.push(impiantoModel);
}
}
Your model seems wrong in this case, because you already use a ImpiantoModel array, so just create a class with whatever you want in :
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel // or simply [];
orario: OrariModel // or simply [];
}
I'm not sure I understand you, but I'll try.
is it the right why to define my object?
It should be:
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: Array<string>;
orario: Array<string>;
}
(But I have to confess, I don't know why model and not an interface)
How can I use the array "vel" and "orario"?
What do you mean?
How can I print (access) the array "vel" of machine with
"nomeImpianto" = "MFL1"
const thisContainsTheDataFromVel = whereYourDataIsStored['ImpiantiProva'].find((item) => { item['nomeImpianto'] === 'MFL1'})['vel'];
and how can I copy the array "vel" on new array?
UPDATE after reading your comment under this answer:
I took code from your example and added what you are missing. I made it so it can be more reusable (it can be enhanced even more, but I hope you understand the code and do what you need).
copyArray(data, targetValue) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
return mfl1Data.vel;
}
return [];
}
getdata2() {
this.http.get<ImpiantoModel[]>(this.myUrl)
.subscribe(
data => {
this.variableToStoreIn = this.copyArray(data, 'MFL1');
data.forEach(item => {
this.sub$.next(item);
});
});
return this.sub$;
}
CopyArray finds the data and returns it. If you don't want it like this, but just set a value of some property to the value of vel array then you can change it to:
copyArray(data) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
this.yourVariable = mfl1Data.vel;
}
}
If this answer is sufficient, please consider to mark it as the best answer, thank you.
According to your model classes, your JSON is wrong. You should have something like this:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [
{
"vel": 24.5
},
{
"vel": 13.6
}
...
],
"orario": [
{
"orario": "17.05"
},
{
"orario": "17.06"
}
...
]
}
]
}
Your model expects ImpiantoModel.vel and ImpiantoModel.orario to be arrays of objects. In your JSON response one is an array of numbers and the other of strings.
An if you want to use it in an HTML template, considering that you have a class attribute in your .ts file like this:
private impiantoModels: ImpiantoModel[];
You could do something like this inside your .html template:
<div *ngFor="let impModel of impiantoModels">
...
<div *ngFor="let v of impModel.vel">
<p>{{v.vel}}</p>
</div>
<div *ngFor="let o of impModel.orario">
<p>{{o.orario}}</p>
</div>
</div>
This is how example of my state looks:
const INITIAL_STATE = {
contents: [ {}, {}, {}, etc.. ],
meta: {}
}
I need to be able and somehow replace an item inside contents array knowing its index, I have tried:
return {
...state,
contents: [
...state.contents[action.meta.index],
{
content_type: 7,
content_body: {
album_artwork_url: action.payload.data.album.images[1].url,
preview_url: action.payload.data.preview_url,
title: action.payload.data.name,
subtitle: action.payload.data.artists[0].name,
spotify_link: action.payload.data.external_urls.spotify
}
}
]
}
where action.meta.index is index of array item I want to replace with another contents object, but I believe this just replaces whole array to this one object I'm passing. I also thought of using .splice() but that would just mutate the array?
Note that Array.prototype.map() (docs) does not mutate the original array so it provides another option:
const INITIAL_STATE = {
contents: [ {}, {}, {}, etc.. ],
meta: {}
}
// Assuming this action object design
{
type: MY_ACTION,
data: {
// new content to replace
},
meta: {
index: /* the array index in state */,
}
}
function myReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case MY_ACTION:
return {
...state,
// optional 2nd arg in callback is the array index
contents: state.contents.map((content, index) => {
if (index === action.meta.index) {
return action.data
}
return content
})
}
}
}
Just to build on #sapy's answer which is the correct one. I wanted to show you another example of how to change a property of an object inside an array in Redux without mutating the state.
I had an array of orders in my state. Each order is an object containing many properties and values. I however, only wanted to change the note property. So some thing like this
let orders = [order1_Obj, order2_obj, order3_obj, order4_obj];
where for example order3_obj = {note: '', total: 50.50, items: 4, deliverDate: '07/26/2016'};
So in my Reducer, I had the following code:
return Object.assign({}, state,
{
orders:
state.orders.slice(0, action.index)
.concat([{
...state.orders[action.index],
notes: action.notes
}])
.concat(state.orders.slice(action.index + 1))
})
So essentially, you're doing the following:
1) Slice out the array before order3_obj so [order1_Obj, order2_obj]
2) Concat (i.e add in) the edited order3_obj by using the three dot ... spread operator and the particular property you want to change (i.e note)
3) Concat in the rest of the orders array using .concat and .slice at the end .concat(state.orders.slice(action.index + 1)) which is everything after order3_obj (in this case order4_obj is the only one left).
Splice mutate the array you need to use Slice . And you also need to concat the sliced piece .
return Object.assign({}, state, {
contents:
state.contents.slice(0,action.meta.index)
.concat([{
content_type: 7,
content_body: {
album_artwork_url: action.payload.data.album.images[1].url,
preview_url: action.payload.data.preview_url,
title: action.payload.data.name,
subtitle: action.payload.data.artists[0].name,
spotify_link: action.payload.data.external_urls.spotify
}
}])
.concat(state.contents.slice(action.meta.index + 1))
}