414

In one of my Angular 2 routes's templates (FirstComponent) I have a button

first.component.html

<div class="button" click="routeWithData()">Pass data and route</div>

My goal is to achieve:

Button click -> route to another component while preserving data and without using the other component as a directive.

This is what I tried...

1ST APPROACH

In the same view I am storing collecting same data based on user interaction.

first.component.ts

export class FirstComponent {
     constructor(private _router: Router) { }

     property1: number;
     property2: string;
     property3: TypeXY; // this a class, not a primitive type

    // here some class methods set the properties above

    // DOM events
    routeWithData(){
         // here route
    }
}

Normally I'd route to SecondComponent by

 this._router.navigate(['SecondComponent']);

eventually passing the data by

 this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);

whereas the definition of the link with parameters would be

@RouteConfig([
      // ...
      { path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent} 
)]

The issue with this approach is that I guess I can't pass complex data (e.g. an object like property3) in-url;

2ND APPROACH

An alternative would be including SecondComponent as directive in FirstComponent.

  <SecondComponent [p3]="property3"></SecondComponent>

However I want to route to that component, not include it!

3RD APPROACH

The most viable solution I see here would be to use a Service (e.g. FirstComponentService) to

  • store the data (_firstComponentService.storeData()) on routeWithData() in FirstComponent
  • retrieve the data (_firstComponentService.retrieveData()) in ngOnInit() in SecondComponent

While this approach seems perfectly viable, I wonder whether this is the easiest / most elegant way to achieve the goal.

In general I'd like to know whether I'm missing other potential approaches to pass the data between components, particularly with the less possible amount of code

mpro
  • 14,302
  • 5
  • 28
  • 43
dragonmnl
  • 14,578
  • 33
  • 84
  • 129
  • 4
    thanks @Prashobh. `Pass data using Query Parameters` is what i was looking for. your [link](http://www.angulartutorial.net/2017/12/3-simple-ways-to-share-data-through.html) saved my day. – Raj Nov 28 '18 at 14:21
  • Angular 7.2 has now new feature to pass data between routes using `state` check the [PR](https://github.com/angular/angular/pull/27198) for more details. Some useful information [here](https://netbasal.com/set-state-object-when-navigating-in-angular-7-2-b87c5b977bb) – AzizKap21 Apr 18 '19 at 22:14
  • @Prashobh Thanks a lot. The link which you have shared is very useful – vandu Jan 24 '20 at 10:27
  • Working example: https://stackoverflow.com/a/69420764/7186739 – Billu Nov 24 '21 at 17:57
  • 2
    Routing is a complex feature in Angular and definitely worth learning! Here you may find interesting details about passing data via the routing: https://indepth.dev/tutorials/angular/indepth-guide-to-passing-data-via-routing This guide goes through various techniques about using static data in routing definition and dynamic data (state) during specific navigation. – Maciej Wojcik Jul 23 '22 at 11:46

20 Answers20

298

Update 4.0.0

See Angular Angular Router - Fetch data before navigating for more details.

Original

Using a service is the way to go. In route params you should only pass data that you want to be reflected in the browser URL bar.

See Angular Angular Cookbook Component Communication - Bidirectional Service.

The router shipped with RC.4 re-introduces data

constructor(private route: ActivatedRoute) {}
const routes: RouterConfig = [
  {path: '', redirectTo: '/heroes', pathMatch: 'full'},
  {path: 'heroes', component: HeroDetailComponent, data: {some_data: 'some value'}}
];
class HeroDetailComponent {
  ngOnInit() {
    this.sub = this.route
      .data
      .subscribe(v => console.log(v));
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

See also the Plunker.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • thanks for the answer. I had a look at the docs. Can you please clarify when I should opt for observables and when just set attributes using a set/get method in the service? – dragonmnl Apr 25 '16 at 10:31
  • Observables allow interested components to subscribe to changes. With properties you need to poll (`{{myService.someProp}}` is recognized and updated by Angulars change detection though). If you want to switch to the way more performant `OnPush` change detection strategy `Observables` have a strong advantage because then your code depends on being notified about changes so your code can invoke change detection. – Günter Zöchbauer Apr 25 '16 at 10:34
  • 4
    Is this answer still valid for Angular 2.1.0 ? – smartmouse Oct 21 '16 at 09:50
  • 14
    RC.4 router data is only for static data. You can not send different data to the same route it always has to be the same data am I wrong? – aycanadal Dec 06 '16 at 11:33
  • If you use a resolver, then you get the data the resolver returns. – Günter Zöchbauer Dec 06 '16 at 11:35
  • Is there a way to ensure the data has been stored before retrieving it? For example in the OP situation, not loading `SecondComponent` until a good response from the store data has been received. Otherwise `SecondComponent` would be empty – fidev May 24 '17 at 13:42
  • I guess it would be better to create a new question with more details (some code). I don't really get what you try to accomplish. – Günter Zöchbauer May 24 '17 at 14:02
  • @GünterZöchbauer I am a bit confused... i have 3 components which use same data but how can i update the data in all 3 components if the data is changed by 1 of the 3 components... 1 is app component 1 is popup component which is inside app component..i can exchange data thr.. i can pass data to router-outlet by using service but if after passing data..the data is changed by popup component..it wont change in router-outlet component..how can i update data thr?? – Himanshu Bansal Jun 29 '17 at 10:04
  • @HimanshuBansal please create a new question with the code that demonstrates what you try to accomplish – Günter Zöchbauer Jul 01 '17 at 10:14
  • Is it possible to pass data during navigate instead of in the router as described in this answer? Example, this.router.navigate(['/apply', res.loanApplicationId, data{}]); – Anthony Aug 02 '17 at 18:59
  • 1
    No, use a shared service for this use case. – Günter Zöchbauer Aug 02 '17 at 19:00
  • How to get that 'some value'. getting an array in this.sub – user630209 Oct 03 '17 at 05:57
  • @user630209 sorry, I don't understand the question. I'd suggest you create a new question that contains your code and more details about what you try to accomplish. – Günter Zöchbauer Oct 03 '17 at 06:01
  • 11
    In Angular 5, anyway, you should be able to ... `ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }` – Roger Mar 13 '18 at 07:52
  • how to pass dynamic json? – bhaumik shah Jun 01 '18 at 13:06
  • @bhaumikshah use a shared service. – Günter Zöchbauer Jun 02 '18 at 10:02
  • @GünterZöchbauer is there any example for reference? – bhaumik shah Jun 04 '18 at 04:53
  • Injecting a service is an effective hack that dates back to the first release of the framework and continues to this day. This approach also stinks to high heaven. Components should not be wired for eternity to services. That's what @input is designed for. – Rick O'Shea Jun 11 '18 at 03:59
  • 1
    @RickO'Shea that's clearly wrong. Services can be configured by providers at any parent level and are therefore much more flexible with less coupling than inputs. Routed components don't support values being passed through inputs anyway. – Günter Zöchbauer Jun 11 '18 at 04:01
  • 8
    If you're able to use Angular v7.2 it allows for passing state now in the router using the `NavigationExtras` - https://stackoverflow.com/a/54879389/1148107 – mtpultz Feb 26 '19 at 06:17
  • sadly, this doesnt work anymore (checked on angular 10) - you can pass data in route config, but not get it in cmp like in Gunther's answer above. But you can use this: this.router.events.subscribe((data) => { if (data instanceof RoutesRecognized) { const passedData = data.state.root.firstChild.data.passedData; } }); – Sergey Sob May 02 '21 at 17:36
81

I think since we don't have $rootScope kind of thing in angular 2 as in angular 1.x. We can use angular 2 shared service/class while in ngOnDestroy pass data to service and after routing take the data from the service in ngOnInit function:

Here I am using DataService to share hero object:

import { Hero } from './hero';
export class DataService {
  public hero: Hero;
}

Pass object from first page component:

 ngOnDestroy() {
    this.dataService.hero = this.hero; 
 }

Take object from second page component:

 ngOnInit() {
    this.hero = this.dataService.hero; 
 }

Here is an example: plunker

Utpal Kumar Das
  • 1,226
  • 14
  • 21
  • This is beautiful, but how common in the Ng2 community is this? I can't recall reading it in the docs... – Alfa Bravo Jul 10 '18 at 09:16
  • 1
    Comparing to other option like url parameters or other browser storage this seems to me better. I also did not see in any documentation to work like this. – Utpal Kumar Das Jul 10 '18 at 19:59
  • 3
    Does it work when the user opens a new tab and copy paste the second component route? Can I able to fetch `this.hero = this.dataService.hero`? Will I get the values? –  Aug 09 '18 at 11:14
  • 1
    This is indeed very simple and every Angular developer knows but problem is once you refresh you loose data in the services. User will have to do all stuffs again. – Santosh Kadam Sep 19 '19 at 15:06
  • @SantoshKadam the question is "How do I pass data to Angular routed components?" so passing data by ngOnDestroy and ngOnInit functions is a way, and always simple is the best. If user needs to get data after reload then there need to save data in a permanent storage and read again from that storage. – Utpal Kumar Das Sep 20 '19 at 07:10
  • Warning! If you use this method and make refresh of the page, the data from the service will be deleted! Its better to store the values in a database – IonicMan Jul 24 '21 at 13:33
  • Where did you got the attribute dataService? – Luis Alfredo Serrano Díaz Jan 23 '22 at 01:08
76

Angular 7.2.0 introduced new way of passing the data when navigating between routed components:

@Component({
  template: `<a (click)="navigateWithState()">Go</a>`,
})
export class AppComponent  {
  constructor(public router: Router) {}
  navigateWithState() {
    this.router.navigateByUrl('/123', { state: { hello: 'world' } });
  }
}

Or:

@Component({
  selector: 'my-app',
  template: `
  <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>`,
})
export class AppComponent  {}

To read the state, you can access window.history.state property after the navigation has finished:

export class PageComponent implements OnInit {
  state$: Observable<object>;

  constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }
}
Washington Braga
  • 1,593
  • 15
  • 14
  • 16
    does not work for me, `window.history.state` returns something like `{navigationId: 2}` instead of returning the object I passed-in. – Louis Jul 25 '19 at 15:04
  • @Louis which Angular version are you using? – Washington Braga Jul 26 '19 at 18:38
  • I am using angular version 8.1.0 – Louis Jul 28 '19 at 12:58
  • I'm seeing the same thing as Louis, with a lower version than his but still high enough that it's supposed to have that feature. – WindowsWeenie Oct 03 '19 at 00:43
  • as part of the returned object, `navigationId` with a value is added. If no data is added, only the `navigationId` will be shown. Be see the passed data, go back or refresh your app and re-trigger the action that adds the data to the route and navigates to the next route (eg. button click). This will then add your data to the object. – Alf Moh Oct 04 '19 at 08:37
  • After refreshing, the data gets cleared. You should store the data somewhere temporarily if you want it to be available after refresh. – jjasspper Mar 20 '20 at 16:02
  • use { state: {data: {hello: 'world'}} } instead { state: { hello: 'world' } } to avoid navigationId ... restore it by history.state.data – Ermindo Lopes Jun 04 '20 at 13:48
  • 5
    There is a 640k data size browser limit on the state object. https://stackoverflow.com/questions/24425885/failed-to-execute-pushstate-on-history-error-when-using-window-history-pushs – P.Brian.Mackey Sep 22 '20 at 15:05
  • @Louis I played around with the setting of state and it looks like "navigationId" is a reserved key that cannot be set using ```navigateByUrl()```. If you change the name of the key that you set in the "state" node to something like "navigation_id", you will then get ```window.history.state``` to return ```{navigation_id: , navigationId: 2}```. – John Verco Apr 29 '22 at 16:11
  • try following this article. it works good for me. it uses ```history.state``` instead. https://medium.com/ableneo/how-to-pass-data-between-routed-components-in-angular-2306308d8255#:~:text=There%20are%20several%20ways%20how,and%20directly%20calling%20component's%20API – stoneshishang Aug 24 '22 at 15:16
  • `this.router.navigate(['/123'], { state: { hello: 'world' } });` should work – buffalo94 Dec 21 '22 at 23:20
46
<div class="button" click="routeWithData()">Pass data and route</div>

well the easiest way to do it in angular 6 or other versions I hope is to simply to define your path with the amount of data you want to pass

{path: 'detailView/:id', component: DetailedViewComponent}

as you can see from my routes definition, I have added the /:id to stand to the data I want to pass to the component via router navigation. Therefore your code will look like

<a class="btn btn-white-view" [routerLink]="[ '/detailView',list.id]">view</a>

in order to read the id on the component, just import ActivatedRoute like

import { ActivatedRoute } from '@angular/router'

and on the ngOnInit is where you retrieve the data

ngOnInit() {
       this.sub = this.route.params.subscribe(params => {
        this.id = params['id'];
        });
        console.log(this.id);
      }

you can read more in this article https://www.tektutorialshub.com/angular-passing-parameters-to-route/

Rohit Sharma
  • 3,304
  • 2
  • 19
  • 34
  • 24
    what if i want to send a complex object? i don't want to bloat my routes to unmaintainable nonsense :( – cmxl Dec 11 '18 at 12:45
  • 2
    @cmxl Use a shared service then. – Standaa - Remember Monica Mar 27 '19 at 16:45
  • @cmxl the idea with sending only the id or a simple string as data is to make the URL more 'sharable' and easily crawlable by bots etc. So that the resultant link can be shared by users of your app. For sending bigger objects, a service will be more effective. – Muhammad bin Yusrat Apr 27 '21 at 10:10
35

I looked at every solution (and tried a few) from this page but I was not convinced that we have to kind of implement a hack-ish way to achieve the data transfer between route.

Another problem with simple history.state is that if you are passing an instance of a particular class in the state object, it will not be the instance while receiving it. But it will be a plain simple JavaScript object.

So in my Angular v10 (Ionic v5) application, I did this-

this.router.navigateByUrl('/authenticate/username', {
    state: {user: new User(), foo: 'bar'}
});

enter image description here

And in the navigating component ('/authenticate/username'), in ngOnInit() method, I printed the data with this.router.getCurrentNavigation().extras.state-

ngOnInit() {
    console.log('>>authenticate-username:41:',
        this.router.getCurrentNavigation().extras.state);
}

enter image description here

And I got the desired data which was passed-

enter image description here

Shashank Agrawal
  • 25,161
  • 11
  • 89
  • 121
  • `extras` ? is that something you just defined or an angular property? – Gel Aug 13 '20 at 16:18
  • That's a property from Angular. – Shashank Agrawal Aug 14 '20 at 07:40
  • 1
    exactly what i was looking for thanks mate..here is the upvote for you ;) I am also using it in ionic5 proj – minigeek Aug 14 '20 at 11:56
  • @minigeek Yes, I'm also using it in Ionic. That's where I dug into Angular. – Shashank Agrawal Aug 15 '20 at 02:13
  • Just a doubt. I am able to pass data.. but if I have to pass instance of inappbrowser to different module..its not able to serialise and threw error? Any idea other way?I don't really want to import whole module in my page just for one inappbrowser – minigeek Aug 15 '20 at 05:12
  • In that case, the only way is to use the service reference. Basically, define a global service and use its scope to pass the reference. Or else you can use observables/subject as well. – Shashank Agrawal Aug 15 '20 at 06:36
  • 7
    Wonderful answer! It's important to remember that accessing the ```state``` (after routing to the new page) only worked on the ```constructor``` for me, and not inside the ```ngOnInit```. That's because the ```getCurrentNavigation()``` was null. – Itay Jan 24 '21 at 09:26
  • That's working fine for us. What's your Angular version @Itay? – Shashank Agrawal Jan 24 '21 at 12:01
  • 2
    @Itay I agree. I am using Angular 11. Current navigation scope is ended before ngOnInit(). So I had to take the state value from constructor. – Naseem Ahamed Apr 15 '21 at 05:51
  • 2
    Data is lost if page is refreshed – Ojonugwa Jude Ochalifu May 24 '21 at 08:03
  • Yes, of course. That is supposed to get lost. You need to implement the caching on your own if it is a requirement. – Shashank Agrawal May 24 '21 at 19:51
  • 1
    it worked for me ONLY after i moved the code from ngOnInit to constructor – Shaybc Dec 08 '21 at 09:04
  • @OjonugwaJudeOchalifu data is lost because it is not stored into the URL (queryParams). Please vote up [this issue](https://github.com/angular/angular/issues/47307) for the angular team to support storing JSON objects in urls – Flavien Volken Sep 02 '22 at 05:35
24

It is 2019 and many of the answers here would work, depending on what you want to do. If you want to pass in some internal state not visible in URL (params, query) you can use state since 7.2 (as I have learned just today :) ).

From the blog (credits Tomasz Kula) - you navigate to route....

...from ts: this.router.navigateByUrl('/details', { state: { hello: 'world' } });

...from HTML template: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

And to pick it up in the target component:

constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }

Late, but hope this helps someone with recent Angular.

PeS
  • 3,757
  • 3
  • 40
  • 51
21

I this the other approach not good for this issue. I thing the best approach is Query-Parameter by Router angular that have 2 way:

Passing query parameter directly

With this code you can navigate to url by params in your html code:

<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>

Passing query parameter by Router

You have to inject the router within your constructor like:

constructor(private router:Router){

}

Now use of that like:

goToPage(pageNum) {
    this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });
}

Now if you want to read from Router in another Component you have to use of ActivatedRoute like:

constructor(private activateRouter:ActivatedRouter){

}

and subscribe that:

  ngOnInit() {
    this.sub = this.route
      .queryParams
      .subscribe(params => {
        // Defaults to 0 if no query param provided.
        this.page = +params['serviceId'] || 0;
      });
  }
AmirReza-Farahlagha
  • 1,204
  • 14
  • 26
  • 1
    this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} }); can be replaced with this.router.navigate(['/product-list'], { queryParams: { serviceId} }); – Ramakant Singh Aug 22 '19 at 07:20
21

Some super smart person (tmburnell) that is not me suggests re-writing the route data:

let route = this.router.config.find(r => r.path === '/path');
route.data = { entity: 'entity' };
this.router.navigateByUrl('/path');

As seen here in the comments.

I hope someone will find this useful

terary
  • 940
  • 13
  • 30
17

Solution with ActiveRoute (if you want pass object by route - use JSON.stringfy/JSON.parse):

Prepare object before sending:

export class AdminUserListComponent {

  users : User[];

  constructor( private router : Router) { }

  modifyUser(i) {

    let navigationExtras: NavigationExtras = {
      queryParams: {
          "user": JSON.stringify(this.users[i])
      }
    };

    this.router.navigate(["admin/user/edit"],  navigationExtras);
  }

}

Receive your object in destination component:

export class AdminUserEditComponent  {

  userWithRole: UserWithRole;      

  constructor( private route: ActivatedRoute) {}

  ngOnInit(): void {
    super.ngOnInit();

      this.route.queryParams.subscribe(params => {
        this.userWithRole.user = JSON.parse(params["user"]);
      });
  }

}
scorpion
  • 671
  • 1
  • 9
  • 16
10

Routes:

{ path: 'foo-route', component: FooComponent, data: { myData: false } },

In component access the data object once:

pipe(take(1)) unsubsrcibes immediately so there is no memory leak and no need to manually unsubscribe

constructor(private activatedRoute: ActivatedRoute) { ... }

ngOnInit(): void {
  this.activatedRoute.data.pipe(take(1)).subscribe((data) => {
    console.log(data); // do something with the data
  });
}
  • remember to import needed stuff

Edit: the new firstValueFrom() could be better

O-9
  • 1,626
  • 16
  • 15
5

3rd approach is most common way to share data between components. you may inject the item service which you want to use in related component.

import { Injectable } from '@angular/core';
import { Predicate } from '../interfaces'

import * as _ from 'lodash';

@Injectable()
export class ItemsService {

    constructor() { }


    removeItemFromArray<T>(array: Array<T>, item: any) {
        _.remove(array, function (current) {
            //console.log(current);
            return JSON.stringify(current) === JSON.stringify(item);
        });
    }

    removeItems<T>(array: Array<T>, predicate: Predicate<T>) {
        _.remove(array, predicate);
    }

    setItem<T>(array: Array<T>, predicate: Predicate<T>, item: T) {
        var _oldItem = _.find(array, predicate);
        if(_oldItem){
            var index = _.indexOf(array, _oldItem);
            array.splice(index, 1, item);
        } else {
            array.push(item);
        }
    }


    addItemToStart<T>(array: Array<T>, item: any) {
        array.splice(0, 0, item);
    }


    getPropertyValues<T, R>(array: Array<T>, property : string) : R
    {
        var result = _.map(array, property);
        return <R><any>result;
    }

    getSerialized<T>(arg: any): T {
        return <T>JSON.parse(JSON.stringify(arg));
    }
}



export interface Predicate<T> {
    (item: T): boolean
}
ahankendi
  • 244
  • 3
  • 9
  • 3
    The service gets instantiated when switching routes. So you loose data – Jimmy Kane Jun 10 '17 at 14:43
  • 1
    @JimmyKane You speaking about specifically when the page refreshes but if it doesn't refresh then the memory is still saved in a service. This should be the default behaviour since it will save loading many times. – Aaron Rabinowitz Aug 10 '17 at 15:50
  • 1
    @AaronRabinowitz right. Sorry for the confusion. And sorry for the down vote. Wish I could undo it now. Too late. Was new to angular 2 and my problem with trying your approach was that I had the service provided to many components and not provided via the app module. – Jimmy Kane Aug 10 '17 at 16:09
3

Pass using JSON

  <a routerLink = "/link"
   [queryParams] = "{parameterName: objectToPass| json }">
         sample Link                   
  </a>
  • 10
    This would be a better answer if you could show how the parameter is consumed in the receiving component as well - the whole route that it takes. Meaning if someone does not know how to pass a parameter, he is also not going to know how to use this parameter in the receiving component. :) – Alfa Bravo Jul 10 '18 at 09:14
  • 1
    A disadvantage to this is there's a size limitation to the querystring and sometimes you don't want object properties visible in the address bar. – C.M. Oct 17 '18 at 19:32
3

use a shared service to store data with a custom index. then send that custom index with queryParam. this approach is more flexible.

// component-a : typeScript :
constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.DataCollector['someDataIndex'] = data;
}

// component-a : html :
<a routerLink="/target-page" 
   [queryParams]="{index: 'someDataIndex'}"></a>

.

// component-b : typeScript :
public data;

constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.route.queryParams.subscribe(
        (queryParams: Params) => {
            this.data = this.DataCollector[queryParams['index']];
        }
    );
}
Amin Adel
  • 970
  • 3
  • 17
  • 33
2

say you have

  1. component1.ts
  2. component1.html

and you want to pass data to component2.ts.

  • in component1.ts is a variable with data say

      //component1.ts
      item={name:"Nelson", bankAccount:"1 million dollars"}
    
      //component1.html
       //the line routerLink="/meter-readings/{{item.meterReadingId}}" has nothing to 
      //do with this , replace that with the url you are navigating to
      <a
        mat-button
        [queryParams]="{ params: item | json}"
        routerLink="/meter-readings/{{item.meterReadingId}}"
        routerLinkActive="router-link-active">
        View
      </a>
    
      //component2.ts
      import { ActivatedRoute} from "@angular/router";
      import 'rxjs/add/operator/filter';
    
      /*class name etc and class boiler plate */
      data:any //will hold our final object that we passed 
      constructor(
      private route: ActivatedRoute,
      ) {}
    
     ngOnInit() {
    
     this.route.queryParams
      .filter(params => params.reading)
      .subscribe(params => {
      console.log(params); // DATA WILL BE A JSON STRING- WE PARSE TO GET BACK OUR 
                           //OBJECT
    
      this.data = JSON.parse(params.item) ;
    
      console.log(this.data,'PASSED DATA'); //Gives {name:"Nelson", bankAccount:"1 
                                            //million dollars"}
       });
      }
    
Nelson Bwogora
  • 2,225
  • 1
  • 18
  • 21
2

You can use BehaviorSubject for sharing data between routed components. A BehaviorSubject holds one value. When it is subscribed it emits the value immediately. A Subject doesn't hold a value.

In the service.

@Injectable({
  providedIn: 'root'
})
export class CustomerReportService extends BaseService {
  reportFilter = new BehaviorSubject<ReportFilterVM>(null);
  constructor(private httpClient: HttpClient) { super(); }

  getCustomerBalanceDetails(reportFilter: ReportFilterVM): Observable<Array<CustomerBalanceDetailVM>> {
    return this.httpClient.post<Array<CustomerBalanceDetailVM>>(this.apiBaseURL + 'CustomerReport/CustomerBalanceDetail', reportFilter);
  }
}

In the component you can subscribe to this BehaviorSubject.

this.reportService.reportFilter.subscribe(f => {
      if (f) {
        this.reportFilter = f;
      }
    });

Note: Subject won't work here, Need to use Behavior Subject only.

sushil suthar
  • 639
  • 9
  • 12
2

By default i won't use a guard for this one for me it is more can i enter the route or can i leave it. It is not to share data betweenn them.

If you want to load data before we entered a route just add an resolver to this one this is also part of the Router.

As very basic example:

Resolver

import { Resolve, ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { take } from "rxjs/operators";

@Injectable()
export class UserResolver implements Resolve<User> {

    constructor(
        private userService: UserService,
        private route: ActivatedRoute
    ) {}

    resolve(): Observable<firebase.User> {
        return this.route.params.pipe(
            switchMap((params) => this.userService.fetchUser(params.user_id)),
            take(1)
        );
    }
}

put to the router:

RouterModule.forChild([
{
    path: "user/:user_id",
    component: MyUserDetailPage,
    resolve: {
        user: UserResolver
    }
  }
}]

get the data in our component

ngOnInit() {
    const user: firebase.User = this.activatedRoute.snapshot.data.user;
}

The downside on this approach is, he will enter the route first if he get the user data not before, this ensures the data for the user has been loaded and is ready on start of the component, but you will stay on the old page as long the data has been loaded (Loading Animation)

0

One fine solution is to implement a Guard with canActivate method. In this scenario you can fetch data from a given api and let user access the component describe in the routing file. In the meantime one can set the data property of the route object and retrieve it in the component.

Let say you have this routing conf:

const routes: Routes = [
    { path: "/:projectName", component: ProjectComponent, canActivate: [ProjectGuard] }
]`

in your guard file you may have:

canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot)
: Observable<boolean> | Promise<boolean> | boolean {
return this.myProjectService.getProject(projectNameFoundElsewhere).pipe(
  map((project) => {
    if (project) {
      next.data = project;
    }
    return !!project;
  }),
);

}`

Then in your component

constructor(private route: ActivatedRoute) {
    this.route.data.subscribe((value) => (this.project = value));
}

This way is a bit different than passing via a service since service keep the value in a behaviorSubject as long as it is not unset. Passing via tha guard make the data available for the current route. I havent check if the children routes keep the data or not.

Jeff G.
  • 1
  • 1
0

In scenario where data needs to be passed to another Route, best and simplest solution is using { window.localStorage }. Also, do not remember to remove data from local storage once its use is over. I used ngOnDestroy's destroy() method to clean up this data. This also resolves a problem where data is lost by page refresh.

0

If you have a formula collection which handles a couple of ng components which are basely build on a collection / array of class objects hold approx. 10 props e.g. include input values, nominal value and at least units and Booleans …, so to keep the page status (input+results) ends into duplicate a lot of stuff.

Therefore, I simulate a routing by using *ngif to display the related parts (component s) of the single page but never change the url.

<div *ngIf="visibleComponentA>
... All part of ComponetA 
  ></div>

CpmponetA.html

<div *ngIf="visibleComponentB>
... All part of ComponetB 
  ></div>

CpmponetB.html

This Boolean will be set inside the relate code of the component:

@Input()visibleComponentA: boolean = true; 

ComponetA.ts

Now in the top page

<div (click)="OnClickNav(visibleComponentA)" >ComponentA</div>
<div (click)="OnClickNav(visibleComponentB)" >ComponentB</div> 

app.component.html

and the method OnClickNav(Selected:NavFlags) switching the correct visible status of the component.

OnClickNav(Selected:NavFlags){

    Selected.NavStatus=!Selected.NavStatus

    Selected.NavItem=='visibleComponetA'? this.visibleComponetA.NavStatus=Selected.NavStatus: this.visibleComponetA.NavStatus= false;
    Selected.NavItem=='visibleComponetB'? this.visibleComponetB.NavStatus=Selected.NavStatus: this.visibleComponetB.NavStatus= false;

app.commonet.ts

The class NavFlags is simple

export class NavFlags {
  NavItem: string = '';
  NavStatus: boolean = false;

  constructor(NavItem: string, NavStatus: boolean) {
    this.NavItem = NavItem;
    this.NavStatus = NavStatus;
  }
}

nav-flags.ts

By this the "individual" pages will not leave an no data are lost. I have no duplicated store. The complete example can be visit on https://angulartool.de. By clicking the button, it is possible to navigate through the page in components without loss of data.

This hack is not perfect, so maybe there will be better way to solve this angular matter.

JuergenG
  • 15
  • 1
  • 6
0

There are several approaches to pass data to the routed components.

One of the approach is through the 'Query Paramaeters'.

If i want to pass data of table-id from the TableReservationComponent then i can send it through query params.

TableReservationComponent.ts

TableId : number = 2;

this.router.navigate(['order'],{queryParams:{table_id : TableId}});

Now in the OrderComponent , we are able to get the table_id from the TableReservationComponent.

export class OrderComponent implements OnInit

{
    tableId : number ;

    constructor(private route: ActivatedRoute) { }

    ngOnInit(): void {
       this.route.queryParams.subscribe(params => {
       this.tableId = params['table_id'];
       
        });
    }

}

Here the table_id is coming from the TableReservationComponent in a Key Value Pair , and I can access in Order Component With the Key(params['table_id']) and I can Use the table_id in the OrderComponent