web-dev-qa-db-fra.com

Instance Angular 2 Component Deux fois

J'essaie d'apprendre Angular 2, donc je faisais quelques exemples hello world . Voici mon code:

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;
    }
}

Le code semble fonctionner correctement car la première balise personnalisée hello-world est correctement affichée avec le code contenu dans la ts. Cependant, le second hello-world tag is not transformed. Un seul élément personnalisé est affiché.

Ne peut pas être plus de 1 balise personnalisée? Comment puis je faire ça?

MODIFIER

J'ai ajouté la nouvelle importation dans app.components.ts

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

et dans app.byeworld.ts

import {Component} from 'angular2/core';

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

export class ByeWorld {
    constructor() {
    }
}
14
Pablo

Tout comme une page HTML standard devrait avoir une balise <body> pour le contenu et une balise <head> pour 'métadonnées', une application Angular2 devrait avoir une balise racine. Pour que l'application fonctionne, vous devez l'initialiser (indiquez à Angular que c'est une application) et vous le faites en appelant la fonction bootstrap()

Si le fait que votre balise racine (par exemple <app>) se trouve à l'intérieur du corps vous dérange, vous pouvez changer le sélecteur de balise personnalisée app à balise standard body. Si vous ajoutez un composant différent en tant que root, procédez comme suit:

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]);

... le reste de votre code devrait fonctionner. 

Bien sûr, si dans votre code HTML vous avez besoin d'autres éléments (contenu non-applicatif ou autres applications angulaires), vous ne sélectionnez pas body comme sélecteur de racine pour votre application Angular2.

J'espère que cela vous aide à mieux comprendre les choses ...

4
Sasxa

J'ai testé cela. Vous ne pouvez pas créer plus de un Composant principal angulaire 2 portant le même nom. Mais vous pouvez en créer autant que vous le souhaitez pour les composants non principaux.

Comment les composants principal et non principal peuvent-ils être différenciés?

Le composant principal est celui qui est démarré.

Regardez la capture d'écran .  enter image description here

Mon composant principal appelé: Ce que je l'ai fait deux fois dans le HTML:

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

Comme vous pouvez le constater, il y a une loading au bas de la photo.

Cependant, cela fonctionne pour les composants non principaux. Comme vous pouvez le constater, les composants my-hero-detail peuvent être créés autant que je peux.

<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>

Composant Mon détail de héros:

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;
}
9
Shaohao Lin

Si vous rencontrez cette question et souhaitez réellement deux instances d'application au niveau racine, vous pouvez le faire en démarrant manuellement le ou les composants de niveau racine dans la méthode NgModule ngDoBootstrap.

(Notez que dans Angular 5+, cette méthode peut ne plus être nécessaire, voir ce PR angulaire )

Nous trouvons d’abord tous les éléments racines que nous voulons booter et leur attribuons un identifiant unique. Puis, pour chaque instance, piratez le sélecteur d’usine de composants avec le nouvel ID et déclenchez le bootstrap.

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);
      }
    });
  }
}

Maintenant, en supposant que le sélecteur de notre RootComponent soit '.angular-micro-app', cela fonctionnera comme prévu:

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