【问题标题】:Instance Angular 2 Component Two times实例化 Angular 2 组件两次
【发布时间】:2016-04-21 11:17:02
【问题描述】:

我正在尝试学习 Angular 2,所以我制作了一些 hello world 示例。 这是我的代码:

boot.ts

import {bootstrap}    from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {DataService} from './app.dataservice'

bootstrap(AppComponent, [DataService]);

index.html

...
<body>
    <hello-world>Loading...</hello-world>
    <hello-world>Loading...</hello-world>
</body>
...

app.component.ts

import {Component} from 'angular2/core';
import {DataService} from './app.dataservice'

@Component({
    selector: 'hello-world',
    template: '<h1>Hello {{ item }}</h1>'
})

export class AppComponent {
    items: Array<number>;
    item: number;

    constructor(dataService: DataService) {
        this.items = dataService.getItems();
        this.item = this.items[0];
    }
}

app.dataservice.ts

export class DataService {
    items: Array<number>;

    constructor() {
        this.items = [1,2,3];
    }

    getItems() {
        return this.items;
    }
}

代码似乎可以正常工作,因为第一个 hello-world 自定义标记与 ts 中的代码一起正确显示。但是,第二个hello-world tag is not transformed。仅显示一个自定义元素。

不能超过 1 个自定义标签?我该怎么做?

编辑

我在 app.components.ts 中添加了新的导入

import {ByeWorld} from './app.byeworld';

app.byeworld.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'bye-world',
    template: '<h1>Bye World</h1>'
})

export class ByeWorld {
    constructor() {
    }
}

【问题讨论】:

  • 是的,但我想插入相同的自定义标签,两次
  • 这是你的主要组件吗?
  • @ShaohaoLin 我也遇到了与2.0.0-beta.1 版本相同的问题.. 看起来很奇怪。它在页面上一次引导应用程序..其他元素标签被忽略..
  • 我猜bootstrap() 只会引导它找到的第一个实例。也就是说,你不能在同一个应用程序中有两个根组件,这是有道理的,因为 Angular 构建了一个组件树,而树不能有两个根。
  • @Pablo 我认为我们的应用程序中可以有多个main-component,但它们应该有不同的名称......就像Angular1有能力有多个应用程序(规则是根组件不应该嵌套在彼此)

标签: javascript typescript angular


【解决方案1】:

我已经对此进行了测试。您不能制作多个具有相同名称的 Angular 2 主要组件。但是您可以为非主组件制作任意数量的组件。

如何区分主成分和非主成分?

主要组件是被引导的组件。

看一下屏幕截图。

我的主要组件称为: 我在 HTML 中做了两次:

<body>
  <my-app>Loading...</my-app>
  <my-app>Loading...</my-app>
</body>

如您所见,图片底部末尾有一个loading

但是,它适用于非主要组件。如您所见,my-hero-detail 组件可以创建尽可能多。

<div class="center-align">
<h1>{{title}}</h1>
</div>
<div class="row" style="margin-bottom: 0;">
    <div class="col s12 m6">
        <div id="my-heroes" class="card">
            <div class="card-header">
                <span>My Heroes</span>
            </div>
            <div class="card-content">
                <ul class="heroes">
                    <li *ngFor="#hero of heroes" 
                        (click)="onSelect(hero)"
                        [class.selected]="hero === selectedHero">
                        <span class="badge">{{hero.id}}</span> {{hero.name}}
                    </li>
                </ul>   
            </div>
        </div>      
    </div>
    <my-hero-detail [hero]="selectedHero"></my-hero-detail>
</div>
<div class="row">
    <my-hero-detail [hero]="selectedHero"></my-hero-detail>
    <my-hero-detail [hero]="selectedHero"></my-hero-detail>
</div>

我的英雄详细信息组件:

import {Component} from 'angular2/core';
import {Hero} from '../hero';
@Component({
    selector: 'my-hero-detail',
    templateUrl: 'app/hero-detail/hero-detail.html',
    inputs: ['hero'],
})

export class HeroDetailComponent {
    public hero: Hero;
}

【讨论】:

  • 如何区分主成分和非主成分?被引导的组件是主要组件。对吗?
  • 你能提供一个你的my-hero-detail组件的例子吗?
  • @Pankaj Parkar 是的,主要组件是一个被引导的组件
  • @PankajParkar 真的吗?你能提供例子吗?相同的根组件还是引导两个不同的组件作为主组件?
  • 分别引导两个组件作为主要组件。他们没有任何层次结构。
【解决方案2】:

正如标准的 HTML 页面应该有一个 &lt;body&gt; 标记用于内容和一个 &lt;head&gt; 标记用于“元数据”,Angular2 应用程序应该有一个根标记。要使应用程序工作,您必须初始化它(告诉 Angular 它是一个应用程序),然后通过调用 bootstrap() 函数来完成。

如果您的根标签(例如&lt;app&gt;)位于正文内,您可以将选择器从自定义标签app 更改为标准标签body。如果你添加不同的组件作为根,像这样:

import {bootstrap} from 'angular2/platform/browser'
import {Component} from 'angular2/core';
import {AppComponent} from './app.component'
import {DataService} from './app.dataservice'

@Component({
  selector: 'body',
  directives: [AppComponent],
  template: `
    <hello-world>Loading...</hello-world>
    <hello-world>Loading...</hello-world>
  `
})
class RootComponent {}

bootstrap(RootComponent, [DataService]);

...您的其余代码应该可以工作。

当然,如果您的 HTML 中需要其他内容(非应用内容或其他 Angular 应用),您不会选择 body 作为 Angular2 应用的根选择器。

希望这可以帮助您更好地理解事物...

【讨论】:

  • 我明白,但是在根标签内,我可以添加新的自定义标签吗?比如 我可以渲染自定义标签 'tag ' 在根元素内两次?我该怎么做?
  • 应用程序初始化/引导后,您可以在其中使用您的组件多少次。 Angular 每次找到您指定的选择器时都会创建组件的实例(不一定是标签 - 可以是类、id 或任何其他选择器)。在您的示例 AppComponent 中有selector: 'hello-world',因此将为每个&lt;hello-world&gt; 标签创建一个实例...
  • 是的,但是请检查我编辑的问题的最后一部分,我创建了一个新的自定义标签,它没有被引导,但是它没有显示。
  • 您需要在父组件模板中添加&lt;bye-world&gt;标签,导入它的类并作为依赖包含在父组件中...在您的AppComponent中selector: 'hello-world',下方添加directives: [ByeWorld]...跨度>
【解决方案3】:

如果您遇到这个问题并且确实想要两个根级应用实例,这可以通过在 NgModule ngDoBootstrap 方法中手动引导您的根级组件来完成。

(请注意,在 Angular 5+ 中可能不再需要此方法,请参阅 this Angular PR

我们首先找到我们想要引导的所有根元素并给它们一个唯一的 ID。然后对于每个实例,使用新 ID 破解组件工厂选择器并触发引导程序。

const entryComponents = [
  RootComponent,
];

@NgModule({
  entryComponents,
  imports: [
    BrowserModule,
  ],
  declarations: [
    RootComponent,
  ],
})
export class MyModule {
  constructor(private resolver: ComponentFactoryResolver) {}

  ngDoBootstrap(appRef: ApplicationRef) {
    entryComponents.forEach((component: any) => {
      const factory = this.resolver.resolveComponentFactory(component);
      let selectorName;
      let elements;

      // if selector is a class
      if (factory.selector.startsWith('.')) {
        selectorName = factory.selector.replace(/^\./, '');
        elements = document.getElementsByClassName(selectorName);

      // else assume selector is an element
      } else {
        selectorName = factory.selector;
        elements = document.getElementsByTagName(selectorName);
      }

      // no elements found, early return
      if (elements.length === 0) {
        return;
      }

      // more than one root level componenet found, bootstrap unique instances
      if (elements.length > 1) {
        const originalSelector = factory.selector;

        for (let i = 0; i < elements.length; i += 1) {
          elements[i].id = selectorName + '_' + i;
          (<any>factory).factory.selector = '#' + elements[i].id;
          appRef.bootstrap(factory);
        }

        (<any>factory).factory.selector = originalSelector;

      // only a single root level component found, bootstrap as usual
      } else {
        appRef.bootstrap(factory);
      }
    });
  }
}

现在,假设我们的 RootComponent 的选择器是“.angular-micro-app”,这将按预期工作:

<body>
    <div class="angular-micro-app"></div>
    ...
    <div class="angular-micro-app"></div>
</body>

【讨论】:

  • 我的 Angular 应用程序中有单例外观,所以如果我多次加载根组件,它们会共享状态吗?我希望他们不要分享和保持分开。谢谢。
猜你喜欢
  • 2020-03-03
  • 2016-07-15
  • 2016-08-04
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 2018-11-16
  • 2013-06-08
相关资源
最近更新 更多