Angular 5 formBuilder - Nested Array and For loop - javascript

Im working on a profile editing page in an ionic app everything works as fine with all the top level items of the users profiles info (name, email, sex etc..)
I have hobbies stored in an array off this main json node (using Firestore) so its 1 level deep off the main node..
I cant seem to figure out how to use form builder with it. I suspect I am going wrong on 2 points, 1 being how I am using formbuilder and 2 on the merge fuction as it doesnt take into account nested structures which I am also unsure how to approach.. any help would be awesome.
_buildForm() {
this.form = this.formBuilder.group({
displayName: [this.options.displayName] || '',
dob: [this.options.dob] || '',
sex: [this.options.sex] || '',
city: [this.options.city] || '',
country: [this.options.country] || '',
bio: [this.options.bio] || '',
hobbies: this.formBuilder.group( this.options.hobbies )
});
// Watch the form for changes, and
this.form.valueChanges.subscribe((v) => {
this.merge(this.form.value);
});
}
merge(settings: any) {
for (let k in settings) {
this.settings[k] = settings[k];
}
return this._save();
}
_save() {
// this function then save the data back to firestore using a simple update of the entire json output
}

You need these
_buildForm() {
this.form = this.formBuilder.group({
displayName: [this.options.displayName] || '',
dob: [this.options.dob] || '',
sex: [this.options.sex] || '',
city: [this.options.city] || '',
country: [this.options.country] || '',
bio: [this.options.bio] || '',
hobbies: this.formBuilder.group([])
});
if(this.options.hobbies.length>0){
this._setHobbiesForm(this.options.hobbies);
}
}
//To build the hobby gorm
_buildHobbyForm(hobby) {
var hobbyForm = this.fb.group({
Name: hobby.Name||''
});
return hobbyForm ;
}
//To attach the hobbies form with data back to main form
_setHobbiesForm(hobbies) {
const hobbiesFGs = hobbies.map(hobby=> this._buildHobbyForm(hobby));
const hobbiesFR = this.fb.array(hobbiesFGs);
this.form.setControl('hobbies', hobbiesFR);
}
//To get form values for saving
_prepareSaveInfo(){
const formModel = this.form.value;
//deep copy of hobbies
const hobbiesDeepCopy= formModel.hobbies.map(
(hobby) => Object.assign({}, hobby)
);
const profile={
displayName: formModel.displayName as string,
sex: formModel.sex as string,
dob: formModel.dob as string,
city: formModel.city as string,
country: formModel.country as string,
hobbies:hobbiesDeepCopy
}
return profile;
}
_save() {
let dataToSave=this._prepareSaveInfo();
console.log(dataToSave);
}
This is the way to handle array inside forms in angular , if it doesn't fits your code logic exactly , take it as a example and build your logic from it , definitely this example will help you .
Iam posting an example for html also here (basically to show how to handle arrays inside the form in html)
<div formArrayName="hobbies">
<div *ngFor="let hobby of form.get('hobbies').controls; let i=index" [formGroupName]="i">
<!-- your code for hobby-->
</div>
</div>

Related

How to handle the JSON object which lack of some information?

I am using React with nextJS to do web developer,I want to render a list on my web page, the list information comes from the server(I use axios get function to get the information). However some JSON objects are lack of some information like the name, address and so on. My solution is to use a If- else to handle different kind of JSON object. Here is my code:
getPatientList(currentPage).then((res: any) => {
console.log("Response in ini: " , res);
//console.log(res[0].resource.name[0].given[0]);
const data: any = [];
res.map((patient: any) => {
if ("name" in patient.resource) {
let info = {
id: patient.resource.id,
//name:"test",
name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
} else {
let info = {
id: patient.resource.id,
name: "Unknow",
//name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
}
});
Is there any more clever of efficient way to solve this problem? I am new to TS and React
Use the conditional operator instead to alternate between the possible names. You should also return directly from the .map callback instead of pushing to an outside variable.
getPatientList(currentPage).then((res) => {
const mapped = res.map(({ resource }) => ({
id: resource.id,
// want to correct the spelling below?
name: "name" in resource ? resource.name[0].given[0] : "Unknow",
birthDate: resource.birthDate,
gender: resource.gender,
}));
// do other stuff with mapped
})

Update object array without adding new properties from another object

Is it possible to update only the existing property values of an object without adding new properties from another object?
Here is my example.
form = {name: '',email: ''};
data = {name: 'sample', email: 'sample#gmail.com', datofbirth: '6/2/1990' };
form = {...form, ...data};
console.log(form);
Result:
{"name":"sample","email":"sample#gmail.com","datofbirth":"6/2/1990"}
Expected Result:
{"name":"sample","email":"sample#gmail.com"}
I dont want the dateofbirth or any new property added on my form object.
Not sure this is what you want, hope it helps
const form = { name: '', email: '' };
const data = {
name: 'sample',
email: 'sample#gmail.com',
datofbirth: '6/2/1990',
};
Object.keys(form).forEach(key => {
if (data.hasOwnProperty(key)) {
form[key] = data[key];
}
});
console.log(form);
Only add the keys you want in the spread rather than the whole object
form = { ...form, name: data.name, email: data.email };
Iterate over all the keys in form and generate a new object using Object.assign and spread syntax.
const form = {name: '',email: ''},
data = {name: 'sample', email: 'sample#gmail.com', datofbirth: '6/2/1990' },
result = Object.assign(...Object.keys(form).map(k => ({[k] : data[k]})));
console.log(result);

Set correct value for each property in an object

I have a problem in pushing input into array. I have an array with some properties and I'm going to push some value into it, but I have no idea how to tell which value is for which property.
This is my array that I want to push into it:
validInput: [{
image: avatar1,
name: '',
email: '',
passwrod: '',
phone: '',
revenue: '',
create_date: '',
age: '',
id: ''
}]
This is my function that pushes into the array:
validation(value, REGEX) {
if (REGEX.test(value) === true) {
this.state.validInput.push(value);
this.setState({
validInput: this.state.validInput
});
} else {
console.log('error');
}
}
If I understood correctly and you wish to convert your object inside validInput array into an array of objects you can do this:
Let's say we are looking to get an array of objects with the following format:
{keyName:key,keyValue:value}
we can do something like that:
const newArray = new Array();
Object.keys(this.validInput[0])
.forEach(singleKey => {
newArray.push({
keyName:singleKey,
keyValue:this.validInput[0][singleKey]
})
})
// finally - we will have the newly formatted array in newArray
I think you should have some unique way of identifying the object you want for filtering process like id, name etc. For modified function,
validation(id, value, REGEX) {
if(REGEX.test(value)){
this.state.validInput.map((user) => {
if(user.id === id) {
user.PROPERTY_YOU_NEED_TO_UPDATE = value
}
}
}
}
Since this validInput might receive another object better use to identify it using if(user.id === id). If validInput won't receive another there is no point to use array of objects.
validInput: {
image: avatar1,
name: '',
email: '',
passwrod: '',
phone: '',
revenue: '',
create_date: '',
age: '',
id: ''
}
If it's like above you can just edit the property you want...
this.setState(state => {
let user = Object.assign({}, state.validInput);
user.PROPERTY_YOU_NEED_TO_UPDATE = value;
return { user };
})

Reactjs setState issues from form inputs

I am new to react world, I can't manage to change the state properly from form input field. I am building an employee profile that is going to be saved in a database. I created a profile in component state and get user data from the input field. But however, salary and headline fields are not changing while OnChange event handling function. Candidate is an object representation of employee
this.state = {
candidate: {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [{
experience1: '',
title1: ''
}, {
experience2: '',
title2: ''
}, {
experience3: '',
title3: ''
},
],
}
}
onChangefunction
handleChange(e) {
const name = e.target.name;
const value = e.target.value;
let copyState = Object.assign({},
this.state.candidate);
copyState.account[name] = value;
copyState.topSkills[name] = value;
copyState.salary = value;
copyState.headline = value;
this.setState(copyState);
}
The input field in salary and headline is not accepting input from user
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={this.handleChange}
/>
Can anyone provide me with help and suggest how to structure setState on onChange function?
You can simply handle changes like that for inputs:
state = {
candidate: {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [
{
experience1: '',
title1: ''
},
{
experience2: '',
title2: ''
},
{
experience3: '',
title3: ''
},
],
}
}
handleChange = (e) => {
this.setState( { candidate: { [e.target.name]: e.target.value }})
}
SetState does not required the entire object just what you are updating in the state.
Based on what you already have you could just do this
handleChange(e) {
const name = e.target.name;
const value = e.target.value;
this.setState({
account[name]: value,
topSkills[name]: value,
salary: value,
headline: value,
});
}
Though looking at your implementation, I'm not sure you will achieve what you want here... It looks like if you updated Salary, you account[name], topSkills[name], and 'headline` would be updated to the value you entered for salary.
As devserkan mentioned you can update one field at a time with setState
so what you could do is...
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={(e)=>this.setState({ salary: e.currentTarget.value })}/>
This is slightly inefficient because it would recreate the onChange function on every render. Your approach of creating a function outside the render in this case better...
handleSalaryChange { (e)=>this.setState({ salaray: e.currentTarget.value }); }
handleHeadlineChange { (e)=>this.setState({ headline: e.currentTarget.value }); }
render{ return (
<div>
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={this.handleSalaryChange)}/>
<input
name="headline"
value={this.state.candidate.headline|| ''}
onChange={this.handleHeadlineChange)}/>
...
</div>
)}
UPDATE For the handle*Change functions to work as they are currently, state would need to be updated to remove the candidate wrapper....
state = {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [
{
experience1: '',
title1: ''
},
{
experience2: '',
title2: ''
},
{
experience3: '',
title3: ''
},
],
}
Credit to udemy academy MLR — Teaching Assistant. He solved this way,the answer solve the problem.
handleChange = e => {
const candidateClone = Object.assign({}, this.state.candidate);// Shallow clone.
const accountClone = Object.assign({}, this.state.candidate.account);// Deep clone.
const topSkillsClone = Object.assign({}, this.state.candidate.topSkills);// Deep clone.
// below (let): Persists the last entered value (required).
let myHeadline = candidateClone.headline;
let myFirstName = candidateClone.account.firstName;
let mySalary = candidateClone.salary;
let myTopSkillsTitle = candidateClone.topSkills[0].title;
switch (e.target.name) {
case "headlineInput": // name in input field
myHeadline = e.target.value;
break;
case "firstNameInput": // name in input field
myFirstName = e.target.value;
break;
case "salaryInput":
mySalary = e.target.value;
break;
case "topSkillsTitleInput": // name in input field
myTopSkillsTitle = e.target.value;
break;
default:
console.log("Switch statement error");
}
accountClone.firstName = myFirstName;// Place the property value inside the deep cloned embedded object.
topSkillsClone[0].title = myTopSkillsTitle;// Place the property value inside the deep cloned embedded array.
candidateClone["account"] = accountClone;// Place the deep cloned embedded object inside the shallow cloned main object.
candidateClone["salary"] = mySalary;// Place the property inside the shallow cloned main object.
candidateClone["headline"] = myHeadline;// Place the property inside the shallow cloned main object.
candidateClone["topSkills"] = topSkillsClone;// Place the deep cloned embedded array inside the shallow cloned main object.
this.setState({candidate:candidateClone});
};

Check if property is not undefined

I'm building a node+express app and I'm filling an object with JSON that's submitted from a form in the frontend. This works, unless I leave a field empty in the form so that e.g. req.body.address.street is empty/undefined.
This will result in the error:
TypeError: Cannot read property 'street' of undefined
var b = new Business({
name: req.body.name,
phone: req.body.phone,
address: {
street: req.body.address.street,
postalCode: req.body.address.postalCode,
city: req.body.address.city
},
owner: {
email: req.body.owner.email,
password: req.body.owner.password
}
});
My question is how I can best prevent my app from crashing when values are empty. I would like to avoid manually checking each and every property in my app against undefined.
I'm wondering what the best practice is for this common issue.
I don't know if you use jQuery in your project, but if you do, you can create a mask:
// creating your object mask
var req = {
body: {
name: '',
phone: '',
address: {
street: '',
postalCode: '',
city: ''
},
owner: {
email: '',
password: ''
}
}
}
And then, you simply use the jQuery "extend" method (req2 is your submmited object):
$.extend(true, req, req2);
I've create this fiddle for you!
-
Update
Nothing related to your question, but I've just noticed that you're passing an object with a similar structure of req.body to the Business class. However, there is no need to copy property by property manually - you can make, for example, a simple copy of req.body to pass as parameter:
var b = new Business($.extend({}, req.body));
or
var b = new Business(JSON.parse(JSON.stringify(req.body)));
You can't, really. You have two options;
Use a try/ catch:
try {
var b = new Business({
//
});
} catch (e) {
// Something wasn't provided.
}
... or you can define a helper function:
function get(path, obj) {
path = path.split('.');
path.shift(); // Remove "req".
while (path.length && obj.hasOwnProperty(path[0])) {
obj = obj[path.shift()];
}
return !path.length ? obj : null;
}
... you could then replace your use of req.body.address.street etc. with get('req.body.address.street', req).
See a demo here; http://jsfiddle.net/W8YaB/

Categories