首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >停止ngAfterContentInit执行两次

停止ngAfterContentInit执行两次
EN

Stack Overflow用户
提问于 2017-06-29 18:12:11
回答 2查看 1.4K关注 0票数 5

我有点不明白为什么在这个场景中ngAfterContentInit要执行两次。我已经创建了一个精简版本的应用程序来重现这个bug。简而言之,我使用一个*contentItem来标记组件,然后由standard-layout组件获取这些组件进行呈现。一旦我遵循这个模式,demongAfterContentInit就会被执行两次。

我将演示应用程序放在github上,它将复制错误:https://github.com/jVaaS/stackoverflow/tree/master/ngaftercontentinit

否则,这里有一些重要的部分:

马车-飞镖:

代码语言:javascript
复制
@Component(
    selector: "buggy-app",
    template: """       
        <standard-layout>
            <demo *contentItem></demo>
        </standard-layout>
    """,
    directives: const [
        ContentItemDirective,
        StandardLayout,
        Demo
    ]
)
class BuggyApp implements AfterContentInit {

    @override
    ngAfterContentInit() {
        print(">>> ngAfterContentInit: BuggyApp");
    }
}

标准布局。省道:

代码语言:javascript
复制
////////////////////////////////////////////////////////////////////////////////
///
/// Standard Layout Component
/// <standard-layout></standard-layout>
///
@Component(
    selector: "standard-layout",
    template: """
        <div *ngFor="let item of contentItems ?? []">
            <template [ngTemplateOutlet]="item.template"></template>
        </div>
    """,
    directives: const [ROUTER_DIRECTIVES, ContentItem])
class StandardLayout implements AfterContentInit {

    @ContentChildren(ContentItemDirective)
    QueryList<ContentItemDirective> contentItems;

    @override
    ngAfterContentInit() {
        print(">>> ngAfterContentInit: StandardLayout");
    }

}

////////////////////////////////////////////////////////////////////////////////
///
/// Content Item Directive
/// *contentItem
///
@Directive(selector: '[contentItem]')
class ContentItemDirective implements AfterContentInit {
    final ViewContainerRef vcRef;
    final TemplateRef template;
    final ComponentResolver componentResolver;

    ContentItemDirective(this.vcRef, this.template, this.componentResolver);

    ComponentRef componentRef;

    @override
    ngAfterContentInit() async {
        final componentFactory =
        await componentResolver.resolveComponent(ContentItem);
        componentRef = vcRef.createComponent(componentFactory);
        (componentRef.instance as ContentItem)
            ..template = template;
    }
}

////////////////////////////////////////////////////////////////////////////////
///
/// Content Item Generator
///
@Component(
    selector: "content-item",
    host: const {
        '[class.content-item]': "true",
    },
    template: """
        <template [ngTemplateOutlet]="template"></template>
    """,
    directives: const [NgTemplateOutlet]
)
class ContentItem {
    TemplateRef template;
}
////////////////////////////////////////////////////////////////////////////////

最后是demo.dart

代码语言:javascript
复制
@Component(
    selector: "demo",
    template: "Hello World Once, but demo prints twice!")
class Demo implements AfterContentInit {

    @override
    ngAfterContentInit() {
        print(">>> ngAfterContentInit: Demo");
    }

}

main.dart中没有太多内容:

代码语言:javascript
复制
void main() {
    bootstrap(BuggyApp);
}

当我运行这个程序时,Hello World按预期打印一次:

但当你看航站楼时:

因此,demo组件只呈现一次,但是它的ngAfterContentInit被执行了两次,如果假设它只执行一次,就会造成破坏。

我尝试过添加一个恶意的解决方案,但似乎组件实际上被重新呈现了两次:

代码语言:javascript
复制
int counter = 0;

@override
ngAfterContentInit() {

    if (counter == 0) {
        print(">>> ngAfterContentInit: Demo");
        counter++;
    }

}

这是一个角度错误,还是有什么我可以做的,以防止这一点?

如果需要的话,pubspec.yaml

代码语言:javascript
复制
name: bugdemo
version: 0.0.0
description: Bug Demo
environment:
  sdk: '>=1.13.0 <2.0.0'
dependencies:
  angular2: 3.1.0
  browser: ^0.10.0
  http: any
  js: ^0.6.0
  dart_to_js_script_rewriter: ^1.0.1
transformers:
- angular2:
    platform_directives:
    - package:angular2/common.dart#COMMON_DIRECTIVES
    platform_pipes:
    - package:angular2/common.dart#COMMON_PIPES
    entry_points: web/main.dart
- dart_to_js_script_rewriter
- $dart2js:
    commandLineOptions: [--enable-experimental-mirrors]

index.html的良好措施:

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
    <title>Strange Bug</title>
</head>
<body>

    <buggy-app>
        Loading ...
    </buggy-app>

<script async src="main.dart" type="application/dart"></script>
<script async src="packages/browser/dart.js"></script>
</body>
</html>

更新

因此,有人建议,它可能只发生了两次在开发模式。我做了一个pub build并运行了index.html,它现在在普通Chrome中包含main.dart.js

它仍在执行两次,用ngAfterViewInit尝试过,而且也执行了两次。

同时记录了一个错误:https://github.com/dart-lang/angular/issues/478

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-05 15:33:25

这看起来不像是窃听器。

下面是为demo.darthttps://gist.github.com/matanlurey/f311d90053e36cc09c6e28f28ab2d4cd生成的代码

代码语言:javascript
复制
void detectChangesInternal() {
  bool firstCheck = identical(this.cdState, ChangeDetectorState.NeverChecked);
  final _ctx = ctx;
  if (!import8.AppViewUtils.throwOnChanges) {
    dbg(0, 0, 0);
    if (firstCheck) {
      _Demo_0_2.ngAfterContentInit();
    }
  }
  _compView_0.detectChanges();
}

我很怀疑,所以我把你的打印信息改成了hashCode

代码语言:javascript
复制
@override
ngAfterContentInit() {
  print(">>> ngAfterContentInit: Demo $hashCode");
}

然后我看到了以下情况:

ngAfterContentInit:演示516575718 ngAfterContentInit:演示57032191

这意味着两个不同的演示实例还活着,而不仅仅是一个发射了两次。然后,我将StackTrace.current添加到演示的构造函数中,以获得堆栈跟踪:

代码语言:javascript
复制
Demo() {
  print('Created $this:$hashCode');
  print(StackTrace.current);
}

并得到:

0 (package:bugdemo/buggy-app.template.dart:136:21) (软件包:bugdemo/demo.dart:11:20)1 ViewBuggyApp1.构建(package:angular2/src/core/linker/app_view.dart:180:12) 2 AppView.create (package:angular2/src/core/linker/app_view.dart:180:12)3 DebugAppView.create (package:angular2/src/debug/debug_app_view.dart:73:26) 4 TemplateRef.createEmbeddedView (package:angular2/src/core/linker/template_ref( (package:angular2/src/core/linker/view_container.dart:86:43) ) .dart:27:10) 5 ViewContainer.createEmbeddedView

这反过来又说您的组件是由BuggyApp的模板创建的:

代码语言:javascript
复制
<standard-layout>
  <demo *contentItem></demo>
</standard-layout>

再近一点。继续挖。

我注释掉了ContentItemDirective,并看到Demo现在只创建了一次。我想你完全没有预料到它是由于*contentItem而创建的,所以继续挖掘--并最终找到了答案。

StandardLayout组件创建模板:

代码语言:javascript
复制
<div *ngFor="let item of contentItems ?? []">
  <template [ngTemplateOutlet]="item.template"></template>
</div>

但你的ContentItemDirective也是通过ComponentResolver实现的。我想你不是有意这么做的。因此,我想弄清楚您想做什么,并通过删除在其中一个位置创建组件来解决您的问题。

票数 3
EN

Stack Overflow用户

发布于 2017-06-29 19:18:58

可能与In Angular2, why are there 2 times check for content and view after setTimeout?有关

你处于开发模式吗?控制台告诉您什么时候应用程序引导。在显影模式下,角度击穿changeDetection两次,从而可以检测出副作用。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44832253

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档