We are trying to use the NavigationExtras to pass data from one component to another (one page to another) like below
viewProjectDetails(e) {
const navigationExtras: NavigationExtras = {
state: {
ProjectInfo: e.data,
UserSelection: this.UserSelection
}
};
this.router.navigate(['dashboard/ProjectShipment'], navigationExtras);
}
I am trying to get the ProjectInfo and UserSelection array in to the another component
projDetail : any;
userSelection: any;
getPrjDetails() {
const navigation = this.activatedRoute.getCurrentNavigation();
const state = navigation.extras.state as {
}
Listen to the queryParams and catch the NavigationExtras
this.route.queryParams.subscribe(params => {
console.log(params["state"]);
});
constructor(private router: Router) {
console.log(this.router.getCurrentNavigation().extras.state.Projectinfo);
// should log e.data
}
You need to call getCurrentNavigation() method inside of the constructor, elsewhere the navigation has finished.
Related
I have a List and Details components in an application and I am trying to navigate to Details component by passing id parameter. However, there is not a reponse or error when calling the following method. I also share the routing.module:
routing.module
const routes: Routes = [
{
path: '',
component: ListComponent,
data: {...}
},
{
path: '/details/:id',
component: DetailsComponent,
data: {...}
}
];
list.component
constructor(private router: Router) {}
details(id) {
// the code hits here and get the id parameter correctly
this.router.navigate(['/details'], {
queryParams: { id: id }
});
}
details.component
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
this.route.paramMap
.subscribe(params => {
let id = +params.get('id');
});
}
So, what is wrong with this approach? The ngOnInit block of the details page is not fired.
In your route, you have specified '/details/:id' where ID is Router Param not a Query Param.
Thus, if you want to navigate to that url, use this instead:
ListComponent
this.router.navigate(['/details', id])
DetailsComponent
constructor(private route: ActivatedRoute) {}
ngOnInit() {
const id = this.route.snapshot.params.id; // Fetch the ID from your
// current route "/details/:id"
}
or you can also do it this way
ngOnInit() {
this.route.params.subscribe(params => console.log(params.id))
}
More info on Angular Router Documentation
you have to add queryParamsHandling: 'merge' to your code
details(id) {
// the code hits here and get the id parameter correctly
this.router.navigate(['/details'], {
queryParams: { id: id },
queryParamsHandling: 'merge'
});
}
Hello in the app component.html
Please add
<router-outlet></router-outlet>
I am trying to share data between components using the rxjs subject and i've used that data in component
Component.html
<div class="spinner-container" *ngIf="loading">
<div class="spinner-item">
<nx-spinner nxSize="large"></nx-spinner>
</div>
</div>
component.ts
ngOnInit(){
setTimeout(()=>{
this.commonService.spinnerTrigger.subscribe((trigger)=>{
this.loading = trigger;
})
},100)
}
Here is the error
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed
after it was checked. Previous value: 'ngIf: false'. Current value:
'ngIf: true'.
I found a workaround using changedetectref but I don't think its good practice is ther any other way to solve this issue
You can manually trigger change detection using the detectChanges() method of the ChangeDetectorRef
Try like this:
import { ChangeDetectorRef} from '#angular/core';
constructor(private cdr: ChangeDetectorRef) { }
ngOnInit(){
setTimeout(()=>{
this.commonService.spinnerTrigger.subscribe((trigger)=>{
this.loading = trigger;
if (this.cdr && !(this.cdr as ViewRef).destroyed) {
this.cdr.detectChanges();
}
})
},100)
}
Making the next callback async worked for me once:
this.commonService.spinnerTrigger.subscribe(async (trigger) => {
this.loading = await trigger;
});
Or adding a zero delay:
this.commonService.spinnerTrigger.pipe(delay(0)).subscribe((trigger) => {
this.loading = trigger;
});
This is an open issue in Github,
Github issue => https://github.com/angular/angular/issues/15634
And they provided a workaround using setTimeout() for now and still there aren't any updates regarding this issue.
And also you can try changeDetector that may solve your issue.
import { ChangeDetectorRef } from '#angular/core';
constructor(private cdRef:ChangeDetectorRef) {}
ngAfterViewChecked()
{
this.cdRef.detectChanges();
}
I don't see any need here to mess around with change detection / setTimeout (which triggers change detection).
Stackblitz
Use a spinner service which parent and child can use.
spinner.service.ts
#Injectable()
export class SpinnerService {
private loading = new BehaviorSubject<boolean>(true)
loading$: Observable<boolean> = this.loading.asObservable()
setSpinner(bool: boolean) {
this.loading.next(bool)
}
}
Example - Component setting spinner
ngOnInit() {
this.service.getChildData().pipe(
// handle any errors
catchError(err => {
console.log('Error caught: ', err)
this.data = err
return throwError(err)
}),
// no matter what set spinner false
finalize(() => {
this.spinnerService.setSpinner(false)
}),
// subscription clean up
takeUntil(this.destroyed$)
).subscribe(data => this.data = data)
}
Example - parent / container displaying spinner
ngOnInit() {
this.loading$ = this.spinnerService.loading$
this.spinnerService.setSpinner(true) // if needed
}
<div *ngIf="loading$ | async">
I am a spinner
</div>
I want to pass an object between 2 components. I created the following shared service:
[PageService Component]
private messageSource = new BehaviorSubject([]);
currentMessage = this.messageSource.asObservable();
changeMessage(message) {
this.messageSource.next(message)
}
And I have implemented it in these 2 components:
[COMPONENT WHEN I GET ON CLICK SONO DATAS]
constructor(private pageService: PageService, private _sanitizer: DomSanitizer) {}
...
onClickMethod(){
self.pageService.getCustomers(self.filters).toPromise().then(response => {
self.searchResults = response;
});
self.pageService.changeMessage(self.searchResults);
}
and
[Component where I need to see above datas]
ngOnInit() {
let self = this;
self.pageService.currentMessage.subscribe(message => self.searchResults = message);
console.log(self.searchResults);
}
Now...if I put the "changeMessage" method in the first component in the method onInit or in the costructor and i try to pass some data like [1,2,3] (so not the response of another api rest) it seems to work...this doesn't work just when i put it inside onClick method and passing "self.searchResults" (the response)...anyone can help me?
Thanks
Go from this
self.pageService.getCustomers(self.filters).toPromise().then(response => {
self.searchResults = response;
});
self.pageService.changeMessage(self.searchResults);
To this
self.pageService.getCustomers(self.filters).toPromise().then(response => {
self.searchResults = response;
self.pageService.changeMessage(self.searchResults);
});
Because you make an HTTP call (I assume), you should wait for the call to end. In your code, it doesn't.
I am using angular 5
my scenario is from one component im setting the data in service and from another component i'm getting that data|
Component- 1:
makeUser(row){
this.agentsService.setSelectedAgentData(row); // setting the data (row) in agentsService.
const mrf = this.modalService.open(MakeUserComponent);
}
Service:
declaring the varible in class.
public selectedData:any = {};
setter and getter methods are as below
setSelectedAgentData(selectedTableRowData){
this.selectedData = selectedTableRowData;
}
getSelectedAgentData(){
return this.selectedData;
}
Component - 2:
ngOnInit() {
this.userDetails = this.agentsService.getSelectedAgentData();
this.roles = this.agentsService.getRolesList();
}
Here the selectedData value is an empty object when I call the method this.agentsService.getSelectedAgentData() from component -2
Any help would be appreciated.
You can use Subject (rxjs library) for this purpose. So Subject can generate data on the one hand. And on the other hand, you can subscribe to changes in any place.
You service would look like this:
#Injectable()
export class YourService {
public selectedData: Subject<any>;
constructor() {
this.selectedData = new Subject();
}
generateSelectedAgentData(row: string) {
this.selectedData.next(row);
}
}
In your first Component:
makeUser(row){
this.agentsService.generateSelectedAgentData(row);
const mrf = this.modalService.open(MakeUserComponent);
}
In your second Component:
constructor(private ys: YourService){
this.ys.selectedData.subscribe(
data => {
console.log(data);
this.userDetails = data;
});
}
I have issue, with passing async data to child component. I trying to write dynamic form generator. Issue starts when I try to call json via Observable and pass it into child component.
service:
generateSearchFields2(): Observable<any> {
return this.http
.get(this.API + 'searchFields')
.map((res:Response) => {
res.json().data as any;
for (var i = 0; i < res.json().data.length; i++) {
var searchField = res.json().data[i];
switch (searchField.component) {
case "TextboxQuestion":
let TXQ: TextboxQuestion = new TextboxQuestion({
key: searchField.key,
label: searchField.label,
value: searchField.value,
required: searchField.required,
order: searchField.order
});
this.searchFieldModels.push(TXQ);
console.log("TXQ: ", TXQ, this.searchFieldModels);
break;
case "DropdownQuestion":
let DDQ: DropdownQuestion = new DropdownQuestion({
key: searchField.key,
label: searchField.label,
required: searchField.required,
options: searchField.options,
order: searchField.order
});
this.searchFieldModels.push(DDQ);
console.log("TXQ: ", DDQ, this.searchFieldModels);
break;
default:
alert("DEFAULT");
break;
}
}
return this.searchFieldModels.sort((a, b) => a.order - b.order);
})
.catch(this.handleError);
}
Component Parent:
generateSearchFields2() {
this.service.generateSearchFields2()
.subscribe(res => this.searchFields = res)
}
Iam passing variable via INPUT directive in parent template to child: [searchFields]="searchFields"
Issue is in child component, where searchField has undefined value. In this child I pass value to another service, to create formContros, but I got undefined there also. Data missing starts here, in child:
#Input() searchFields: SearchBase<any>[] = [];
ngOnInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
Please for hint how I can pass async variable, to not loose data meantime
You can make #Input() searchFields a setter
private _searchFields: SearchBase<any>[] = [];
#Input() set searchFields(value SearchBase<any>[]) {
if(value != null) {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
}
get searchFields() : SearchBase<any>[] {
return this.searchFields;
}
You can also use ngOnChanges() which is called every time an input is updated, but a setter is usually more convenient except perhaps when the executed code depends on multiple inputs being set.
In the ngOnInit event the data which comes from the parent is not bound yet. So your searchFields is undefined yet. You can use it in NgAfterViewInit component lifecycle event.
#Input() searchFields: SearchBase<any>[] = [];
ngAfterViewInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
For other cases you can see Angular2 Component Lifecycle events