Angular + TypeScript:$ watchCollection,"控制器为"良好做法

Joh*_*lly 5 angularjs typescript angularjs-scope

一般来说,在我的Angular/TypeScript应用程序中,我倾向于使用"控制器为"语法.

然而,对于我正在研究的CRUD屏幕,我发现自己偏离了这一点.所以,我可以利用$watchCollection /检查更改等我发现自己使用由优秀建议章的@basarat模式在这里.也就是说,在视频中(在Angular中将"控制器作为"语法之前)@basarat创建一个表示被$scope调用者上的控制器的变量vm.与在视图中使用"controller as vm"一样,您仍然可以使用vm.myProperty/ vm.myFunction()style语法与模型进行交互.

所以,控制器看起来像这样:

module controllers {

    "use strict";

    interface sageEditRouteParams extends ng.route.IRouteParamsService {
        id: number;
    }

    interface sageEditScope extends ng.IScope {
        vm: SageEdit;
    }

    class SageEdit {

        log: loggerFunction;
        sage: sage;
        title: string;

        private _hasChanges: boolean;

        static $inject = ["$routeParams", "$scope", "common", "datacontext"];
        constructor(
            private $routeParams: sageEditRouteParams,
            private $scope: sageEditScope,
            private common: common,
            private datacontext: datacontext
            ) {

            this.sage = undefined;
            this.title = "Sage Edit";

            this.log = common.logger.getLogFn(controllerId);

            $scope.vm = this;
            $scope.$watchCollection("vm.sage", (newSage: sage, oldSage: sage) => {
                if (newSage && oldSage) {
                    this._hasChanges = true;
                }
            });

            this.activate();
        }

        // Prototype methods

        activate() {
            var id = this.$routeParams.id;
            var dataPromises: ng.IPromise<any>[] = [this.getSage(id)];

            this.common.activateController(dataPromises, controllerId)
                .then(() => {
                    this.log("Activated Sage Edit View");
                    this.title = "Sage Edit: " + this.sage.name;
                });
        }

        getSage(id: number) {
            return this.datacontext.sage.getById(id).then(data => {
                this.sage = data;
                this._hasChanges = false;
            });
        }

        get hasChanges(): boolean {
            return this._hasChanges;
        }
    }

    var controllerId = "sageEdit";
    angular.module("app").controller(controllerId, SageEdit);
}
Run Code Online (Sandbox Code Playgroud)

这样的观点:

<section class="mainbar" ng-controller="sageEdit">
    <section class="matter">
        <div class="container-fluid">
            <div>
                <button class="btn btn-info"
                        ng-click="vm.cancel()"
                        ng-disabled="!vm.canSave">
                    <i class="fa fa-undo"></i>Cancel
                </button>
                <button class="btn btn-info"
                        ng-click="vm.save()"
                        ng-disabled="!vm.canSave">
                    <i class="glyphicon glyphicon-save"></i>Save
                </button>
                <span ng-show="vm.hasChanges"
                      class="dissolve-animation ng-hide">
                    <i class="glyphicon glyphicon-asterisk text-info"></i>
                </span>
            </div>
            <div class="widget wgreen">
                <div data-cc-widget-header title="{{vm.title}}"></div>
                <div class="widget-content form-horizontal">
                    <div class="form-group">
                        <label class="col-xs-12 col-sm-2">Name</label>
                        <input class="col-xs-12 col-sm-9" ng-model="vm.sage.name" />
                    </div>
                    <div class="form-group">
                        <label class="col-xs-12 col-sm-2">Username</label>
                        <input class="col-xs-12 col-sm-9" ng-model="vm.sage.userName" />
                    </div>
                    <div class="form-group">
                        <label class="col-xs-12 col-sm-2">Email</label>
                        <input class="col-xs-12 col-sm-9" 
                               type="email"
                               ng-model="vm.sage.email" />
                    </div>
                </div>
            </div>
        </div>
    </section>
</section>
Run Code Online (Sandbox Code Playgroud)

我把它放在一起似乎工作得很好,但我想把它放在那里并得到一些其他观点.在那儿:

  1. 这种方法有任何缺点
  2. 任何原因这都是一个坏主意
  3. 更好的选择?

我做了一点挖掘,但没有找到任何结论.

bas*_*rat 6

魔术弦去除

对于

$scope.$watchCollection("vm.sage", (newSage: sage, oldSage: sage) => {
Run Code Online (Sandbox Code Playgroud)

您还可以轻松实现重构:

$scope.$watchCollection(()=>this.sage, (newSage: sage, oldSage: sage) => {
Run Code Online (Sandbox Code Playgroud)

在某些情况下,当您尝试观看像foo.bar在那里foo仍然是不确定的,你可以使用一个安全的包装函数safeWatch(()=>this.foo.bar):

 function safeWatch<T extends Function>(expression: T) {
        return () => {
            try {
                return expression();
            }
            catch (e) {
                return null;
            }
        };
    }
Run Code Online (Sandbox Code Playgroud)

控制器与$ scope.vm

Fron源代码:https: //github.com/angular/angular.js/blob/36831eccd1da37c089f2141a2c073a6db69f3e1d/src/ng/controller.js#L95

这正是角度为你做的事情.即$scope.vm = instance(其中vm == indentifier)所以它们是等价的