'How to pass data from angluar component to angular layout component
I have a page where the contract has some basic information display in app-contract and I have a menu with different taps each tab contains some information about the contract shipping the components needed etc. I just click on a tab and it load contract details example the shipping information of the contract. However my issue is that when I do a search in the search tab the <app-contract></app-contract> fields are not updating for example the contract number remains the same. I am not sure what to use emitter viewchild etc to get the a <app-contract></app-contract> component to be up to date.
Here is a visual representation of angular component structure
here is the relevant code for the contract search. Has you can see I have added a change: EventEmitter<Contract> = new EventEmitter<Contract>(); in the search component <app-contract-search></app-contract-search> I am not sure what to do after words with it.
@Component({
selector: 'app-contract-search',
templateUrl: './contract-search.component.html',
styleUrls: ['./contract-search.component.css']
})
export class ContractSearchComponent implements OnInit {
public contractNumber: string;
public contract: Contract;
public searchValue: string;
@Output()
change: EventEmitter<Contract> = new EventEmitter<Contract>();
constructor(private route: ActivatedRoute, private router: Router, private toastr: ToastrService, private contractService: ContractService) {}
ngOnInit(): void {
// First get the contract id from the current route.
const url = window.location.href;
var parts = url.split('/', 6);
this.contractNumber = parts[4];
this.searchValue = this.contractNumber;
console.log("Contract Id form url : " + this.contractNumber);
this.GetContract(this.contractNumber);
}
GetContract(id: string) {
this.contractService.getContractByContractNumber(Number(id)).subscribe(result => {
this.contract = result;
this.change.emit(this.contract);
}, error => {
console.log(error)
});
}
search() {
this.contractService.checkIfContractExists(this.searchValue).subscribe(contract => {
if (contract) {
console.log("contract " + this.searchValue + " found");
this.GetContract(contract.contractNumber.toString());
this.toastr.success("Success","The contract is being loaded!");
this.router.navigate(['/contract', contract.contractNumber, 'search']);
}
else {
console.log("contract " + this.searchValue + " NOT found");
this.toastr.error("Error", "The contract number was not found!");
}
}, error => {
console.log(error)
});
}
}
And then here in the <app-contract></app-contract> component is where I want to get the update contract information here. Is there a way I can listen to the event emitter here?
@Component({
selector: 'app-contract',
templateUrl: './contract-layout.component.html',
styleUrls: ['./contract-layout.component.css']
})
export class ContractComponent implements OnInit {
public contract: Contract;
public contractId: number;
constructor(private router: ActivatedRoute, private http: HttpClient,
private contractService: ContractService ) { }
ngOnInit(): void {
// First get the product id from the current route.
const routeParams = this.router.snapshot.paramMap;
this.contractId = Number(routeParams.get('contractId'));
console.log("Contract Id form url : " + this.contractId);
this.GetContractByNumber(this.contractId);
}
GetContractByNumber(number: number) {
this.contractService.getContractByContractNumber(number).subscribe(result => {
this.contract = result;
}, error => {
console.log(error)
});
}
}
And here is the layout of my contract
<p *ngIf="!contract"><em>{{'HtmlElement.Loading' | translate}}</em></p>
<div *ngIf="contract">
<h2>Contract details {{contract.contractNumber}}</h2>
<form action="" id="GeneralForm" method="post">
//Here is the form Form Contract details
<div class="form-row">
<div class="col-md-2 mb-3">
<br>
<input id="btnSalvar" class="btn btn-primary" type="submit" value="Update">
</div>
</div>
</form>
<div class="card">
<p class="card-header">
<app-contract-menu></app-contract-menu>
</p>
<div class="card-body">
<router-outlet></router-outlet>
</div>
</div>
</div>
Solution 1:[1]
I can propose another option. The idea is to use an Observable Subject in your ContractService.
So in your ContractService declare something like:
private _newContractSearched = new Subject<Contract>();
newContractSearched = this._newContractSearched.asObservable();
public triggerNewContractSearched(contract: Contract){
this._newContractSearched.next(contract);
}
Then in ContractSearchComponent, every time you search:
this.contractService.triggerNewContractSearched(contract);
And finaly in ContractComponent just subscribe to that observable:
this.contractService.newContractSearched.subscribe(contract: Contract => {
// Here you have the contract object
});
Maybe this is not the best solution, but should work.
Solution 2:[2]
You can alter you app component ts to the following
@Component({
selector: 'app-contract',
templateUrl: './contract-layout.component.html',
styleUrls: ['./contract-layout.component.css']
})
export class ContractComponent implements OnInit {
public contract: Contract;
public contractId: number;
constructor(private router: ActivatedRoute, private http: HttpClient,
private contractService: ContractService, private ChangeDetectorRef
changeDetector) { }
ngOnInit(): void {
// First get the product id from the current route.
const routeParams = this.router.snapshot.paramMap;
this.contractId = Number(routeParams.get('contractId'));
console.log("Contract Id form url : " + this.contractId);
this.GetContractByNumber(this.contractId);
}
GetContractByNumber(number: number) {
this.contractService.getContractByContractNumber(number).subscribe(result => {
this.contract = result;
this.changeDetector.detectChanges();// this will force rerender your component
}, error => {
console.log(error)
});
}}
Solution 3:[3]
I think you should do something like this:
in app-contract component
<app-contract-search (change)="GetContractByNumber($event.number)"><app-contract-search>
Solution 4:[4]
Your component has been created once and since it is not getting any new inputs, nothing is forcing it to be rerendered
1- Move your App Contract component onInit event code to OnChanges event instead
2- Inject the contract as an input in your component, so when ever contract changes the onChanges event will be called
3- You are emitting the contract from search using event emitter therefore you can grab it in html and pass it to the app contract component as input
3- Your Html should look like that
<app-contract-search (change)= "contract=$event"></app-contract-search>
<app-contract [contract]="contract"></app-contract>
4- Your new App Component ts code
public [input] contract: Contract;
5- you don't need to do getContract method or any further subscriptions because you will be duplicating the logic
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Robert Cojocaru |
| Solution 2 | Sameh |
| Solution 3 | Robert Cojocaru |
| Solution 4 |


