在 AngularJS 1.5 中使用 ES6 类 服务

Using ES6 Classes for services in AngularJS 1.5

我正在尝试重构一些 AngularJS 1.5 服务代码以利用 classes。

我的服务定义如下:

(() => {
    "use strict";

    class notification {
        constructor($mdToast, $mdDialog, $state) {

            /* injected members */
            this.toast = $mdToast
            this.dialog = $mdDialog
            this.state = $state

            /* properties */
            this.transitioning = false
            this.working = false
        }

        openHelp() {
            this.showAlert({
                "title": "Help",
                "textContent": `Help is on the way!`,
                "ok": "OK"
            })
        }
        showAlert(options) {
            if (angular.isString(options)) {
                var text = angular.copy(options)
                options = {}
                options.textContent = text
                options.title = " "
            }
            if (!options.ok) {
                options.ok = "OK"
            }
            if (!options.clickOutsideToClose) {
                options.clickOutsideToClose = true
            }
            if (!options.ariaLabel) {
                options.ariaLabel = 'Alert'
            }
            if (!options.title) {
                options.title = "Alert"
            }
            return this.dialog.show(this.dialog.alert(options))
        }
        showConfirm(options) {
            if (angular.isString(options)) {
                var text = angular.copy(options)
                options = {}
                options.textContent = text
                options.title = " "
            }
            if (!options.ok) {
                options.ok = "OK"
            }
            if (!options.cancel) {
                options.cancel = "Cancel"
            }
            if (!options.clickOutsideToClose) {
                options.clickOutsideToClose = false
            }
            if (!options.ariaLabel) {
                options.ariaLabel = 'Confirm'
            }
            if (!options.title) {
                options.title = "Confirm"
            }
            return this.dialog.show(this.dialog.confirm(options))
        }
        showToast(toastMessage, position) {
            if (!position) { position = 'top' }
            return this.toast.show(this.toast.simple()
                .content(toastMessage)
                .position(position)
                .action('OK'))
        }
        showYesNo(options) {
            options.ok = "Yes"
            options.cancel = "No"
            return this.showConfirm(options)
        }
        uc() {
            return this.showAlert({
                htmlContent: "<img src='img\underconstruction.jpg'>",
                ok: "OK",
                title: "Under Construction"
            })
        }

    }
    angular.module('NOTIFICATION', []).service("notification", notification)
})()

该服务似乎创建得很好,但是,当我从组件的控制器引用它时,它已被注入到服务方法内部 "this" 引用了该服务已被注入的控制器,而不是服务。在调试器中查看控制器时,似乎我为服务定义的所有方法实际上都已添加到控制器中。

在控制器中,我基本上是将一些控制器方法映射到服务的方法,如下所示:

function $onInit() {
        Object.assign(ctrl, {
            // Properties        
            title: "FTP Order Processing",
            menuComponent: "appMenu",
            reportsOn: false,
            userName: "",
            notification: notification,
            // working: false,
            // Methods
            closeSideNav: closeSideNav,
            menuHit: menuHit,
            openHelp: notification.openHelp,
            showConfirm: notification.showConfirm,
            showSideNav: showSideNav,
            showAlert: notification.showAlert,
            showToast: notification.showToast,
            showYesNo: notification.showYesNo,
            toggleReports: toggleReports,
            // uc: uc
        })

        Object.defineProperty(ctrl, "working", {
            get: () => { return ctrl.notification.working },
            set: (value) => { ctrl.notification.working = value }
        })
    }

所以 "this" 指的是控制器是有道理的。当我使用基于非 class 的服务时,这并不重要,因为我使用引用服务的变量来引用服务中的服务成员。

所以我想我的问题是,当这些方法已映射到另一个对象时,如何从其方法中引用服务 class 的成员?

这是我们使用 Angular 1.5+:

为我们的 类、ES6 做的事情
import { decorator } from 'app/decorators';
export default class FooClass {
  /* @ngInject */
  constructor(Restangular) {
    this._Restangular = Restangular;
  }

  @decorator
  someMethod(argument_one) {
    return argument_one.property;
  }
}

所以它和你的差不多,略有不同。为了以防万一,我留下了一个装饰器示例。

问题与 ES6 classes 无关(它们只是 ES5 构造函数的语法糖),但通常与 JS 相关。

当一个方法被分配给另一个对象时

foo.baz = bar.baz

foo.baz()foo 作为 this - 除非 bar.baz 被绑定为 bar.baz = bar.baz.bind(baz),或者它是具有 [=20 的 ES6 箭头函数=] 作为词法 this.

像这样将方法分配给 controller 不会很好地工作,并且会导致它们的上下文错误。

这可以像

一样修复
Object.assign(this, {
  ...
  showAlert: notification.showAlert.bind(notification)
});

Object.assign(this, {
  ...
  showAlert: (...args) => notification.showAlert(...args)
});

但最好的办法就是不要让服务方法失去上下文。

控制器应将服务实例分配为

this.notification = notification;

并访问它的方法,例如控制器中的 this.notification.openHelp() 或视图中的 {{ $ctrl.notification.openHelp() }}

否则最好使用 class 原型方法:

showAlert(options) {
  return this.notification.showAlert(options);
}

由于它公开了控制器原型上的方法,这允许使用实例方法不可用的继承和测试方法(当控制器被多次实例化时也更有效)。