Angular 2: Observable / Subscription未触发

21

我在我的应用程序中已经多次这样做了。它很简单,应该可以工作……但是这一次没有。

我的问题:

我从组件A中调用了一个服务中的方法,我的组件B已经订阅了,但没有反应也没有收到任何东西。subscribe()没有触发!

navigation-elements.service.ts

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigation$: Observable<any>;

    private updateIBOsNavigationSubject = new Subject<any>();

    constructor() {
        this.updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();
    }

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}
IboDetailsGeneral 组件
export class IboDetailsGeneral implements OnInit, OnDestroy {
    id: string;
    private sub: any;

    constructor(private route: ActivatedRoute, private iboService: IBOsService, private navigationService: NavigationElementsService) {
        this.sub = this.route.params.subscribe(params => {
            this.id = params['id'];

            console.log('CALLING updateIBOsNavigation FUNCTION');
            this.navigationService.updateIBOsNavigation(this.id);
        });
    }

    ngOnInit() {
        console.log('CALLING updateIBOsNavigation FUNCTION AGAIN');
        this.navigationService.updateIBOsNavigation('test');
    }

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

这个组件触发了服务的方法:updateIBOsNavigation

IBOsNavigationElement 组件

export class IBOsNavigationElement implements OnInit {
    private id: string;

    constructor(private navigationService: NavigationElementsService) {
        this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
                log.d('I received this Navigation Data:', JSON.stringify(navigationData));
                this.id = navigationData;
            }
        );
    }

    ngOnInit() {
    }
}

这个组件已经订阅,应该列出并接收数据...

让我们梳理一下 DI(依赖注入):

需要考虑到 IboDetailsGeneral 位于应用程序结构的较低层,因此 IboDetailsGeneralIBOsNavigationElement子级

这就是为什么我将 NavigationElementsService 添加到 IBOsNavigationElement 模块中的原因:

NavigationModuleIBOsNavigationElement 的模块

@NgModule({
    imports: [
        // A lot of stuff
    ],
    declarations: [
        // A lot of stuff
        IBOsNavigationElement
    ],
    exports: [
        // A lot of stuff
    ],
    providers: [
        NavigationElementsService
    ]
})

控制台:

调用updateIBOsNavigation函数

updateIBOsNavigation "95"

再次调用updateIBOsNavigation函数

updateIBOsNavigation "test"

这些控制台结果告诉我:

  • 方法正在被调用,所以没有提供者错误。 与服务的通信良好

我的测试:

  • 我尝试在IBOsNavigationElement(侦听器)中随机调用一个方法,并且与服务的通信良好。
  • NavigationElementsService仅在NavigationModule中添加到providers中,因此只有一个服务实例,是吗?然后,以下链接中解释的问题不会发生:Angular 2 observable subscription not triggering

很抱歉我写了一堵文本墙,但目前我有点绝望。

感谢任何帮助,谢谢!

更新1:

在第一个回答之后,我尝试了几件事情...

使用ReplaySubject

@Injectable()
export class NavigationElementsService {
    public updateIBOsNavigation$ = new ReplaySubject();

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigation$.next(navigationData);
    }
}

结果:调用updateIBOsNavigation()时,subscribe()仍未触发。

使用BehaviorSubject

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigationSubject = new BehaviorSubject<any>('');
     updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}

结果:在初始化时进入subscribe(),但当我调用updateIBOsNavigation()时,subscribe()仍未触发。

使用BehaviorSubject v2:

我尝试了这种方法:behaviourSubject in angular2 , how it works and how to use it

@Injectable()
export class NavigationElementsService {
    public updateIBOsNavigation$: Subject<string> = new BehaviorSubject<string>(null);

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigation$.next(navigationData);
    }
}

结果:与 BehaviorSubject 先前应用的结果相同。

更新2:

在整个网络上研究后,更多绝望尝试的示例...

使用 BehaviorSubject v3:

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigation$: Observable<any>;
    updateIBOsNavigationSubject = <BehaviorSubject<any>> new BehaviorSubject([]);

    constructor() {
        this.updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();
    }

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation()', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}

结果:与之前的BehaviorSubject尝试相同... 绝望正在上升...

更新3:

为了确保NavigationElementsService是一个单例,我想要做个说明:

export class NavigationModule {
    static forRoot() {
        return {
            ngModule: NavigationModule,
            providers: [NavigationElementsService]
        };
    }
}

导入时:

imports: [
        NavigationModule.forRoot()
       ]

结果:与以往一样的问题,subscribe()未触发,但至少我知道有一个NavigationElementsService实例。


我正在尝试绝望地添加更新...请结束我的折磨!xD - SrAxi
你好,你现在解决了这个问题吗? - Bones and Teeth
@BonesandTeeth 是的!谢谢!:D - SrAxi
1
你能告诉我你是怎么让它工作的吗?我也遇到了同样的问题... - Bones and Teeth
@BonesandTeeth 当然,只需阅读答案和随后的所有评论。在我的最后一条评论中,我解释了如何解决我的问题。希望你也能解决它!干杯! - SrAxi
{btsdaf} - bharath muppa
3个回答

46

我认为问题出在你的 Subject 类型上。使用 Subject,任何在事件发生后订阅的组件都不会收到已发出的值。如果想要向后订阅者重放先前的值,请改用 BehaviorSubjectReplaySubject,而不是 Subject

更多有关不同 Subject 类型的信息,请查看http://reactivex.io/documentation/subject.html

BehaviorSubject

当观察者订阅 BehaviorSubject 时,它从源 Observable 中最近发出的项目开始发出(如果还没有发出,则为种子/默认值),然后继续发出源 Observable(s) 后面发出的任何其他项目。

ReplaySubject

ReplaySubject 向任何观察者发出由源 Observable(s) 发出的所有项,而不管观察者何时订阅。

设置 BehaviorSubject 时需要提供默认值。因此,您需要使用某些值来创建它:

updateIBOsNavigationSubject = new BehaviorSubject<any>('');
updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();

updateIBOsNavigation(navigationData) {
    log.d('updateIBOsNavigation', JSON.stringify(navigationData));
    this.updateIBOsNavigationSubject.next(navigationData);
}

ReplaySubject 不需要默认值。

编辑

当使用共享服务时,确保该服务仅在根模块中提供非常重要。如果它在多个地方提供,则组件可能无法获得相同的实例。即使该服务在根级别提供,如果在根级别和组件之间的某个模块提供了该服务,则新实例将沿着依赖注入树的该分支发送。

希望这有所帮助。


1
@TylerJennings 再次感谢!但是,它仍然只能工作一次,不能在我调用 updateIBOsNavigation() 时工作。 - SrAxi
1
有趣。看起来一切都应该工作。你说subscribe()只能工作一次,之后就不能再工作了? - Tyler Jennings
2
这个服务所在的模块只提供在第一个根模块中吗?是否有可能在根模块和这些组件所在的模块之间创建服务的新实例?在我提供的示例中,我在多个地方使用完全相同的语法而没有问题。 - Tyler Jennings
6
@TylerJennings 的时间点非常巧合!我正要发布这个答案。我刚刚使它工作了!而且,确切地说,是因为该服务被包含在 AppModule 和最终组件之间的 @Module 中。所以,Subject 的类型与此无关,它是服务的包含:必须在根模块 = AppModule 中。如果您想将此添加到您的问题中,我可以将其作为正确答案添加,如果不想,我会发布一个带有更新代码的答案。无论如何,你的回答显然得到了我的赞同,我在过去的24小时里研究了很多关于 Subject 类型的知识。谢谢! - SrAxi
1
“编辑”部分应该放在顶部,因为那是真正有帮助的部分 :) - sr9yar
显示剩余11条评论

8
当我们将一个服务包含在“providers”中时,它就会被实例化,并且在其组件及其子组件之间维护此状态。
如果我们在两个组件的提供程序数组中都包含该服务,则它不再遵循单例模式。状态将是独立的,不会在它们之间共享。
因此,请仅在父组件或根组件中包含您的服务。
我也遇到了这个问题,并通过这里的解决方案解决了它:https://dev59.com/61oT5IYBdhLWcg3w6iy3#38034298

3

在我的情况下,我在父模块 "app.module" 和子模块中都使用了相同的服务。我从子模块的提供程序部分中移除了服务。它起作用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接