Angular BehaviorSubject在服务和组件中的使用,可能的竞争条件

时间:2017-08-16 06:41:27

标签: angular rxjs

我确定这是一个简单的问题,我只是不知道在哪里查明它。 我有一个UserProfile服务,为userdata创建一个BehaviorSubject。 当我登录时,我使用此服务来广播用户数据。 登录后,我在Dashbord组件中订阅了这个userdata。但是userdata observable只在我第一次创建BehaviorSubject时返回第一个空对象。就好像使用.next()推送新数据的登录功能仅在仪表板订阅了BehaviourSubject之后运行。我尝试将我的仪表板的订阅放在ngAfterContentInit()中,以便.next()在订阅之前运行,但我仍然得到一个空白对象。如果我通过我的dashbord中的set_new_data()函数运行.next()来检查是否可观察到的工作,我得到.next()传递的数据;但我没有通过我的服务中的set_userdata()函数获取我的登录组件设置的数据。

我们不能使用服务分享这样的数据吗?离开登录组件后服务是否被销毁或正在发生什么?

我的USerProfile服务:

@Injectable()
export class UserProfileService {
    public isLoggedIn: boolean = false;
    public userdata: BehaviorSubject<any> = new BehaviorSubject<any>({});

    constructor() {

    }

    public set_userdata(data) {
        // this.userdata = data;
        console.log(this.userdata);
        if (this.userdata === undefined) {
            console.log(data);
            //this.userdata = new BehaviorSubject<any[]>(data);
            console.log(this.userdata);
        } else {
            console.log(this.userdata);
            this.userdata.next(data);
        }

    }
}

我的Dashbord组件:

@Component({
    selector: 'dashboard',
    styleUrls: ['./dashboard.scss'],
    templateUrl: './dashboard.html'
})
export class Dashboard {
    public isLoggedIn: boolean;
    public curr_user: object;

    constructor(private _appGlobals: AppGlobals, private _user: UserProfileService) {
        this._appGlobals.isUserLoggedIn.subscribe(value => this.isLoggedIn = value);
        // this.curr_user = _user.userdata;
    }

    ngAfterContentInit() {
        console.log("this executes first");
        this._user.userdata.subscribe(value => {
            this.curr_user = value;
            console.log(value);
        });
    }

    set_new_data() {
        let obj = {
            name: "test",
            age: 45
        };
        this._user.userdata.next(obj);
    }
}

我的控制台输出,(显示空对象:{},预期用户数据)

enter image description here

2 个答案:

答案 0 :(得分:1)

确保您只有一个服务实例,否则订阅者可能会订阅与您用于发布事件的实例不同的BehaviorSubject

在组件上提供服务可以导致创建与此组件的实例一样多的服务实例。

可能导致多个实例的另一个缺陷是延迟加载的模块,这些模块获得了自己的DI根范围 来自非延迟加载模块的提供程序被提升到应用程序根范围,即使多个非延迟加载的模块提供相同的服务,也只有一个实例。

由于DI范围在初始化后无法更新,因此无法将提供者从延迟加载的模块提升到应用程序根范围,因此新的&#34; root&#34;创建范围(作为应用程序根范围的子范围或另一个延迟加载的&#34; root&#34;范围),用于加载在一起的每组延迟加载模块。

使用forRoot允许从延迟加载的模块向应用程序根范围添加提供程序。另见https://angular.io/guide/ngmodule-faq#what-is-the-forroot-method
或者只是在AppModule或任何其他已知不会直接加载延迟的模块中提供服务。

答案 1 :(得分:0)

如果从userdata返回set_userdata(data)作为Observable,然后订阅该函数而不是Subject本身,则代码应该按预期工作。虽然我不确定最佳做法;

//UserProfileService
public set_userdata(data) { //--> Maybe make this get_userdata()
    // this.userdata = data;
    console.log(this.userdata);
    if (this.userdata === undefined) {
        console.log(data);
        console.log(this.userdata);
    } else {
        console.log(this.userdata);
        this.userdata.next(data);
    }
    return this.userdata.asObservable();
}

//Dashboard
ngAfterContentInit() { //--> this could be in ngOnInit
    console.log("this executes first");
    this._user.set_userdata().subscribe(value => {
        this.curr_user = value;
        console.log(value);
    });
}