I have dialog box where user can enter data and after clicking on "create", my dialog box gets close and user gets notification. I want to close my dialog box after user get notification and if user enters wrong data, user should get notification for that too and dialog box should not get close.
Currently, everything is working properly but I want my dialog box should disappear after notification (toster service).
Can anyone help me with this thing so that my dialog box will stay till i get notification for success and also for error?
exhibit.component.ts (main component)
createExhibit(event: any) {
let context = this;
this.createDialogRef = this.dialog.open(CreateExhibitDialogComponent, { width: '45em', data: {} });
this.createDialogRef.afterClosed().subscribe(
(newExhibit: Exhibit) => {
if (newExhibit.latitude) { newExhibit.latitude = newExhibit.latitude.toString().replace(/,/g, '.'); }
if (newExhibit.longitude) { newExhibit.longitude = newExhibit.longitude.toString().replace(/,/g, '.'); }
if (newExhibit) {
this.exhibitService.createExhibit(newExhibit)
.then(
() => {
this.toasterService.pop('success', this.translate('exhibit saved'));
setTimeout(function () {
context.reloadList();
}, 1000);
}
).catch(
error => this.toasterService.pop('error', this.translate('Error while saving'), error)
);
}
this.createDialogRef = null;
}
);
}
createExhibit.component.ts
<h1 md-dialog-title>{{ 'create exhibit' | translate }}</h1>
<md-dialog-content>
<form id="new-exhibit-form">
<md-input-container>
<input mdInput placeholder="{{ 'name' | translate }}" [(ngModel)]="exhibit.name" name="name" required>
</md-input-container>
<md-input-container>
<textarea
mdInput
mdTextareaAutosize
#autosize="mdTextareaAutosize"
placeholder="{{ 'description' | translate }}"
[(ngModel)]="exhibit.description"
name="desc"></textarea>
</md-input-container>
<div layout="row" layout-align="start center" flex>
<md-icon _ngcontent-c7="" class="mat-icon material-icons centered" role="img" aria-hidden="true">search</md-icon>
<md-input-container>
<input mdInput placeholder="search for location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control" #search [formControl]="searchControl">
</md-input-container>
<md-input-container>
<input (blur)="updateMap()" mdInput type="number" min="-90" max="90" step="0.000001"
placeholder="{{ 'latitude' | translate }}" [(ngModel)]="exhibit.latitude" name="lat">
</md-input-container>
<md-input-container>
<input (blur)="updateMap()" mdInput type="number" min="-180" max="180" step="0.000001"
placeholder="{{ 'longitude' | translate }}" [(ngModel)]="exhibit.longitude" name="long">
</md-input-container>
<md-select class="align-right" placeholder="{{ 'status' | translate }}" [(ngModel)]="exhibit.status" name="status">
<md-option *ngFor="let statusOption of statusOptions" [value]="statusOption">{{ statusOption | translate }}</md-option>
</md-select>
</div>
<agm-map (mapClick)="selectLocation($event)" [zoom]=15 [latitude]="lat" [longitude]="lng">
<agm-marker [iconUrl]="'../../../images/map-marker.png'" *ngIf="exhibit.longitude && exhibit.latitude" [latitude]="exhibit.latitude" [longitude]="exhibit.longitude"></agm-marker>
</agm-map>
</form>
</md-dialog-content>
<md-dialog-actions align="end">
<button md-dialog-close md-raised-button>
{{ 'cancel' | translate }}
<md-icon>cancel</md-icon>
</button>
<button md-raised-button [disabled]="!exhibit.isValid()" color="primary" (click)="dialogRef.close(exhibit)">
{{ 'create' | translate }}
<md-icon>add_circle</md-icon>
</button>
</md-dialog-actions>
how to do this?
As windmaomao stated you need to manulally call dialog close() method. Material Dialog component proivide Observable only from afterClose() or beforeClose() and these methods listen to data passed through close() method. The close() method closes the dialog ofcourse what is not our expectation. You need to implement your own communication system between component and dialog build with kind of Observable or EventEmitter.
I have prepared simplified solution of your problem. The trick is you can obtain reference to any field or method of your dialog component with 'componentInstance' getter.
Dialog component
import {Component, EventEmitter, OnInit} from '#angular/core';
import {MatDialogRef} from "#angular/material";
#Component({
selector: 'app-simple-dialog',
template: `<h2 mat-dialog-title>Entering some data</h2>
<mat-dialog-content>Is data OK?</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="actionNo()">No</button>
<button mat-button (click)="actionYes()">Yes</button>
</mat-dialog-actions>`,
styleUrls: ['./simple-dialog.component.css']
})
export class SimpleDialogComponent implements OnInit {
private _action: EventEmitter<boolean> = new EventEmitter<boolean>();
answer = this._action.asObservable();
constructor(public dialogRef: MatDialogRef<SimpleDialogComponent>) { }
actionYes() {
this._action.next(true);
}
actionNo() {
this._action.next(false);
}
closeDialog() {
this.dialogRef.close();
}
ngOnInit() {
}
}
And HTML template excerpt code to include in your main component:
<button (click)="openDialog()">Open Dialog</button>
The code of openDialog() method:
openDialog() {
let dialogRef = this.dialog.open(SimpleDialogComponent);
dialogRef.componentInstance.answer.subscribe( answer => {
console.log('Answer from Dialog: ' + answer);
switch(answer) {
case true:
console.log('Data is OK. Closing dialog');
//do some complicated stuff
dialogRef.componentInstance.closeDialog();
//can be simple: dialogRef.close();
break;
case false:
console.log('Data is not OK. Not closing dialog. Show some toaster');
break;
}
}
)
}
Related
I'm using a for loop to display different sections of my blog for editing purposes.
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Route, ParamMap } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { Content } from '../model/Content';
#Component({
selector: 'app-edit-story',
templateUrl: './edit-story.component.html',
styleUrls: ['./edit-story.component.css']
})
export class EditStoryComponent implements OnInit {
story: any;
storyName: any;
isMe: boolean= false;
constructor(private route: ActivatedRoute, private httpClient: HttpClient) { }
ngOnInit(): void {
if (localStorage.getItem('userId')=='62e348924d52fa7420bb96bc') {
this.isMe = true;
}
this.storyName = this.route.snapshot.paramMap.get('storyName');
var url = 'http://localhost:3002/api/stories/' + this.storyName;
this.httpClient.get(url).subscribe(data => {
this.story = data;
console.log(this.story);
})
}
editStory() {
}
addContent() {
var newContent = new Content("", "", "");
this.story.contents.push(newContent);
}
}
<div *ngIf="isMe">
<form #editStoryForm = "ngForm" (ngSubmit)="editStory()" class="addStory">
<label for="title">Title</label>
<input name="title" type="text" [(ngModel)]="story.title" req/>
<label for="subtitle">Subtitle</label>
<input type="subtitle" [(ngModel)]="story.subtitle" name="subtitle" req>
<label for="name">Name</label>
<input type="name" [(ngModel)]="story.name" name="name" req>
<button (click)="addContent()">Add Content</button>
<div *ngFor="let content of story.contents">
<label for="type">Type</label>
<select name='type' [(ngModel)]="content.type" value="{{content.type}}">
<option value=''>Pick one</option>
<option value='text' selected="selected">text</option>
<option value='image'>image</option>
</select>
<label for="text">Text</label>
<textarea name="text" cols="50" rows="7" [(ngModel)]="content.text">{{content.text}}</textarea>
<label for="url">url</label>
<input name="url" type="text" value="{{content.url}}" [(ngModel)]="content.url">
</div>
<button type="submit">Edit</button>
</form>
</div>
In the console.log to display the story, the contents array appears fine. Even when I open devtools and check the HTML elements, the values are correct.
devtools showing the innerHTML values are all different
However the page itself has all these element with only the values of the last array item.
contarary to the devtools html, the page displays the last array values over and over
please help.
You are using template-driven forms. The problem is in the way you're registering the child controls using ngModel and the name attribute.
In your ngFor you do:
<textarea name="text" [(ngModel)]="content.text">{{content.text}}</textarea>
So basically you're assigning the same name to all children.
Instead, you should do something like this:
<div *ngFor="let content of story.contents; index as i">
<!-- other elements -->
<label for="text">Text</label>
<textarea name="{{ 'text-' + i }}" [(ngModel)]="content.text">
{{ content.text }}
</textarea>
<!-- other elements -->
</div>
You may also find the last section of this article helpful.
Cheers.
First I have a table to show all the record of books, I have a button is for going to edit the record, and I want to edit on the editbook.html and when I click submit , the record will be updated. May I know how can I implement this?
<tbody>
<tr *ngFor="let book of books">
<td class="td-title">{{book.title}}</td>
<td>{{book.author}}</td>
<td>{{book.price}}</td>
<td>{{book.isbn}}</td>
<td>{{book.details.pages}}</td>
<td>{{book.details.language}}</td>
<td class="td-desc">{{book.description}}</td>
<td><button type="button"><a [routerLink]="['/editbook/', book.isbn]">Edit</a></button></td>
<td><button type="button">Delete</button></td>
</tr>
</tbody>
And now I going to edit the record when I click the edit button.
and my edit form html
editbook.html
<div *ngIf="book" class="bookdetail-block">
<div *ngFor="let bookdetail of book" class="bookdetail">
<h1>Edit Book</h1>
<form [formGroup]="editbookForm" (ngSubmit)="onSubmit()">
<label>Title: <input type="text" formControlName="title">
<div *ngIf="submitted && editbookForm.controls.title.errors" class="error">
<div *ngIf="editbookForm.controls.title.errors.required">Required</div>
</div>
</label>
<label>Author: <input type="text" formControlName="author">
<div *ngIf="submitted && editbookForm.controls.author.errors" class="error">
<div *ngIf="editbookForm.controls.author.errors.required">Required</div>
</div>
</label>
<label>Description: <textarea type="text" formControlName="description"></textarea>
<div *ngIf="submitted && editbookForm.controls.description.errors" class="error">
<div *ngIf="editbookForm.controls.description.errors.required">Required</div>
</div>
</label>
<label>Page: <input type="number" min="1" max="10000" formControlName="page">
<div *ngIf="submitted && editbookForm.controls.page.errors" class="error">
<div *ngIf="editbookForm.controls.page.errors.required">Required</div>
</div>
</label>
<label>language:
<select formControlName="language">
<option value="English">English</option>
<option value="Traditional Chinese">Traditional Chinese</option>
<option value="Simpify Chinese">Simpify Chinese</option>
</select>
<div *ngIf="submitted && editbookForm.controls.page.errors" class="error">
<div *ngIf="editbookForm.controls.page.errors.required">Please select one option</div>
</div>
</label>
<br />
<label>Price: <input type="number" formControlName="price">
<div *ngIf="submitted && editbookForm.controls.price.errors" class="error">
<div *ngIf="editbookForm.controls.price.errors.required">Required</div>
</div>
</label>
<label>ISBN: <input type="text" formControlName="isbn">
<div *ngIf="submitted && editbookForm.controls.isbn.errors" class="error">
<div *ngIf="editbookForm.controls.isbn.errors.required">Your name is required</div>
</div>
</label>
<label>Image: <input (change)="onFileSelect($event)" type="file">
<div *ngIf="imageData" class="error">
<img [src]="imageData" [alt]="editbookForm.value.name">
</div>
</label>
<label style="display:none">ImageData:
<!-- <div *ngIf="submitted && editbookForm.controls.image.errors" class="error"> -->
<input type="hidden" id="uploadImage">
<!-- </div> -->
</label>
<input type="submit" value="Update Book" class="cta">
</form>
<div *ngIf="success" class="results">
<p>The Book is updated into the record.</p>
</div>
</div>
</div>
and the editbook.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
import { DataService } from '../data.service';
import { ActivatedRoute, Router } from "#angular/router";
interface HTMLInputEvent extends Event {
target: HTMLInputElement & EventTarget;
}
#Component({
selector: 'app-editbook',
templateUrl: './editbook.component.html',
styleUrls: ['./editbook.component.scss']
})
export class EditbookComponent implements OnInit {
isbn: any;
book: any;
editbookForm: FormGroup;
submitted = false;
success = false;
imageData : String;
constructor(private formBuilder: FormBuilder, private data: DataService, private router: Router, private route: ActivatedRoute) { }
ngOnInit() {
this.isbn = this.route.snapshot.paramMap.get("isbn");
this.data.getOneBook(this.isbn).subscribe(data =>{
console.log({ data }); //show data
this.book = data
//console.log(this.books);
})
}
onFileSelect(event?: HTMLInputEvent){
console.log("selected file")
console.log(event)
let files: any = event.target.files[0];
console.log(files);
const reader = new FileReader();
reader.onloadend = () => {
var b64 = reader.result.toString();
let el :any = document.querySelector('#uploadImage');
el.value = b64;
}
reader.readAsDataURL(event.target.files[0]);
}
onSubmit() {
/*
// upload image
this.bookForm.reset();
this.imageData = null;
*/
this.submitted = true;
if(this.editbookForm.invalid) {
return;
}
this.data.insertBook(this.editbookForm.controls);
this.success = true;
}
}
how can I insert the value into the html input ?
Don't use ngFor for insert the value into the html input. use angular patch method.
For example:
this.bookForm.patchValue({
title: this.book.title,
author: this.book.author
});
You can also initialize value on form:
this.bookForm({
title:[this.book.title],
author:[this.book.author]
});
Suppose you have two fields for the moment (title and author), you complete the other fields later after you be sure everything is fine.
Start with a simple example(Remove the validations from HTML for now).
So, initially, your HTML should be something like this:
<form [formGroup]="editbookForm" (ngSubmit)="onSubmit()">
<label>Title:
<input type="text" formControlName="title">
</label>
<label>Author:
<input type="text" formControlName="author">
</label>
<input type="submit" value="Update Book" class="cta">
</form>
Then your OnInit should be something like this:
ngOnInit() {
this.isbn = this.route.snapshot.paramMap.get("isbn");
if (this.isbn) {
//Cleaning the form from the previous data
if (this.editbookForm) this.editbookForm.reset();
this.data.getOneBook(this.isbn)
.pipe(take(1)) //To ensure that it will unsubscribe later
.subscribe((data) => {
this.book = data;
//Filling the form
this.editbookForm.patchValue({
title: this.book.title,
author: this.book.author
});
});
}
}
If that works, step by step add validations and other things as you like.
Examples are:
<div *ngIf="submitted && editbookForm.controls.author.errors" class="error">
<div *ngIf="editbookForm.controls.author.errors.required">Required</div>
</div>
and
<div *ngIf="book" class="bookdetail-block">
<div *ngFor="let bookdetail of book" class="bookdetail">
<h1>Edit Book</h1>
and so on.
I have this simple piece of code in my ReactJs app:
import React from 'react'
import '../../../assets/styles/authentication/login.css'
import '../../../assets/bootstrap/css/bootstrap.min.css'
import { login } from './login_form_actions.js'
export default class LoginForm extends React.Component {
constructor(props) {
super(props)
this.state = {
email : { email: "" },
password : { password: "" }
}
this.handle_sign_in_click = this.handle_sign_in_click.bind(this)
}
handle_sign_in_click(e) {
console.log(e, this.state.email, this.state.password)
login(this.state.email, this.state.password)
}
render() {
return (
<div className="col-sm-6 col-sm-offset-3 form-box">
<div className="form-auth">
<form className="login-form">
<div className="form-group">
<label className="sr-only" htmlFor="form-username">Email</label>
<input type="text" name="form-username" placeholder="Email..."
className="form-username form-control" id="form-username"
onChange={(event) => this.setState({email:event.target.value})}/>
</div>
<div className="form-group">
<label className="sr-only" htmlFor="form-password">Password</label>
<input type="password" name="form-password" placeholder="Password..."
className="form-password form-control" id="form-password"
onChange={(event) => this.setState({password:event.target.value})}/>
</div>
<button className="btn" onClick={this.handle_sign_in_click}>Sign in!</button>
</form>
</div>
</div>
)
}
}
When I click the Sign In button, nothing happens for some reason.
I tried to bind the handle_sign_in_click as proposed in some other reactjs question (from StackOverflow) but still nothing.. What am I missing here? (next step is to use redux to store the result of the auth on success)
Thanks for any help
Edit1: Console and proposition
console:
react-dom.development.js:22287 Download the React DevTools for a better development experience: react-devtools
authentication:1 [DOM] Input elements should have autocomplete attributes (suggested: "current-password"):
However, when I use an <a/> instead of <button/>, it does work but my design is broken (it's the same at all as when it's a button)
Here is how to properly handle basic forms in React (without libs like Formik etc...):
import React from 'react';
export default class Form extends React.Component {
state = { email: '', password: '' };
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
// login logic here ususally passed down as props from the parent (if not using redux...) so it will be something like this.props.login(...)
this.setState({ email: '', password: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="email"
type="text"
onChange={this.handleChange}
placeholder="email"
value={this.state.email}
/>
<input
name="password"
type="text"
onChange={this.handleChange}
placeholder="password"
value={this.state.password}
/>
<input type="submit" />
</form>
);
}
}
You are missing a e.preventDefault in your signin handler, check this working sample
How to fetch data from database to textbox typescript? My Get function is working it display on console but not on textbox.
export class ProfileComponent implements OnInit {
Profile: Profile = new Profile;
constructor(
private ProfileService: ProfileService,
private Security: Security){
}
ngOnInit() {
this.GetProfileByID(this.Security.UserID);
}
GetProfileByID(UserID: any) {
this.ProfileService.GetProfileByID(UserID).then(
(response) => {
console.table(response.Body)
}
)
}
}
Here's my html
`
<form #updateForm="ngForm">
<div class="input-container mb-3">
<div class="col-12">
<label>* Name</label>
<input name="name" #name="ngModel" id="TACName" type="text" [(ngModel)]="Profile.Name" pInputText required/>
<div class="errortxt" [hidden]="name.valid || name.pristine" class="alert alert-danger">
<div class="errortxt" [hidden]="!name.hasError('required')"> Name is Required!</div>
</div>
</div>
</div>
<button class="ui-button-raised submit-btn" [disabled]="!updateForm.valid || !Agreement" pButton label="SAVE" type="submit" (click)="Save(updateForm)"></button>
</form>
`
here's the result on console
According the output result in the console...
GetProfileByID(UserID: any) {
this.ProfileService.GetProfileByID(UserID).then(
(response) => {
this.Profile.Name = response.Body.Name;
}
)
}
There is a typo, you should use new Profile() instead of new Profile.
Really recommending to not use [(ngModel)]. Use FormGroup instead and then after receiving your response from service, use FormGroup.get(controlName: string).patchValue. That will fix your issue for sure.
I am using Angular6 and angular material stepper. There are three flows in which stepper control have one form and second stepper control have 2 input fields.
When i have entered the firstStepperCtrl and navigate to second stepper and click on prev and come to second stepper which shows the validation.
Without touching the form , the validation is showing , how to reset the stepper and validate the stepper if second form field has been touched.
Here is the mark up:
<md-horizontal-stepper [linear]="isLinear" #stepper>
<md-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template mdStepLabel>Fill out your name</ng-template>
<md-form-field>
<input mdInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</md-form-field>
<div>
<button md-button mdStepperNext>Next</button>
</div>
</form>
</md-step>
<md-step [stepControl]="secondFormGroup">
<form [formGroup]="secondFormGroup" #formDirective="ngForm">
<ng-template mdStepLabel>Fill out your address</ng-template>
<md-form-field>
<input mdInput placeholder="Address" formControlName="secondCtrl" required>
</md-form-field>
<md-form-field>
<input mdInput placeholder="phoneNo" formControlName="secondCtrlPhoneNo" required>
</md-form-field>
<div>
<button md-button mdStepperPrevious>Back</button>
<button md-button mdStepperNext>Next</button>
<button md-button (click)="resetWholeStepper(stepper)">Reset</button>
<button md-button (click)="reset2Stepper()">Reset1</button>
</div>
</form>
</md-step>
<md-step>
<ng-template mdStepLabel>Done</ng-template>
You are now done.
<div>
<button md-button mdStepperPrevious>Back</button>
<button md-button (click)="resetStepper(stepper)">Reset</button>
</div>
</md-step>
</md-horizontal-stepper>
app.component.ts
export class AppComponent implements OnInit, AfterViewInit {
private ngVersion: string = VERSION.full;
isLinear = false;
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
constructor(private _formBuilder: FormBuilder,) { }
// Event fired when component initializes
ngOnInit() {
this.firstFormGroup = this._formBuilder.group({
firstCtrl: ['', Validators.required]
});
this.secondFormGroup = this._formBuilder.group({
secondCtrl: ['', Validators.required],
secondCtrlPhoneNo:['',Validators.required]
});
}
// Event fired after view is initialized
ngAfterViewInit() {
}
resetWholeStepper(stepper: MdStepper){
stepper.selectedIndex = 0;
}
reset2Stepper1(formDirective: FormGroupDirective){
alert(2);
formDirective.resetForm();
// this.secondFormGroup.clearValidators();
this.secondFormGroup.reset();
}
[screencast link of issue]]1
demo