基类中 $mdToast 的单个实例/注入
Posted
技术标签:
【中文标题】基类中 $mdToast 的单个实例/注入【英文标题】:single instance/injection of $mdToast in a base class 【发布时间】:2017-02-11 16:31:30 【问题描述】:我有一个关于将单个 $mdToast 实例(来自 Angular Material)放入基类(Typescript)的具体问题。我的 UI 中有五个选项卡,并为每个选项卡提供了一个单独的控制器实例(即,单独的注入和 ctor 声明)。将 $mdToast 声明移动到基类中而不是在任何地方单独声明它是有意义的。你会看到基类有它自己的“$inject”,但显然它被派生类上的那个取代了。我只是想找出将 $mdToast 移动到公共基类的最干净的方法。什么是最好的方法?这是我的代码目前的样子。
注意原来的 $mdToast 行被注释掉了:
export class MainController extends BaseController
static $inject = [
'tableService',
'$mdSidenav',
//'$mdToast',
'$mdDialog',
'$mdMedia',
'$mdBottomSheet'];
constructor(
private tableService: ITableService,
private $mdSidenav: angular.material.ISidenavService,
//private $mdToast: angular.material.IToastService,
private $mdDialog: angular.material.IDialogService,
private $mdMedia: angular.material.IMedia,
private $mdBottomSheet: angular.material.IBottomSheetService)
super();
var self = this;
具有以下基类。注意 $mdToast 的注入和 $mdToast 在构造函数之外的声明。
export class BaseController
static $inject = [
'$mdToast'];
constructor(
)
var self = this;
private $mdToast: angular.material.IToastService;
openToast(message: string): void
this.$mdToast.show(
this.$mdToast.simple()
.textContent(message)
.position('top right')
.hideDelay(3000)
);
我在 SO 的其他地方看到了 $injector 的巧妙使用,但它对我不起作用。收到所有回复都很高兴!
【问题讨论】:
【参考方案1】:当然,这种方法是一种 hack,但它会完成工作是一种非常枯燥的方法。利用 ES6 的导入/导出语句,在您需要的地方提供该服务。
export let $injector;
class injectorConfig
static $inject = ['$injector'];
constructor (private $originalInjector)
$injector = $originalInjector;
app.config(injectorConfig);
那么您的 BaseController.ts 文件将如下所示
import $injector from '../yourfilename';
export class BaseController
private $mdToast = $injector.get('$mdToast');
constructor( )
var self = this;
仅仅因为它有效并不意味着你应该依赖它,但在这种情况下,我认为这种技术是有意义的。请记住,在 Angular 运行此配置块之前,$injector 将不可用,因此您不能在提供程序或在配置块运行之前运行的任何其他代码中使用它。
【讨论】:
这看起来像是一种确保 $mdToast 的单例值的干净方法。谢谢!【参考方案2】:可能是这样的一种模式:
export class BaseController
static $inject = [...];
...
export class MainController extends BaseController
static $inject = [...BaseController.$inject,
...
];
constructor(...deps)
const superDeps = BaseController.$inject.map((dep, i) => deps[i]);
super(...superDeps);
const thisDeps = deps.slice(superDeps.length);
const thisDepNames = this.constructor.$inject.slice(superDeps.length);
...
...
如果使用不止一次或两次,它可能会被打包到一些用于注入或装饰器的基类中,但它总是关于解析两个数组$inject
和deps
,并将依赖项分配给@987654324 @。
此方法不是类型安全的。
对于 TypeScript,最好保持 WET 但类型安全。为了保持一致性,我们总是希望父类的依赖关系优先:
export class BaseController
static $inject = ['$mdToast'];
constructor(protected $mdToast: angular.material.IToastService) ...
export class MainController extends BaseController
static $inject = [
'$mdToast'
'tableService',
...
];
constructor(
$mdToast: angular.material.IToastService,
private tableService: ITableService,
...
)
super($mdToast);
...
【讨论】:
你的陈述中的有趣智慧。你给了我很多考虑。第二个示例仍然受到我试图避免的两次注入 $mdToast 的影响。如何确保在从 BaseController 继承的所有类中仅获得一个 $mdToast 实例? (顺便说一句,我赞成) Dupe$mdToast
在这里是正确的。无论如何,它都会被注入器作为构造函数参数传递(这可以用类装饰器修复,但老实说这是不值得的)。但它应该只分配给this
一次,并且它不能覆盖可见性(请注意,父类构造函数中有protected $mdToast
,但子类构造函数中只有$mdToast
)。当然,$mdToast
只会在每个控制器实例中注入一次,无论该类是否被继承,这就是继承的工作原理。
在繁忙的周末和飓风马修的所有难民中,我决定实施您的更改,它们完全按照承诺工作。我仍然讨厌必须是 WET,但你关于它是保持 TypeScript 类型安全的最佳方式的评论引起了我的共鸣。我来自核心 C++/Java/C# 背景,在 javascript/TypeScript 中以这种方式思考是一个好地方,我需要比过去更灵活。
当然,DI 可以通过使用装饰器和基类处理 $inject 数组和构造函数参数来非常简洁(例如,参见 goo.gl/ubLAqy),这对 ES* 和 Babel 非常有用,但它是类型TS 中的不安全(可能是可以容忍的,取决于从 DRY/WET 权衡中获得多少)。这个问题没有显示任何 WET 足以放弃类型安全,但也许你的实际情况不同。以上是关于基类中 $mdToast 的单个实例/注入的主要内容,如果未能解决你的问题,请参考以下文章
如何在构造函数中注入 IEnumerable<ICustomRepository>,以便我可以决定在基类中使用哪一个?
Typescript - 从基类中的静态方法实例化子类,使用 args 并引用其他静态方法