Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Router Using BehaviorSubject Observable in Resolve

I'm trying to set up my router config using a Resolve that returns an Observable from a BehaviorSubject. I've tried this in both angular 4.0.0-beta8 and angular 2.4.8+router 3.4.8

Here's my service:

@Injectable()
export class MyService {
    private _data: BehaviorSubject<Array<string>> = new BehaviorSubject(undefined);

    constructor() {}

    public getData(): Observable<Array<string>> {

        this._data.next(['test1', 'test2', 'test3']);

        let asObservable = this._data.asObservable().delay(1000);
        asObservable.subscribe((myData) => {
            console.log([myData, 'this console message DOES show up']);
        });

        // if I return here, my component's constructor and ngOnInit never fire
        // return asObservable;

        let fakeObservable = Observable.of(['test1', 'test2', 'test3']).delay(1000);
        fakeObservable.subscribe((fakeData) => {
            console.log([fakeData, 'this console message shows up']);
        });

        console.log([asObservable, fakeObservable]);
            /* console log output
            Observable {
                _isScalar: false,
                operator: DelayOperator,
                source: Observable {
                    _isScalar: false,
                    source: BehaviorSubject {
                        _isScalar: false,
                        _value: ['test1', 'test2', 'test3'],
                        closed: false,
                        hasError: false,
                        isStopped: false,
                        observers: Array[1],
                        thrownError: null,
                        value: ['test1', 'test2', 'test3']
                    }
                }
            },
            Observable {
                _isScalar: false,
                operator: DelayOperator,
                source: ScalarObservable {
                    _isScalar: true,
                    scheduler: null,
                    value: ['test1', 'test2', 'test3']
                }
            }
            */

        return fakeObservable; // this WILL reach my component constructor and ngOnInit
    }
}

Here's my resolve

@Injectable()
export class MyResolver implements Resolve<Array<string>> {

    constructor(private myService: MyService) {}

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Array<string>>|undefined {
        return this.myService.getData();
    }
}

Here's the router

RouterModule.forChild([{
    path: 'mypath',
    component: MyComponent,
    resolve: {
        data: MyResolver
    }
}]);

And here's the component:

@Component({
    selector: 'my-component',
    template: '<Span>My Component</span>'
})
export class MyComponent implements OnInit {
    constructor(private route: ActivatedRoute) {
        console.log('component constructor');
    }

    ngOnInit(): void {
        console.log(this.route.snapshot.data['data']); // ['test1', 'test2', 'test3']
    }
}

This is probably not the best way of designing the interaction between the resolve and the service, so I'm very open to suggestions there. However, I might go crazy if I don't figure out why BehaviorSubject.asObservable() doesn't work, but the mocked observable does work.

like image 659
SnailCoil Avatar asked Feb 21 '17 03:02

SnailCoil


2 Answers

I thought about this one overnight, and realized that I was using the resolve incorrectly. The crux of the problem is that the router expects the resolve result to eventually be completed. The BehaviorSubject, even though it only has one value at a time, will never be done, because the value can always change. I changed this._data.asObservable() to this._data.asObservable().first(), and it started working. It seems so obvious now!

like image 56
SnailCoil Avatar answered Oct 30 '22 17:10

SnailCoil


For Angular 6+ to complete observable in resolver you need to do as follows:

this.myService.getData().pipe(take(1))

// or

this.myService.getData().pipe(first())
like image 27
Timothy Avatar answered Oct 30 '22 16:10

Timothy