当注入的服务上的值更新时,如何更新AngularJS(使用TypeScript)控制器中的值

时间:2014-12-27 19:41:02

标签: javascript angularjs typescript

目前我正在努力学习来自Actionscript背景的Typescript和AngularJS。我一直在玩一些代码实验,而van通常会实现我想要的但不总是以我想要的方式实现!

目前我有这段代码:

export class CountingService
{
    countScope = { count : 0 };

    public increment()
    {
        this.countScope.count++;
    }
}

export class PageController
{
    constructor( private service : CountingService )
    {
        this.countScope = service.countScope;
    }

    public localCount : number = 0;

    countScope;

    public increment()
    {
        this.service.increment();

        this.localCount++;
    }

}

我有几个不同的视图,其中包含以下代码:

<div ng-controller="PageController as page">

    <Button ng-click="page.increment()">Count {{page.localCount}}/{{page.countScope.count}}</Button>

</div>

当我在视图之间切换时,localCount总是重置为零,但来自服务的countScope中的计数不会重置,并且会被每个不同的控制器递增。

这有效,但有点乱。我必须让无类型的localScope浮动,视图需要知道countScope对象的内部结构并绑定到page.countScope.count而不是像page.globalCount。

在Actionscript中,我只能读取控制器。每次更改值时,服务都会调度一个事件,控制器会监听它,然后更新它自己的属性,然后更新视图。

我的问题是,实现我在这里做的事情的最佳实践方法是什么,不需要视图知道无类型对象的内部。 我很确定公共吸气剂不存在,但我可以发送和收听事件吗?

非常感谢

5 个答案:

答案 0 :(得分:1)

最后回答三。我没想到这会起作用,但确实如此,恕我直言,这是迄今为止最好的解决方案。

它使用getter强制封装,不需要注入$ scope,也不用担心内存泄漏。

我欢迎任何批评指出我不知道的问题。

我唯一知道的是你需要启用ECMAScript 5,这意味着不支持IE 7和8。这对我来说很好。

export class CountingService
{
    private _serviceCount : number = 100;

    public increment()
    {
        this._serviceCount++;
    }

    get serviceCount() : number
    {
        return this._serviceCount;
    }
}

export class PageController
{
    constructor( private countingService : CountingService )
    {}

    private _localCount : number = 0;

    get localCount() : number
    {
        return this._localCount;
    }

    get serviceCount() : number
    {
        return this.countingService.serviceCount;
    }

    public increment()
    {
        this.countingService.increment();

        this._localCount++;
    }
}

答案 1 :(得分:1)

这似乎非常适合反应库,正是我们所做的。我强烈建议你查看具有一些很棒功能的RxJs。 https://github.com/Reactive-Extensions/RxJS/tree/master/doc

https://www.youtube.com/watch?v=XRYN2xt11Ek

您使用Rx进行编码

export class CountingService {

    //Create a stream of changes.  When caller subscribe always give them the most recent value. 
    private rawCountStream = new Rx.Subject<number>();
    public countStream : Rx.Observable<number>;
    private count = 0;

    constructor() {
        this.countStream = this.rawCountStream.replay(1);
    }

    public increment() {
        this.count++;
        //Pump the value to callers. 
        this.rawCountStream.onNext(this.count);
    }
}

export class PageController {

    constructor(private service: CountingService) {
        //Listen to the stream of changes. 
        service.countStream.subscribe(value => {
            //do something
            //IMPORTANT If you want angular to update the ui you will need to call $apply on a scope. 
            //RXJS has a scheduler for this so you look at that here. 
            //https://github.com/Reactive-Extensions/rx.angular.js
            }
        );
    }

    public localCount: number = 0;

    public increment() {
        this.service.increment();
        this.localCount++;
    }
}

答案 2 :(得分:0)

这是第1号答案,我认为John Kurlak正在讨论的方法是:

export class CountingService
{
    serviceCount : number = 100;

    public increment()
    {
        this.serviceCount++;
    }
}

export class PageController
{
    constructor( $scope, private service : CountingService )
    {
        this.serviceCount = service.serviceCount;

        $scope.$watch( () => this.service.serviceCount, ( newValue : number, oldValue : number ) => this.updateLocal( newValue, oldValue ) );
    }

    localCount : number = 0;

    serviceCount : number;

    public increment()
    {
        this.service.increment();

        this.localCount++;
    }

    private updateLocal( newValue : number, oldValue : number )
    {
        this.serviceCount = newValue;
    }

}

这是有效的,我认为是我追求的那种解决方案。有些事情我不喜欢它。

我不喜欢将$ scope注入我的服务。它似乎是对我不应该需要的东西的额外依赖。 我也真的不喜欢可以通过任何东西更新并打破封装的公共成员。我想用getter解决这个问题,但我无法弄清楚如何更新到ECMAScript 5(问题:Targeting ES5 with TypeScript in IntelliJ IDEA 14)。

约翰 - 这就是你提出的建议吗?如果有人对优点和缺点评论这个答案,我将非常感激。 我将此标记为答案,因为我认为这是我开始时的目标。

答案 3 :(得分:0)

这是答案2,我认为这是PSL所得到的(或类似的)

interface CountingCallBack
{
    parent : any;
    callback : ( value : number ) => void;
}

export class CountingService
{
    private _observerCallBacks : Array<CountingCallBack> = [];

    private _serviceCount : number = 100;

    public increment()
    {
        this._serviceCount++;

        this.notifyObservers();
    }

    public registerObserverCallback( callbackParent : any, callbackFunction : ( value : number ) => void )
    {
        var callback : CountingCallBack = { parent : callbackParent, callback : callbackFunction };

        this._observerCallBacks.push( callback );
        this.updateObserver( callback );
    }

    private notifyObservers()
    {
        angular.forEach( this._observerCallBacks, this.updateObserver, this );
    }

    private updateObserver( callback : CountingCallBack )
    {
        callback.callback.apply( callback.parent, [ this._serviceCount ] );
    }

}

export class PageController
{
    constructor( private countingService : CountingService )
    {
        countingService.registerObserverCallback( this, this.updateLocal );
    }

    localCount : number = 0;

    serviceCount : number;

    public increment()
    {
        this.countingService.increment();

        this.localCount++;
    }

    private updateLocal( newValue : number )
    {
        this.serviceCount = newValue;
    }
}

我很高兴我能够实现这一点,因为我现在已经了解了功能界面语法是如何工作的(而且我非常喜欢它)并且我设法通过&#39;这个&#39;来解决问题。控制器上的updateLocal函数中的范围问题。

这个解决方案不需要和$ scope注入,这很好,但我发现回调实现非常混乱。我不想将控制器和控制器上的功能传递给服务以添加回调。 我想我可以创建一个接口,ICountListener或其他东西,然后将其传递给服务,然后在控制器上调用updateCount。那会更整洁但仍然不那么好。 设置回调的方法是否比这更简洁呢?

此代码的最大问题(以及不应使用它的原因)是它会造成内存泄漏。如果继续切换视图,控制器永远不会被清理,因此内存中有许多控制器都响应更新的计数。 清理回调和取消注册回调会相对容易,但这可能涉及注入$ scope然后听取毁灭事件(我不知道如何做到这一点但我认为这是在在上面的评论中讨论。)

答案 4 :(得分:-1)

简短回答:服务是单例,即构造函数调用一次。如果要重置它,可以从控制器的构造函数中执行此操作。

this.service.countScope.count = 0

注释

我看到很多我不喜欢的代码示例。但我确信它只是一个样本,所以不要将其视为个人冒犯;)

  • 没有名为service的服务。称之为countService
  • 该服务没有成员countScope。只需使用count即可。并且不要将countScope复制到当前控制器。只需使用this.countService.count
  • 即可

更新

  

这个问题的根源是当服务中的值发生变化时如何更新html中的显示值

由于您将控制器设为pagepage.service是服务,因此{{page.service.countScope.count}}只显示计数。