我正在尝试调试我在混合Angular / AngularJS应用程序中重新加载时收到的错误。您可以在stackblitz查看此示例。这种在Angular应用程序中引导AngularJS的方法是based on Victor Savkin work。
TypeError: Cannot read property 'xPosition' of undefined at new MatMenu (VM4642 material.umd.js:13096) at createClass (VM4479 core.umd.js:10963) at createDirectiveInstance (VM4479 core.umd.js:10788) at createViewNodes (VM4479 core.umd.js:12229) at callViewAction (VM4479 core.umd.js:12675) at execComponentViewsAction (VM4479 core.umd.js:12584) at createViewNodes (VM4479 core.umd.js:12256) at createRootView (VM4479 core.umd.js:12124) at callWithDebugContext (VM4479 core.umd.js:13507) at Object.debugCreateRootView [as createRootView] (VM4479 core.umd.js:12824) "<my-layout class="ng-scope" ng-version="4.3.6">"
通过单步执行代码,我可以看到xPosition
所在的对象是injected default options object。 MdMenu
取决于通过InjectionToken引用的此默认选项对象。但是在我的混合Angular / AngularJS应用程序中,重新加载页面时,此依赖关系将返回undefined。如果从另一个页面切换到该页面,则依赖性正确加载。
下面是dom结构的简化视图。请注意,ng1和ng2组件是隔行扫描的。上述错误是指顶级降级组件,而不是包含<md-menu>
的降级组件。
<ng2-root>
<router-outlet></router-outlet>
<div ui-view>
<downgraded-ng2-component>
<ng1-component>
<another-downgraded-ng2-component>
<button md-icon-button [mdMenuTriggerFor]"toolbarMenu"></button>
<md-menu #toolbarMenu="mdMenu">
<button md-menu-item>...</button>
</md-menu>
</another-downgraded-ng2-component>
</ng1-component>
</downgraded-ng2-component>
</div>
</ng2-root>
答案 0 :(得分:1)
这里的问题是,不能保证一个模块的提供者将在另一个模块的提供者之前被实例化,除非有一些明确指定Angular需要它们的东西。
在您的情况下,例如,AngularJSModule
(其构造函数引导应用程序的AngularJS部分)过渡依赖于MatMenu
MAT_MENU_DEFAULT_OPTIONS
注入。但是Angular并没有意识到这一点(因为它隐藏在AngularJS引导中)。
例如,如果您要从MAT_MENU_DEFAULT_OPTIONS
明确声明对AngularJSModule
的依赖性,那么一切都应该有效:
export class AngularJSModule {
constructor(@Inject(MAT_MENU_DEFAULT_OPTIONS) ignored: object, upgrade: UpgradeModule) {
...
}
}
注意:这仅用于说明目的。我实际上并不建议这样做。
虽然这种懒惰引导AngularJS的方法并不完全正式支持&#34;但是你应该能够通过确保引导AngularJS异步发生(为所有提供者提供时间 - 包括{ {1}} - 要初始化)。有几种方法可以实现这一目标。以下是几个例子:
从MAT_MENU_DEFAULT_OPTIONS
的构造函数异步调用upgrade.bootstrap()
:
AngularJSModule
从export class AngularJSModule {
constructor(upgrade: UpgradeModule) {
Promise.resolve().then(() => {
// Bootstrap AngularJS
// ...
});
}
}
的{{1}}生命周期挂钩中拨打upgrade.bootstrap()
。在这种情况下,我们需要确保只调用一次,因为EmptyComponent
可以多次实例化:
ngOnInit()
最后请注意,我们通常建议在主EmptyComponent
的{{1}}方法中调用let angularJsBootstrapped = false;
@Component({template: ''})
export class EmptyComponent implements OnInit {
constructor(private upgrade: UpgradeModule) {}
ngOnInit() {
// Ensure AngularJS is only bootstrapped once.
if (!angularJsBootstrapped) {
this.upgrade.bootstrap(document.body, [module.name]);
setUpLocationSync(this.upgrade);
angularJsBootstrapped = true;
}
}
}
。但是由于upgrade.bootstrap()
没有调用延迟加载的模块(因为它们在技术上不是自举的),所以你需要求助于上面显示的方法之一。