how to pass form value to another component in angular - javascript

I want to show taxDetailsId in my child component Html page.
But when click submit button.
After click submit button then shows taxDetailsId in my child component Html page.
Parent Component
export class OnlinePaymentComponent implements OnInit {
HttpClient: any;
paymentForm: FormGroup = this.formBuilder.group({
taxDetailsId: ['', [Validators.required]]
});
constructor(
private formBuilder: FormBuilder,
private router: Router,
) {}
ngOnInit() {}
submitForm(): void {
if (!this.paymentForm.valid) {
this.router.navigate(['/home/online-payment/error']);
return;
}
}
}
Parent.Component.html
<form [formGroup]="paymentForm" (ngSubmit)="submitForm()">
<label>Tax Details Id</label>
<input type="text" formControlName="taxDetailsId" placeholder="Tax Details Id" />
<button>Pay Bill</button>
<form>
Child Component
export class OnlinePaymentErrorComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
Child.Component.html
<div>
<button [routerLink]="['/home/online-payment']" >Back Home</button>
</div>

you can try this pattern this.router.navigate(['/heroes', { id: heroId }]);
https://angular.io/guide/router

you can use angular #Input() decorator for it.
Child Component
import { Component, Input } from '#angular/core';
export class ChileComponent {
#Input() public taxDetailsId: number;
}
Child Component HTML
enter code here
<div>
{{ taxDetailsId }}
<button [routerLink]="['/home/online-payment']" >Back Home</button>
</div>
Parent Component HTML
<app-child-component [taxDetailsId]="taxDetailsId"> </app-child-component>
https://angular.io/guide/inputs-outputs

You can pass components around using Angular's InjectionToken.
First you start off by creating the token:
export const ONLINE_PAYMENT_REF = new InjectionToken<OnlinePaymentComponent>('OnlinePaymentComponent');
Next you add the token to one of the root components as a provider, in this case it is the OnlinePaymentComponent. This way everything that is a child of this component, and everything that is a child of those components, and so on; will have a reference to the main parent that we create here:
#Component({
selector: 'online-payment',
template: `
<online-payment-error></online-payment-error>
`,
providers: [
{
provide: ONLINE_PAYMENT_REF,
// Forwards the instance of OnlinePaymentComponent when injected into
// the child components constructor.
useExisting: forwardRef(() => OnlinePaymentComponent)
}
]
})
export class OnlinePaymentComponent {
message = 'I am the Online Payment Component';
}
Now that we have the main component setup, we can access it through the constructor of anything that is a child of OnlinePaymentComponent (no matter how deep it is).
#Component({
selector: 'online-payment-error',
template: `
<h2>Child</h2>
<strong>Parent Message:</strong> {{parentMessage}}
`
})
export class OnlinePaymentErrorComponent implements OnInit {
parentMessage = '';
constructor(
#Inject(ONLINE_PAYMENT_REF) private parent: OnlinePaymentComponent
) {}
ngOnInit() {
this.parentMessage = this.parent.message;
}
}
When all is said and done, you will see the following:
The pros of this method are that you don't have to bind values to the elements in the template, and if those components have components that need to reference the parent you wouldn't have to bind to those either. Since components look up the hierarchy till they find the first instance of the provider that we are looking for they will find the one in OnlinePaymentComponent.
This becomes very helpful when components get deeper and deeper into the parent component (say 5 levels deep), that means every time you would have to pass a reference to the template element 5 times, and if it changes or gets deeper you would have to update all the templates.
With this method we no longer need to update templates to pass data from one component to another component, we just request it in our constructor as seen in OnlinePaymentErrorComponent.

There are two simple ways:
Using query params (without routerLink).
Using Observables.
Using query params, you can use the router.navigate and pass the params you need (Id) along with the route.
eg: this.route.navigate(['yourroute/route', { tId: variableWithId }])
Using Observable, when you click on the button, use the same router navigate without params and pass the required data to an observable. On successful routing to the next page, get the resolved data from the observable.

Related

Angular 2+ : Get Reference of appcomponent div in another components

I have components called app.component which is the main component in the angular project.
Navigation to customer component is done by routing.
And
Folder structer
src\app
- app.component.html
- app.component.ts
and
src\app\components\customer
- customer.component.html
- customer.component.ts
In my app.component.html
<div class="top-container" #topContainerRef>
<router-outlet></router-outlet>
</div>
In my customer.component.ts
I want to get reference of the top most container div which is contained in app.components
I want to replace
document.getElementsByClassName('top-container')[0].scrollTop = some values
with something similar to
#ViewChild('topContainerRef', { read: ElementRef, static: false }) topContainerRef: ElementRef;
this.topContainerRef.nativeElement.scrollTop= "some value" //here the topContainerRef is undefined
Is there any way i can use elementRef instead of classname or Id's.
You cannot use ViewChild for the #topContainerRef to get a reference of this element, because it is not rendered by your CustomerComponent.
You either need to get the reference of this element inside the app component itself and find a way to pass it to all the other children that might need it (not recommended).
Or you can just build a service and use that to "request" the scrollTop change by whichever component has access to this element (in your case the app component).
I would do it something like this:
export class AppContainerService {
private scrollTopSource = new ReplaySubject<number>(1);
public scrollTop$ = this.scrollTopSource.asObservable();
public updateScrollTop(value: number) {
this.scrollTopSource.next(value);
}
}
Inside your CustomerComponent:
public class CustomerComponent implements OnInit {
// ...
constructor(private containerService: AppContainerService) {
}
ngOnInit() {
this.containerService.updateScrollTop(/* whatever value you need */);
}
// ...
}
And finally, the AppComponent that will react to the scrollTop changes:
export class AppComponent implements AfterViewInit {
#ViewChild('topContainerRef', { read: ElementRef, static: false }) topContainerRef: ElementRef;
private subscriptions = new Subscription();
constructor(private containerService: AppContainerService) {
}
ngAfterViewInit() {
this.subscriptions.add(this.containerService.scrollTop$.subscribe((value: number) => {
this.topContainerRef.nativeElement.scrollTop = value;
}));
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
Don't forget about unsubscribing inside ngOnDestroy. This is important so that you don't have memory leaks

Angular: How to get value from one component's frontend (app.compont.html) to another component's backend (other.component.ts)

Consider a simple crud scenario. I have a lot of input fields and buttons in app.component.html. When i press a button from app.component.html, it will send html field value to 'other.component.ts' component and will display the result back in app.component.html after processing (like add, subtract or other).
Here is app.component.html
<a routerLink="posts/">Show Posts</a>
<input type="number" [(ngModel)]="get-one-post-id">
<a routerLink="/post-by-id">Show One Posts</a>
<router-outlet>
</router-outlet>
post-by-id-component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-post-by-id',
templateUrl: './post-by-id.component.html',
styleUrls: ['./post-by-id.component.css']
})
export class PostByIdComponent implements OnInit {
posts: object;
constructor(private dataService: DataService) { }
ngOnInit(): void {
// const id = ??
this.GetPost(1);
}
async GetPost(id: number)
{
const response = await this.dataService.Get_A_Post(id);
const dataService = await response.json();
this.posts = dataService;
}
}
post-by-id-component.html
<div *ngFor="let post of posts">
<h3>{{post.title}}</h3>
<p>{{post.body}}</p>
</div>
I just want to get value from the field called get-one-post-id from app.component.html to post-by-id-component.ts [where I commented // const id = ??]. But i can't find a way to import it.
To share Data between Angular Components exists 4 different ways:
Parent to Child: Sharing Data via Input
Child to Parent: Sharing Data via ViewChild
Child to Parent: Sharing Data via Output() and EventEmitter
Unrelated Components: Sharing Data with a Service
You can read this useful article to see how it works.

How to make child component detects object (#Input()) from parent component has changed in Angular

I have an object from parent component also received in a child component similar to this:
{
attribute: 'aaaa',
attribute2: [
{
value
},
{
value
},
{
value
},
]
}
This object is an #Input from a parent component. When I make changes to the objects inside the attribute2 array, I would like the child component detect that changes were made and then gets updated. As this is an object, I could'nt make it work, so I clone the entire object (this.objet = _.cloneDeep(this.object) in the parent component so then the child component detects that changes happened.
Is there any other way of doing this that does not clone the entire object? Thanks in advance
EDIT:
Child Component
export class ChildComponent implements OnInit, OnChanges {
#Input() public object: any;
}
html
<div>
<span>{{object.attribute}}</span>
<div *ngFor="let items of object.attribute2">{{item.value}}</div>
</div>
Parent Component
export class ParentComponent implements OnInit {
public object: any;
updateObject() {
this.object.attribute2[1] = 'Changed value';
this.object = _.cloneDeep(this.object);
}
}
html
<div>
<child-component [object]="object"></child-component>
</div>
An efficient way is to use EventEmitter and service communication to
trigger changes in the child component.
On way as mentioned by #Tony is to use ngOnChanges(). It is a good shortcut for detecting bounded properties change but as you add more and more bindings, using this hook will affect you application in the long run because it will run every time any of the bound property changes whether or not you desire it all the calls.
So for Service based communication, I've created an example on
Stackblitz:
https://stackblitz.com/edit/angular-fgut7t
Gist: https://gist.github.com/stupidly-logical/a34e272156b498513505127967aec851
In this example, I am binding an Array to the child component using #Input() an on addition of new data, the array is updated by the parent and the latest value is passed on the service which then emits this value. The child component subscribes to this value and the relevant code is executed.
The Service:
import { Injectable, EventEmitter } from '#angular/core';
#Injectable({
providedIn: "root"
})
export class DataService {
dataUpdated:EventEmitter<any> = new EventEmitter();
constructor() { }
setLatestData(data) {
this.dataUpdated.emit(data);
}
}
Child Component TS
import { Component, OnInit, Input } from '#angular/core';
import { DataService } from '../data-service.service';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
#Input() allData: [];
latestData: any;
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.dataUpdated.subscribe((data) => {
this.latestData = data;
});
}
}
Child Component HTML
<p>
Latest Data: {{ latestData }}
</p>
<h3>List:</h3>
<li *ngFor="let data of allData">
{{ data }}
</li>
Parent Component TS
import { Component } from '#angular/core';
import { DataService } from './data-service.service'
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
dataArr = [];
constructor(private dataService: DataService){}
onAddTimestamp() {
let timestamp = new Date();
this.dataArr.push(timestamp);
this.dataService.setLatestData(timestamp);
}
}
Parent Component HTML
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<button
(click)="onAddTimestamp()"
>
Add Timestamp
</button>
<app-child
[allData] = "dataArr"
></app-child>
Use the ngOnChanges() lifecycle method in your component.
ngOnChanges is called right after the data-bound properties have been
checked and before view and content children are checked if at least
one of them has changed.
Some like this
#Input() object: string;
ngOnChanges(changes: SimpleChanges) {
console.log(changes.object.currentValue);
// You can also use object.previousValue and
// object.firstChange for comparing old and new values
}

Angular 2+: Child components ts variable changes but UI does not show changed value?

I have a child TestComponent component as follows:
import { Component, OnInit, Input } from '#angular/core';
import { ApiService } from '../../../api.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
constructor(private apiService: ApiService) { }
testDisplayMessage = 'No data to show';
ngOnInit() {
}
getMessage(param: string) {
this.callingTest = true;
this.apiService.getTest( param ).subscribe( data => {
this.setTestDisplayMessage( data );
this.callingTest = false;
}, err => {
console.log( JSON.stringify( err ) );
this.setTestDisplayMessage( 'Failed to get data' );
this.callingTest = false;
} );
}
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
}
}
contents of test.component.html
<p style="padding: 10px;">{{ testDisplayMessage }}</p>
Use in parent componet :
Trigger JS Code in parent component on button click,
import { TestComponent } from './test/test.component';
....
.....
#Component({
providers: [ TestComponent ],
templateUrl: 'parent.component.html'
})
export class ParentComponent implements OnInit {
...
constructor(private testComponent: TestComponent) { }
...
// Button on parent template triggers this method
getMessage() {
this.testComponent.getMessage('Hello');
}
...
}
Html tag added in parent component,
<app-test></app-test>
When I debugged above code trigger point, call to setTestDisplayMessage() happens the field testDisplayMessage in TestComponent gets changed but UI shows the old message 'No data to show', why is the message on change does not reflect on UI template? Or this is not the way it is supposed to get used? Shall I use #Input
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
Use #ViewChild()
In html file:
<app-test #childComp></app-test>
In parent component.ts file
import { Component, OnInit, ViewChild } from '#angular/core';
....
.....
#Component( {
templateUrl: 'parent.component.html'
} )
export class ParentComponent implements OnInit {
#viewChild('childComp') childComp: any;
constructor() { }
...
// Button on parent template triggers this method
getMessage() {
this.childComp.getMessage('Hello');
}
...
}
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
definitely use #Input() but on set method
#Input()
set someProperty(value) {
// do some code
}
now every time you pass new value here, code will run
basically, your approach is wrong, please use Input() or Services to share data between components.
however, if you want to make ur code work, the below may work
import change detector
constructor(private cdRef: ChangeDetectorRef) {
}
note: import reference ->
import { ChangeDetectorRef } from '#angular/core';
execute detect change after the value is updated
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
this.cdRef.detectChanges();
}
I hope this helps

Angular 2, How to pass options from parent component to child component?

I have been looking for a solution for this for a while. I have tried a bunch of different things from #Input, #Query, dynamicContentLoader, #Inject, #Inject(forwardRef(() but haven't been able to figure this out yet.
My Example Structure:
parent.ts
import {Component} from 'angular2/core';
#Component({
selector : 'my-app',
directives : [Child],
template : `<parent-component></parent-component>`
})
export class Parent
{
private options:any;
constructor()
{
this.options = {parentThing:true};
}
}
child.ts
import {Component} from 'angular2/core';
#Component({
selector : 'parent-component',
template : `<child-component></child-component>`
})
export class Child
{
constructor(private options:any) <- maybe I can pass them in here somehow?
{
// what I am trying to do is get options from
// the parent component at this point
// but I am not sure how to do this with Angular 2
console.log(options) <- this should come from the parent
// how can I get parent options here?
// {parentThing:true}
}
}
This is my current HTML output in the DOM, so this part is working as expected
<parent-component>
<child-component></child-component>
</parent-component>
Question Summarized:
How can I pass options from a parent component to a child component and have those options available in the child constructor?
Parent to child is the simplest form of all but it's not available in the constructor, only in ngOnInit() (or later).
This only requires an #Input() someField; in the child component and using binding this can be passed from parent to children. Updates in the parent are updated in the child (not the other direction)
#Component({
selector: 'child-component',
template'<div>someValueFromParent: {{someValueFromParent}}</div>'
})
class ChildComponent {
#Input() someValueFromParent:string;
ngOnInit() {
console.log(this.someValue);
}
}
#Component({
selector: 'parent-component',
template: '<child-component [someValueFromParent]="someValue"></child-component>'
})
class ParentComponent {
someValue:string = 'abc';
}
to have it available in the constructor use a shared service. A shared service is injected into the constructor of both components. For injection to work the service needs to be registered in the parent component or above but not in the child. This way both get the same instance.
Set a value in the parent and read it in the client.
#Injectable()
class SharedService {
someValue:string;
}
#Component({
selector: 'child-component',
template: '<div>someValueFromParent: {{someValueFromParent}}</div>'
})
class ChildComponent {
constructor(private sharedService:SharedService) {
this.someValue = sharedService.someValue;
}
someValue:string = 'abc';
}
#Component({
selector: 'parent-component',
providers: [SharedService],
template: '<child-component></child-component>'
})
class ParentComponent {
constructor(private sharedService:SharedService) {
sharedService.someValue = this.someValue;
}
someValue:string = 'abc';
}
update
There is not much difference. For DI only the constructor can be used. If you want something injected it has to be through the constructor. ngOnInit() is called by Angular when additional initialization has taken place (like bindings being processed). For example if you make a network call it doesn't matter if you do it in the constructor on in ngOnInit because the call to the server is scheduled for later anyway (async). When the current sync task is completed, JavaScript looks for the next scheduled task and processes it (and so on). Therefore it's probably so that the server call initiated in the constructor is actually sent after ngOnInit() anyway no matter where you place it.
You could use an #Input parameter:
import {Component,Input} from 'angular2/core';
#Component({
selector : 'parent-component',
template : `<child-component></child-component>`
})
export class Child {
#Input()
options:any;
ngOnInit() {
console.log(this.options);
}
}
Notice that the value of options is available in the ngOnInit and not in the constructor. Have a look at the component lifecycle hooks for more details:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
And provides the options as decribed below:
import {Component} from 'angular2/core';
#Component({
selector : 'my-app',
directives : [Child],
template : `<parent-component [options]="options"></parent-component>`
})
export class Parent {
private options:any;
constructor() {
this.options = {parentThing:true};
}
}
If you want to implement custom events: child triggers an event and the parent register to be notified. Use #Output.

Categories

Resources