I have a property details which is a nested object.
On clicking sign-in i get a form object which has values i want to set the values from the form into the nested object. Why cannot we use the normal .(dot) operator or the [] to chain and access the nested objects and their properties.
export class AppComponent {
details:{
city:string,
dates:{
date:Date,
data:{
name:string,
comment:string
}[]
}[]
}
// this function is executed on submit of the form
onSignin(form:NgForm){
console.log(form)
this.details.city=form.value.city; // cannot set property 'city' of undifined
console.log(this.details)
}
}
I dont see any problem with your structure or code. It is possible that you may not be assigning the value correctly.
The below code gives me the correct output:
class Example {
details: {
city: string,
dates: {
date: Date,
data: {
name: string,
comment: string
}[]
}[]
} = {
city: "Tokyo",
dates: [
{
date: new Date(),
data: [
{
name: "Stackoverflow",
comment: "Hi"
}
]
}
]
}
getDetails() {
console.log(this.details.city)
this.details.city = "Hong Kong";
console.log(this.details.city)
}
}
new Example().getDetails();
This first prints "Tokyo" and then "Hong Kong".
You have just defined details as variable and not assigned any value. Since details is currently undefined, you cannot directly set the value of only one its nested objects. You can set value for city, if details was assigned to any non null value.
As the error message suggests, this.details is undefined. You need to create the object before you can set its properties. For example:
// A cast is required because `this.details` doesn't yet have the required properties.
this.details = {} as any;
this.details.city = form.value.city;
// ...
Or use an object literal instead of setting individual properties:
this.details = {city: form.value.city, dates: [ /*...*/ ]};
Related
I have a method for handling an update on this object. In this method I want to accept any field value that the object has, such as name, age, weight, eyeColor etc. If that field exists within that object, I'd like to be able to update the object dynamically for any field they pass in.
I am currently doing it incorrectly I believe with the spread operator while trying to update one field in the object. There is an error that fieldName does not exist within myObject. Does anyone know how to do this dynamically without need for a long switch statement checking each of the fields against fieldName passed in? In previous attempts tweaking with this I have successfully added a new field to the object named "fieldName" which is also not what I wanted.
If anyone has any ideas, that would be so helpful, thank you!
let myObject = {
name: 'John',
lastName: 'Smith',
age: 26,
weight: 200,
eyeColor: 'blue',
hairColor: 'blonde'
};
const handleUpdate = (fieldName: string, fieldValue: string | number) => {
if (fieldName in myObject) {
myObject = {...myObject, fieldName: fieldValue};
}
}
handleUpdate('name', 'Jack'); // Want this call to update the name of 'John' to 'Jack'
In short, you're looking for:
{...myObject, [fieldName]: fieldValue}
You can make a generalized, typesafe function to do this as follows:
function updateProp<TObj, K extends keyof TObj>(obj: TObj, key: K, value: TObj[K]) {
return {...obj, [key]: value};
}
and call it as follows:
const foo = { a: 1, b: "monkey" };
const updatedFoo = updateProp(foo, "b", "hello world")
Playground Link
You're looking for the Bracket notation property accessor:
myObject[fieldName] = fieldValue
Compared to the approach with the spread operator, this does actually update the object in place. I.e. if the reference in myObject was previously copied elsewhere, that reference will also "see" the updated field.
Whereas, by overriding the value with myObject = {...myObject}, you're creating a new object each time.
i need to convert a object with have key value to new object that contain new const named form platform and have name to value in js how to do it?
posters: [
{ platform: facebook; name: ["user test1","user test2"] },
{ platform: instagram; name: ["Ig test1","Ig test2"] },
]
in to
posters: {
facebook: ["user test1","user test2"] ,
instagram: ["Ig test1","Ig test2"] ,
}
Your input array is invalid. There are no strings around your platform values, and you're separating your object properties with a semi-colon rather than a comma. So you would need to fix that in order to proceed.
It looks as if posters is a property within a larger object so this answer will take that into account.
Use reduce on the posters array to iterate over the objects in the array and return an object where the keys are the platform names, and the values the name arrays.
Since it looks like posters is within a larger object we'll rebuild the object using the spread syntax.
const data={posters:[{platform:"facebook",name:["user test1","user test2"]},{platform:"instagram",name:["Ig test1","Ig test2"]}]};
// Create a new object
const updated = {
// Spread out the old object into it
...data,
// And replace the old `posters` property with an
// object using `reduce`. Destructure the `platform`
// and `name` properties from the object, and then
// use them to add a key and a value to the initial
// object (`acc`/accumulator)
posters: data.posters.reduce((acc, obj) => {
const { platform, name } = obj;
acc[platform] = name;
return acc;
}, {})
};
console.log(updated);
Additional documentation
Destructuring assignment
const postersArray = [
{ platform: facebook, name: ["user test1","user test2"] },
{ platform: instagram, name: ["Ig test1","Ig test2"] }
]
const postersObject = postersArray.reduce((previous, current) => {
return {
…previous,
[current.platform]: current.name
}
},{})
So I have a data options like this
data() {
return {
userPayload: {
description: '',
languageCodes: [],
facebookUrl: '',
youtubeUrl: ''
},
}
},
Later I applied some functions to fill out each attributes in data. Then when I want to submit the data using handleSaveData(), I passed this userPayload object to axios and turns out it's only read the value of description attributes.
handleSaveData() {
...
const userPayload = {
description: this.userPayload.description,
profile: {
languageCodes: this.userPayload.languageCodes,
facebookUrl: this.userPayload.facebookUrl,
youtubeUrl: this.userPayload.youtubeUrl
}
}
console.log(this.userPayload)
// this one contains value of all attributes
console.log(userPayload)
// this one only shows value of description attributes, while the others are empty string or empty array
// i expect it also return value of another attributes
// because i already access it with this.userPayload.{attributeName}
}
I already tried out deepClone userPayload object but it doesn't work
I am currently having a problem where I am trying to update an of a nested array in a Mongoose record.My schema is as follows:
const customerSchema = new mongoose.Schema({
kimCustomerId: {
type: Number,
required: true
},
addresses: [
{
created: Date,
updated: Date,
addressInfo: {
type: { type: String },
typeOfAddress: String,
careOf: String,
address: String,
addressRow1: String,
addressRow2: String,
zipcode: String,
city: String,
countryCode: String,
physicalAddressType: String,
validFrom: Date,
validTo: Date
}
}
],
.....
As you can see, the adrress array for each record holds many addresses. I want to be able to pass through an update object and update the properties inside the addressInfo nested object inside a particular array object. Here is my query as it stands:
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: { 'addresses.$': addressData } },
{ new: true }
);
and an example of an object I pass through to update a record:
{
addressId: officialAddressExists.addressId,
addressInfo: {
validTo: new Date().toISOString()
}
}
What I want to happen is, when I pass this object to the schema method, I want to select the correct address by the values 'kimCustomerId' and 'addressId' (which I have working fine) then only update the values of the 'addressInfo' nested object that I have passed and keep the ones not passed as they are, in this case the 'validTo' field but it can be any number of them updated. It is overwriting the whole 'addressInfo' nestedObject at the moment so I presume I have to do some kind of set operation on that nested object as well but I am unsure how.
Is anyone able to point me in the right direction here?
Thanks!
There is no straight way to do this in query, you can do it in your client side, something like,
// Sample body request
let addressData = {
addressId: 1,
addressInfo: {
validTo: new Date().toISOString(),
typeOfAddress: "Home",
address: "ABC"
}
};
let set = {};
for (let key in addressData.addressInfo) {
set["addresses.$.addressInfo." + key] = addressData.addressInfo[key];
}
console.log(set);
Pass set variable in to your query,
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: set },
{ new: true }
);
var users = [
{ user: "Name1",geo:{lat:'12',long:'13'} },
{ user: "Name2",geo:{lat:'12',long:'13'}, age: 2 },
{ user: "Name2",geo:{lat:'12',long:'13'} },
{ user: "Name3",geo:{lat:'12',long:'13'}, age: 4 }
];
Above is the array of objects.
Here is the for-loop I destructured and get user and age but I want lat and long also how will I do that ? I want it through destructuring and for-loop like I did with user and age
for (let { user, age = "DEFAULT AGE" } of users) {
console.log(user, age);
}
You can use this:
for (let {user, age = "DEFAULT AGE", geo: {lat, long}} of users) {
console.log(user, age, lat, long);
}
You have already successfully destructured user (simply by the property name in the object) and age (with a default value as well).
To use nested destructuring, step by step, simply put the property name geo in there as well, since that’s the next property on the objects you’re iterating over that contains your needed values:
{user, age = "DEFAULT AGE", geo} — this would yield {lat: "12", long: "13"} for geo.
To access the nested properties directly, follow the object structure:
{user, age = "DEFAULT AGE", geo: {}} — this would just validate that geo is indeed an object.
Then, list the properties you want to access in that object:
{user, age = "DEFAULT AGE", geo: {lat, long}} — this would yield "12" for lat and "13" for long.
You could even go a step further and rename those properties:
{user, age = "DEFAULT AGE", geo: {lat: latitude, long: longitude}} — this would yield "12" for latitude and "13" for longitude.
These are the basic cases for destructuring objects:
name means “just assign the entire value to name”.
{} means “check that the value to be destructured is an object or can be converted into one, i.e. is neither null nor undefined; create no variables”.
{ prop } means “get the value of prop as the variable prop”.
{ prop: rename } means “follow the prop property and get its value as the variable rename”1.
{ prop = value } means “get the value of prop as the variable prop, but assign value if prop yields undefined”2.
For the “rename” case, the rules apply recursively: rename is like name, so it can be replaced by {}, or { anotherProp }, or { anotherProp: anotherRename }, or { anotherProp = anotherDefault }, etc.
Other properties on the same object level may be added via commas, like {propA, propB}.
For arrays, similar cases exist: [] would validate that the value to be destructured is an iterable object; [a, b] has the same meaning as {0: a, 1: b}; etc.
1: Note that in the case of { prop: something } no variable prop is created.
2: “yields undefined” means that obj.prop would be equal to undefined which means either that the property exists and has the literal value undefined or that the property doesn’t exist.
If you're just trying to find a given user and return the geo for it, this will do:
users.find( u => u.user === 'Name1' ).geo;
Keep in mind, you would want to do some checks aginst your 'find result' before trying to use it. For example:
const user = users.find( u => u.user === 'Name1' );
if (user && user.hasOwnProperty('geo')) {
console.log(user.geo);
}