Angular 5 - Updating Model from Form - forms

i'm new to Angular 5 and i'm trying to make a CRUD.
i'm struggling with the "update" part.
what i'd want to do is, in my form, getting back the data of the Model to fill the input text with, then, when i click on the update button, updating the all model.
I tried so many thing from tuts and forums that i'm completly lost now. So here is what i have.
html :
<form [formGroup]="policyForm" (ngSubmit)="update()">
<mat-form-field>
<input formControlName="policyName">
</mat-form-field>
... Many other inputs
<button type="submit" color="primary">Update</button>
</form>
component :
export class PolicyUpdateComponent implements OnInit {
policyModel: PolicyModel;
policyId = +this.route.snapshot.paramMap.get('id');
policyForm: FormGroup;
formBuilder: FormBuilder;
constructor(
private policyService: PolicyService,
private route: ActivatedRoute,
private router: Router,
private fb: FormBuilder
) {
this.policyService.get(this.policyId)
.subscribe(policy => this.policyModel = policy);
}
ngOnInit() {
this.createForm();
}
createForm() {
this.policyForm = this.fb.group({
policyName: [this.policyModel.name, Validators.required]
});
}
update(id: number) {
id = this.policyId;
this.policyModel = <PolicyModel>this.policyForm.value;
this.policyService.update(id, this.policyModel).subscribe(res => {
this.router.navigate(['/policies', id, 'get']);
}, (e) => {
console.log(e);
}
);
}
}
service :
/**
* Update a policy with new parameters
* #param pm PolicyModel
*/
update(id: number, pm: PolicyModel): Observable<any> {
return this.http.put<PolicyModel>(`${environment.baseApiUrl}/${environment.apiVersion}/policies/${id}`, {pm});
}
any help would be nice.. thanks guys!

Change the code according to this.
<form [formGroup]="policyForm" >
<mat-form-field>
<input formControlName="policyName">
</mat-form-field>
<button color="primary" (click)="update()">Update</button>
</form>
Add subscription to detect form changes.
ngOnInit() {
this.createForm();
this.policyForm.valueChanges.subscribe(
(data) => {
if (JSON.stringify(data) !== JSON.stringify({})) {
this.policyModel.name = data.policyName;
}
});
}
createForm() {
this.policyForm = this.fb.group({
policyName: [this.policyModel.name]
});
}
update(id) {
this.policyModel.id = id;
id = this.policyId;
this.policyService.update(id, this.policyModel)//.subscribe(res => {
this.router.navigate(['/policies', id, 'get']);
}, (e) => {
console.log(e);
}
);
}
Sample code : https://stackblitz.com/edit/angular-wwt91u

Related

sending Angular 6 form including checkbox values not working with template driven forms

I'm trying to pass form values including checkboxes in angular 6 forms using formbuilder but I'm unable to read the value from checkbox. I am getting all the values from all the other input fields but only checkbox is not responding Here is my code:
<form [formGroup]="myGroup" (submit)="submit(myGroup.value)">
<div class="row">
<div class="col-sm-4" *ngFor="let info of myGroup.controls['myInfo'].controls; let i = index">
<label for="{{labelValue[i].name}}"> {{labelValue[i].label}}
<input type="{{labelValue[i].type}}" class="{{labelValue[i].class}}" [formControl]="info">
</label>
</div>
</div>
<div class="row">
<button class="form-control btn-sub" type=”submit”>
Submit Details
</button>
</div>
My component class:
import { ProposalService, CustomerDetails, ProposalNumber } from 'src/app/Services/Proposal-service/proposal.service';
export interface InputType{
name:string;
type: string;
label: string;
class:string;
}
export class ProposalComponent implements OnInit {
public labelValue: InputType[] = [
{name:"fname",type:"text",label:"First Name", class:"form-control"},
{name:"form60",type:"checkbox",label:"Is Collection Of form 60", class:"form-control"},
{name:"eia-num",type:"number",label:"EIA Number", class:"form-control"}
];
title = "Customer Details";
details: Observable<CustomerDetails>;
pNumber: ProposalNumber ;
public information: CustomerDetails[] = [
{name:"First Name", value:""},//
{name:"IsCollectionOfform60", value:true},
{name:"EIA Number", value:""}
];
myGroup : FormGroup;
constructor(private formBuilder: FormBuilder,
private _proposalService: ProposalService) { }
ngOnInit() {
this.myGroup = this.formBuilder.group({
myInfo: this.constructFormArray()
});
this.pNumber = <ProposalNumber>{proposalNumber: 0 ,message:"", status: ""};
}
constructFormArray()
{
const arr = this.information.map(cat => {
return this.formBuilder.control(cat.value);
});
return this.formBuilder.array(arr);
}
submit(form){
//this.loading = true;
console.log(form);
let mySelectedAddon = form.myInfo.map((currentValue,i)=> {
return { "name" : this.information[i].name , "value" : currentValue}
}
);
console.log(mySelectedAddon);
this._proposalService.loadCustomer(mySelectedAddon).subscribe((res: ProposalNumber) =>{
//this.loading = false;
console.log(res);
this.pNumber.proposalNumber = res.proposalNumber;
this.pNumber.message = res.message;
console.log(this.pNumber.proposalNumber);
return this.myGroup.value;
});
}
}
You need to use the 'change' event and pass the respective input value and event to a method onChange where you check if it's checked, then add the respective value to the formarray, if it's unchecked, remove the chosen email from the form array.
You can refer the below link:
https://stackblitz.com/edit/angular-rskaug?file=src%2Fapp%2Fapp.component.ts
Above example is useful to get the values of checkbox dynamically.

can't switch status value from 0 to 1 after calling function angular

I am working on a frontend application with Angular 5 and using rest api from backend. Actually, I am developing admin platforme and I have two web pages, one for displaying list of customers and each one has list of feedbacks and one other get you to specific feedback details.
The feedback details display these properties: account, feedback itself and the loyalty point if existed.
There is two ways, if a feedback has its loyalty point then the feedback details will show details with loyalty point value else it will show empty input for this property and if the input is successful, it will return to main list with changed value of status of feedback from false to true.
I am using rest api and for this operation I successfully tested the API:
API: PATCH /Feedbacks/:id
Here is my code:
account.service.ts:
#Injectable()
export class AccountService {
constructor(private http: Http) {}
headers: Headers = new Headers({ 'Content-Type': 'application/json' });
options: RequestOptionsArgs = new RequestOptions({headers: this.headers});
// API: PATCH /Feedbacks/:id
updateStatus(feedback: Feedback) {
let url = "http://localhost:3000/api/Feedbacks";
return this.http.patch(url + feedback.id, feedback, this.options)
.map(res => res.json())
.catch(err => {
return Observable.throw(err)
});
}
}
component.html:
<form *ngIf="feedback">
<div class="form-group">
<label for="InputAccount">Account</label>
<input type="text" class="form-control" id="InputAccount" value="{{feedback.account}}">
</div>
<div class="form-group">
<label for="InputFeedback">Feedback</label>
<textarea class="form-control" id="InputFeedback" rows="3" placeholder="Feedback">{{feedback.feedback}}</textarea>
</div>
<div class="form-group">
<label for="InputLP">LP</label>
<input type="text" class="form-control" id="InputLP" placeholder="LP" [(ngModel)]="account.lp" name="lp">
</div>
<div class="form-group" *ngIf="!edited; else showBack">
<button (click)="addLP(account,feedback)" class="btn btn-primary" data-dismiss="alert">Add LP</button>
</div>
</form>
component.ts:
#Component({
selector: 'app-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.scss']
})
export class AddComponent implements OnInit {
feedback = {};
account = {};
edited:boolean;
status: boolean;
constructor(private route: ActivatedRoute, private accountService: AccountService,
private router: Router) { }
ngOnInit() {
this.route.paramMap
.switchMap((params: ParamMap) =>
this.accountService.getFeedback(+params.get('idF')))
.subscribe(feedback => this.feedback = feedback);
this.route.paramMap
.switchMap((params: ParamMap) =>
this.accountService.getAccount(params.get('idC')))
.subscribe(account => this.account = account);
}
addLP(account:Account,feedback:Feedback){
this.accountService.updateAccount(account)
.subscribe(res => {
this.account = res as Account;
console.log(res);
if (account.lp == null){
console.log(res);
this.edited = false;
} else {
this.edited = true;
this.accountService.updateStatus(feedback)
.subscribe(res => {
feedback.status = true;
this.feedback = res as Feedback;
console.log(this.feedback);
}, err => {
console.log(err);
});
}
}, err => {
console.log(err);
});
}
back() {
this.router.navigate(['list']);
}
}
the feedback property:
public id?: number,
public feedback?: string,
public account?: string,
public status?: boolean
Where the account is a foreign key to account table:
public account?: string,
public lp?: string
When I try to switch status value automatically from false to true, the console log will return:
PATCH http://localhost:3000/api/Feedbacks2 404 (Not Found)
Any help would be appreciated! I really need to solve it. Thanks
I modified the code in account.service.ts:
updateStatus(feedback: Feedback) {
let url = "http://localhost:3000/api/Feedbacks";
var body = {status: true};
return this.http.patch(url +"/"+ feedback.id, body, this.options)
.map(res => res.json())
.catch(err => {
return Observable.throw(err)
});
}
And it worked !

Load form value from state

First I show a list of transactions, when a user selects a single transaction a new page is opened with the transaction ID in the URL. On this page are details of the transaction displayed.
The code below is just the details page. It shows all the right details.
One of the details is a list of 0 or more tags, I'd like to be able to edit the list of tags and save the result.
At this point, I always end up with a clean Input field and I do not understand how to populate this field with the existing transaction['tags'] data.
It seems that the transaction['tags'] is not initialized until the page is rendered, I cannot use it in the constructor or in the componentDidMount.
What I expect is that the transaction object as stated in the mapStateToProps is available and I can change the line in the constructor from: this.state = {value: ''}; to this.state = {value: transaction['tags']}
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchTransaction } from '../actions';
class TransactionsIndex extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const { _key } = this.props.match.params;
this.props.fetchTransaction(_key);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
const { transaction } = this.props;
if (!transaction) {
return <div>Loading...</div>;
}
let tags = null;
tags =
<div>
<form onSubmit={this.handleSaveTagsclick}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
</div>
// console.log(transaction['tags']);
return (
<div className="container">
<div className="well">
<div>Transactiedatum: { transaction["Transactiedatum"] }</div>
<div>Valutacode: { transaction["Valutacode"] }</div>
<div>CreditDebet: { transaction["CreditDebet"] }</div>
<div>Bedrag: { transaction["Bedrag"] }</div>
<div>Tegenrekeningnummer: { transaction["Tegenrekeningnummer"] }</div>
<div>Tegenrekeninghouder: { transaction["Tegenrekeninghouder"] }</div>
<div>Valutadatum: { transaction["Valutadatum"] }</div>
<div>Betaalwijze: { transaction["Betaalwijze"] }</div>
<div>Omschrijving: { transaction["Omschrijving"] }</div>
<div>Type betaling: { transaction["Type betaling"] }</div>
<div>Machtigingsnummer: { transaction["Machtigingsnummer"] }</div>
<div>Incassant ID: { transaction["Incassant ID"] }</div>
<div>Adres: { transaction["Adres"] }</div>
<div>Status: { transaction["status"] }</div>
<div>Created: { transaction["created"] }</div>
{tags}
</div>
<Link to="/"><button type="button" className="btn btn-default">Back</button></Link>
</div>
);
};
}
function mapStateToProps({ transactions }) {
// console.log('transactions_selectedTransaction: ' + transactions['selectedTransaction']);
return { transaction: transactions['selectedTransaction'] };
}
export default connect(mapStateToProps, { fetchTransaction })(TransactionsIndex);
I found this but it did not help me: Redux-form: Set form values from state
and this: How to get state / value from form component?

AsyncValidator in Angular 4 executes fine but could not get the response back to Reactive form

I am trying to implement async validator in my Reactive Form in angular 4.3.4 which will check that entered email exists or not in your system.
but this does not work properly, earlier it was invoking on every key up so I made some changes and make it Observable now only Checking after a given debounce time. checking...' text is displaying but the response comes but no error is being displayed on the page.
what can be the issue? I have very base knowledge of Observable and angular 4. please help me what is the issue. I have checked in the console and it is going and print the value in the asyncvalidator function.
here is the relevant code.
signup.component.html
<form [formGroup]="myForm" novalidate #formDir="ngForm" (ngSubmit)="doSignup()">
<input type="email" formControlName="email" pattern="{{email_pattern}}"/>
<div [hidden]="myForm.controls.email.valid || myForm.controls.email.pristine" class="text-danger">
<div *ngIf="myForm.controls.email.required">Please enter Email</div>
<div *ngIf="myForm.controls.email.pattern">Invalid Email</div>
<div *ngIf="myForm.controls.email.status === 'PENDING'">
<span>Checking...</span>
</div>
<div *ngIf="myForm.controls.email.errors && myForm.controls.email.errors.emailTaken">
Invitation already been sent to this email address.
</div>
</div>
<button type="submit" [disabled]="!myForm.valid">Invite</button>
</form>
signup.component.ts
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
import { ValidateEmailNotTaken } from './async-validator';
export class SignupComponent implements OnInit {
public myForm: FormGroup;
constructor(
private httpClient: HttpClient,
private fb: FormBuilder
) {
}
ngOnInit(): void {
this.buildForm();
}
private buildForm() {
this.inviteForm = this.fb.group({
firstname: [''],
lastname: [''],
email: [
'',
[<any>Validators.required, <any>Validators.email],
ValidateEmailNotTaken.createValidator(this.settingsService)
]
});
}
asyn-validator.ts
import { Observable } from 'rxjs/Observable';
import { AbstractControl } from '#angular/forms';
import { UserService } from './user.service';
export class ValidateEmailNotTaken {
static createValidator(service: UserService) {
return (control: AbstractControl): { [key: string]: any } => {
return Observable.timer(500).switchMapTo(service.checkEmailNotTaken(control.value))
.map((res: any) => {
const exist = res.item.exist ? { emailTaken: true } : { emailTaken: false };
console.log('exist: ', exist);
return Observable.of(exist);
})
.take(1);
};
}
}
user.service.ts
checkEmailNotTaken(email) {
const params = new HttpParams().set('email', email);
return this.httpClient.get(`API_END_POINT`, {
headers: new HttpHeaders({
'Content-type': 'application/json'
}),
params: params
});
}
You use Observable.timer(500) without a second argument, so after 500 milliseconds, it completes and never runs again. So first thing to do is to pass that argument - Observable.timer(0, 500).
switchMapTo cancels its previous inner Observable (service.checkEmailNotTaken(control.value) in your case) every time source Observable emits new value (so every 500 milliseconds). So if your http request lasts longer, you wont get its response. Thats why usually switchMap and switchMapTo are not suitable for http requests.
Here is an illustration:
const source = Rx.Observable.timer(0, 500);
const fail = source.switchMapTo(Rx.Observable.of('fail').delay(600))
const success = source.switchMapTo(Rx.Observable.of('success').delay(400))
const subscribe = fail.subscribe(val => console.log(val));
const subscribe2 = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
So you should pick another flattening operator, like flatMap:
const source = Rx.Observable.timer(0, 500);
const success = source.flatMap(()=>Rx.Observable.of('success').delay(600))
const subscribe = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
I know its too late for the answer but anyone facing same issue might find it useful:
apart from above answer the AsyncValidatorFn should return Promise<ValidationErrors | null> | Observable<ValidationErrors | null>.
Return value of ValidationErrors | null isn't correct.
Check out official docs

How can I use props to auto-populate editable redux-form fields in React?

I'm new to React so I've tried to show as much code as possible here to hopefully figure this out! Basically I just want to fill form fields with properties from an object that I fetched from another API. The object is stored in the autoFill reducer. For example, I would like to fill an input with autoFill.volumeInfo.title, where the user can change the value before submitting if they want.
I used mapDispatchtoProps from the autoFill action creator, but this.props.autoFill is still appearing as undefined in the FillForm component. I'm also confused about how to then use props again to submit the form. Thanks!
My reducer:
import { AUTO_FILL } from '../actions/index';
export default function(state = null, action) {
switch(action.type) {
case AUTO_FILL:
return action.payload;
}
return state;
}
Action creator:
export const AUTO_FILL = 'AUTO_FILL';
export function autoFill(data) {
return {
type: AUTO_FILL,
payload: data
}
}
Calling the autoFill action creator:
class SelectBook extends Component {
render() {
return (
....
<button
className="btn btn-primary"
onClick={() => this.props.autoFill(this.props.result)}>
Next
</button>
);
}
}
....
function mapDispatchToProps(dispatch) {
return bindActionCreators({ autoFill }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(SelectBook);
And here is the actual Form where the issues lie:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';
import { createBook } from '../actions/index;
class FillForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
onSubmit(props) {
this.props.createBook(props)
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
const { fields: { title }, handleSubmit } = this.props;
return (
<form {...initialValues} onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<input type="text" className="form-control" name="title" {...title} />
<button type="submit">Submit</button>
</form>
)
}
export default reduxForm({
form: 'AutoForm',
fields: ['title']
},
state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
}), {createBook})(FillForm)
I think you're mixing up connect and reduxForm decorators in the actual form component. Currently your code looks like this (annotations added by me):
export default reduxForm({
// redux form options
form: 'AutoForm',
fields: ['title']
},
// is this supposed to be mapStateToProps?
state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
}),
/// and is this mapDispatchToProps?
{createBook})(FillForm)
If this is the case, then the fix should be as simple as using the connect decorator as it should be (I also recommend separating this connect props to their own variables to minimize confusions like this):
const mapStateToProps = state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
})
const mapDispatchToProps = { createBook }
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm({ form: 'AutoForm', fields: ['title'] })(FillForm)
)
Hope this helps!