Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an abstract state in Angular2 Router?

I'm looking for similar solution to ui-router abstract state in Angular2 Router.

To have "virtual" parent state to resolve some common things for child states.

like image 827
Stepan Suvorov Avatar asked Dec 25 '22 01:12

Stepan Suvorov


1 Answers

A route doesn't need to be associated with a component. It can just have children with an empty path. You can use this "empty" route for things like guards or resolves.

One thing I didn't know until testing right now, is that Resolves (it's data) will trickle down to child routes. So if you want to "resolve some common things for child states", this might be the place to do it.

{
  path: '',
  resolve: {
    data: DummyResolve
  },
  children: [
    { path: 'one', component: Child1Component },
    { path: 'two', component: Child2Component }
  ]
}

Here is the complete test I used to test with

import { Component, Injectable, OnInit } from '@angular/core';
import { Router, Resolve, ActivatedRoute } from '@angular/router';
import { By } from '@angular/platform-browser';
import { Location, CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TestBed, inject, fakeAsync, tick, ComponentFixture } from '@angular/core/testing';

@Injectable()
class DummyResolve implements Resolve<string> {
  resolve() {
    return 'Hello Routing';
  }
}

@Component({
  template: `
    <router-outlet></router-outlet>
  `
})
class RoutingComponent {
}

@Component({
  template: `
    <h1>Child One</h1>
    <span>{{ data }}</span>
  `
})
class Child1Component implements OnInit {
  data: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.data.forEach((data: { data: string }) => {
      this.data = data.data;
      console.log(`data from child 1: ${this.data}`);
    });
  }
}

@Component({
  template: `
    <h1>Child Two</h1>
    <span>{{ data }}</span>
  `
})
class Child2Component implements OnInit {
  data: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.data.forEach((data: { data: string }) => {
      this.data = data.data;
      console.log(`data from child 2: ${this.data}`);
    });
  }
}

describe('component: RoutingComponent', function () {
  let fixture: ComponentFixture<RoutingComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        CommonModule,
        RouterTestingModule.withRoutes([
          {
            path: '',
            resolve: {
              data: DummyResolve
            },
            children: [
              { path: 'one', component: Child1Component },
              { path: 'two', component: Child2Component }
            ]
          }
        ])
      ],
      providers: [ DummyResolve ],
      declarations: [ RoutingComponent, Child1Component, Child2Component ]
    });

    fixture = TestBed.createComponent(RoutingComponent);
    fixture.detectChanges();
  });

  it('should go to child one',
    fakeAsync(inject([Router, Location], (router: Router, location: Location) => {

    router.navigate(['/one']);
    tick();
    fixture.detectChanges();
    let debugEl = fixture.debugElement;
    expect(debugEl.query(By.css('h1')).nativeElement.innerHTML).toEqual('Child One');
    expect(debugEl.query(By.css('span')).nativeElement.innerHTML).toEqual('Hello Routing');
  })));

  it('should go to child two',
    fakeAsync(inject([Router, Location], (router: Router, location: Location) => {

    router.navigate(['/two']);
    tick();
    fixture.detectChanges();
    let debugEl = fixture.debugElement;
    expect(debugEl.query(By.css('h1')).nativeElement.innerHTML).toEqual('Child Two');
    expect(debugEl.query(By.css('span')).nativeElement.innerHTML).toEqual('Hello Routing');
  })));
});
like image 75
Paul Samsotha Avatar answered Dec 28 '22 09:12

Paul Samsotha