Getting child form values in parent component, onclick of a button present in parent component - angular2-forms

I have a parent component like this
<div>
<div class="selection-area active" style="max-height:initial;" contact-details [(contactDetailsD)]="conDetailsUI"></div>
</div>
<div>
Next
</div>
The child component
<div>
<form>
<input type="text" name="name" value="{{contactDetailsD?.firstName}}">
<input type="text" name="email" value="{{contactDetailsD?.email}}">
<input type="text" name="phone" value="{{contactDetailsD?.phone}}">
</form>
</div>
Can you help me to get the child form values in parent component, onclick of Next button that present in the parent component.

Use services.. Have some getters and setters in the services and inject the service in the parent component set the values and inject the service in the child component and get the values and assign them to some variables and bind them in the template. Here is the example of service
import { Injectable } from '#angular/core';
#Injectable()
export class ActiveclassService {
constructor() { }
private rolesclass:any;
setrolesclass(rolesclass){
this.rolesclass=rolesclass;
}
getrolesclass(){
return this.rolesclass;
}

Parent listens for child event
The child component exposes an EventEmitter property with which it emits events when something happens. The parent binds to that event property and reacts to those events.
The child's EventEmitter property is an output property, typically adorned with an #Output decoration as seen in this VoterComponent:
import { Component, EventEmitter, Input, Output } from '#angular/core';
#Component({
selector: 'my-voter',
template: `
<h4>{{name}}</h4>
<button (click)="vote(true)" [disabled]="voted">Agree</button>
<button (click)="vote(false)" [disabled]="voted">Disagree</button>
`
})
export class VoterComponent {
#Input() name: string;
#Output() onVoted = new EventEmitter<boolean>();
voted = false;
vote(agreed: boolean) {
this.onVoted.emit(agreed);
this.voted = true;
}
}
Clicking a button triggers emission of a true or false, the boolean payload.
The parent VoteTakerComponent binds an event handler called onVoted() that responds to the child event payload $event and updates a counter. VoterParentComponet:
import { Component } from '#angular/core';
#Component({
selector: 'vote-taker',
template: `
<h2>Should mankind colonize the Universe?</h2>
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
<my-voter *ngFor="let voter of voters"
[name]="voter"
(onVoted)="onVoted($event)">
</my-voter>
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
SRC: https://angular.io/guide/component-interaction#parent-listens-for-child-event

Related

cdkDropList how to bind/update data back to the array / source?

In this simple Stackbliz based on Angular's CDK drop-drag list, I use drop() event to check values of array movies. How do I build a two-way binding to capture the data value change back to the array?
[(cdkDropListData)]="movies" won't do it.
You are missing the DragDropModule import.
On the example site components/src/components-examples/cdk/drag-drop it's in index.ts.
main.ts
import './polyfills';
...
import { DragDropModule } from '#angular/cdk/drag-drop';
import {CdkDragDropCustomPlaceholderExample} from './app/cdk-drag-drop-custom-placeholder-example';
#NgModule({
imports: [
...
DragDropModule
],
...
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Binding input
Based on comments, you want to two-way-bind the inputs.
The following needs to be added, if you are using drag and drop, or not.
Banana-in-box on <input>
A trackBy function in the ngFor expression
Bind to movies[i] (it will not work binding to movie)
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box"
*ngFor="let movie of movies; index as i; trackBy: trackByFn"
cdkDrag
>
<div class="example-custom-placeholder" *cdkDragPlaceholder></div>
<input type="text" [(ngModel)]="movies[i]"/>
</div>
</div>
export class CdkDragDropCustomPlaceholderExample {
...
drop(event: CdkDragDrop<string[]>) {
alert(`Before drop update: ${this.movies}`);
moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
alert(`After drop update: ${this.movies}`);
}
trackByFn(index: number, item: String) {
return index;
}
}
Stackblitz
I fire the alert before and after the drop update, take your pick.

Angular Dart 2 - querySelect returning null

I'm trying to set up a drag&drop component to upload multiple files. However, when I attempt to access elements on the DOM with the querySelector method, I end up with null.
I've tried to implement the AfterViewInit class to no avail. Here's my current dart code for the component:
import 'dart:html';
import 'package:dnd/dnd.dart';
import 'package:angular/angular.dart';
#Component(
selector: 'upload',
templateUrl: 'upload.html',
styleUrls: [
'upload.css'
]
)
class Upload implements AfterViewInit {
#override
void ngAfterViewInit() {
// TODO: implement ngOnInit
Draggable d = new Draggable(document.querySelectorAll('.page'), avatarHandler : new AvatarHandler.clone());
var del = document.querySelector('.upload');
print(del); //prints null
Dropzone dropzone = new Dropzone(document.querySelector('.upload')); //throws an error, as it doesn't expect null.
dropzone.onDrop.listen((DropzoneEvent event){
print(event);
});
}
}
Also, my upload.html file is as follows:
<div class="center-me page" uk-grid>
<div class="uk-align-center text-align-center">
<h2 class="text-align-center" >Upload a file</h2>
<div class="upload uk-placeholder uk-text-center">
<span uk-icon="icon: cloud-upload"></span>
<span class="uk-text-middle">Attach binaries by dropping them here or</span>
<div uk-form-custom>
<input type="file" multiple>
<span class="uk-link">selecting one</span>
</div>
</div>
<progress id="progressbar" class="uk-progress" value="0" max="100" hidden></progress>
</div>
</div>
Thanks in advance.
So this looks like it should work. I wouldn't actually suggest doing it this way as it will get any element with an upload class which if you reuse the component will be a lot.
I would suggest using the ViewChild syntax instead
class Upload implements AfterViewInit {
#ViewChild('upload')
void uploadElm(HtmlElement elm) {
Dropzone dropzone = new Dropzone(elm);
dropzone.onDrop.listen((DropzoneEvent event){
print(event);
});
}
}
In the template:
<div class="uk-placeholder uk-text-center" #upload>
That said you shouldn't be getting null from the querySelect, but from the code you have shown I'm not sure why.

Input onchange with Angular2

I try to detect on change with dart. Example html:
<div>
<input id="photoUpload" type="file" name="photo" (onchange)="update()" multiple>
</div>
Dart:
#Component(
selector: "photo-upload-dialog", templateUrl: "photo_upload_dialog.html")
class PhotoUploadDialog {
update() async {
print('Changed!');
}
}
But nothing in a console.
onChange is the default event handler name, not the event name.
Use instead
(change)="update()"

Angular2 how to pass reference for ngModel binding

I wrote this component:
#Component({
selector: 'formfield',
template: `
<div>
<label>{{label}}</label>
<div>
<input class="form-control" type="text" [(ngModel)]="model">
</div>
</div>
`
})
export class Formfield {
#Input() label: string;
#Input() model: string;
}
I use it here:
<formfield label="something" model="somevalue"></formfield>
Not surprisingly the input field shows the string "somevalue". How can I make it to hold the value of the variable somevalue?
You need to use the following:
<formfield label="something" [model]="someprop"></formfield>
where someprop is a property of the component that uses the formfield component.
For example:
#Component({
(...)
})
export class SomeComponent {
someprop:string = 'some value';
}

Angular 2 Post updates my list but doesn't change the view

I am using Angular2 and MVC. I am following along with a lot of the tutorial on angular.io but changing some things to implement my own solution. Basically I have a table that shows clients and their identifier number. Using mock data, I was able to retrieve the clients and add new ones. The new ones would update my clientList and show the new client in my display table.
Once I started converting this to hit the server to get and post the data, I started to have issues.
I am able to retrieve the data perfectly fine from the server and it gives me the output I expect. When I try to post a new one to the server, it goes into my post method perfectly fine. It pushes the value into my clientList, but doesnt update my display table.
I'd like to add that I am not actually adding the data to my list on the server side, I am simply just returning a new object to try to get it to update my view.
Here's my service:
import {Injectable} from 'angular2/core';
import {Client} from './client';
import {RequestOptions, Http, Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class ClientService {
constructor(private http: Http) { }
getClients(): Observable<Client[]> {
return this.http.get('/Client/GetClients')
.map(this.extractData);
}
addClient(client: Client): Observable<Client> {
let clientUrl = '/Client/AddClient';
let body = JSON.stringify({ client });
let header = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: header });
return this.http.post(clientUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if (res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || {};
}
private handleError(error: any) {
// In a real world app, we might send the error to remote logging infrastructure
let errMsg = error.message || 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
My app component:
#Component({
selector: 'my-app',
templateUrl: 'Scripts/typescript/app.component.html',
directives: [ClientFormComponent, MODAL_DIRECTIVES],
providers: [ClientService, HTTP_PROVIDERS]
})
#Injectable()
export class AppComponent implements OnInit {
constructor(private _clientService: ClientService) { }
currentClient: Client;
#ViewChild('modal')
modal: ModalComponent;
public clients: Client[];
public errorMessage: string;
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients().subscribe(clients => this.clients = clients, error => this.errorMessage = <any>error);
}
deleteClient() {
var index = this.clients.indexOf(this.currentClient, 0);
if (index > -1) {
this.clients.splice(index, 1)
}
}
close() {
this.modal.close();
}
open(client: Client) {
this.currentClient = client;
this.modal.open();
And my component with the call to the service when I click an add button:
#Component({
selector: 'client-form',
templateUrl: 'Scripts/typescript/client-form.component.html'
})
export class ClientFormComponent
{
constructor(private _clientService: ClientService) { }
#Input() clientList;
model = <Client>{ name: 'Bob', npi: 12345678 };
submitted = false;
active = true;
errorMessage: string;
onSubmit() { this.submitted = true; }
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients()
.subscribe(
clients => this.clientList = clients,
error => this.errorMessage = <any>error);
}
addClient() {
this._clientService.addClient(this.model)
.subscribe(
client => this.clientList.push(client),
error => this.errorMessage = <any>error);
}
}
Here is my template for displaying the table, which is the app.component.html:
<h1>Client/NPI Cross Reference</h1>
<client-form></client-form>
<table class="table">
<tr style="font-weight: bold;">
<td>Client</td>
<td>NPI</td>
<td>Date Added</td>
<td></td>
</tr>
<tr *ngFor="#client of clients;">
<td>{{client.name}}</td>
<td>{{client.npi}}</td>
<td>{{client.dateAdded}}</td>
<td>
<span style="color: red; font-size: 16px; cursor: pointer;" class="glyphicon glyphicon-trash" aria-hidden="true" (click)="open(client)"></span>
</td>
</tr>
</table>
<modal #modal [animation]="animationsEnabled" (onClose)="deleteClient()" (onDismiss)="dismissed()">
<modal-header [show-close]="true">
<h4 class="modal-title">Delete</h4>
</modal-header>
<modal-body>
Are you sure you want to delete this entry?
</modal-body>
<modal-footer>
<button type="button" class="btn btn-primary" (click)="modal.close()">Yes</button>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">No</button>
</modal-footer>
</modal>
Here is my form template html:
<div class="container">
<form *ngIf="active" (ngSubmit)="onSubmit()" #clientForm="ngForm">
<div class="form-group" style="float: left;">
<label for="clientid">Client Id:</label>
<input type="text" class="form-control" required [(ngModel)]="model.name" ngControl="name" #name="ngForm" #newClient>
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
Name is required
</div>
</div>
<div class="form-group" style="float: left; padding-left: 10px;">
<label for="npi">NPI:</label>
<input type="number" class="form-control" required [(ngModel)]="model.npi" ngControl="npi" #newNpi>
</div>
<div style="float: left; margin: 25px 0 0 10px;">
<button type="submit" class="btn btn-default" (click)="addClient()" [disabled]="!clientForm.form.valid">Add</button>
</div>
</form>
</div>
<br style="clear:both;" />
On the server side, I am simply just returning a json client:
[HttpPost]
public JsonResult AddClient()
{
var client = new Models.Client();
client.name = "test";
client.npi = 12345;
client.dateAdded = DateTime.Now.ToShortDateString();
return Json(client);
}
When using the mock data, it would automatically update my table and I'd be able to see my new client in the table. But now that I am using the server, it adds it to the clientList, but it doesnt actually change the view of my table.
How can I get it to change my table and update it to show it's been added?
Maybe the point is that you are calling twice the method getClients() of your ClientService, once from the the AppComponent and once from the ClientFormComponent.
This means that you will get 2 different arrays, one held in the AppComponent and one held in the ClientFormComponent.
If what I say is correct, then when you add a new client via the POST command, then the ClientFormComponent updates its array but this has no effect on the AppComponent array (which is a different object).
The solution in this case is probably:
1) make the AppComponent pass its clients array to the ClientFormComponent such as <client-form [clientList]="clients"></client-form> (which is probably the reason why clientList is preceded by #Input())
2) Avoid like hell resetting the clientList from within the ClientFormComponent (which is currently happening in the line this.clientList = clients)
I hope I am not missing something and that this can help.
PS: I guess you do not need #Injectable() in the AppComponent, since it is a Component
Here is what I think is happening. When you call getClients() inside ClientFormComponent, it replaces the clientsList with a new array. But, the one in the view is the array assigned by getClients() inside AppComponent. One way to make sure is to comment the line inside ClientFormComponent:
ngOnInit() {
//this.getClients();
}
Change the line:
public clients: Client[];
to
public clients: Client[] = [];

Resources