Angular 2'deki bileşenler arasında nasıl veri paylaşırım?


84

Angular 1.xx'de sadece aynı hizmeti istersiniz ve aynı durumla sonuçlanır, bu da hizmetteki verileri paylaşmayı mümkün kılar.

Şimdi Angular 2'de hizmetime referansı olan bir bileşene sahibim. Servisteki verileri okuyabilir ve değiştirebilirim ki bu iyidir. Aynı hizmeti başka bir bileşene enjekte etmeye çalıştığımda, yeni bir örnek alıyormuşum gibi görünüyor.

Neyi yanlış yapıyorum? Hatalı olan modelin kendisi mi (verileri paylaşmak için bir hizmet kullanmak) yoksa hizmeti tekil olarak mı (uygulamanın bir örneğinde) veya başka bir şey olarak işaretlemem mi gerekiyor?

Hattayım 2.0.0-alpha.27/ btw

Ek açıklamadaki appInjector(edit: now providers) aracılığıyla bir hizmet enjekte ediyorum @Componentve ardından yapıcıya bir referans kaydediyorum. Bileşende yerel olarak çalışır - düşündüğüm gibi bileşenler arasında değil (aynı hizmet örneğini paylaşmazlar).

GÜNCELLEME : Angular 2.0.0'dan itibaren artık @ngModule var ve burada belirtilen providersözellik altında hizmeti tanımlayacaksınız @ngModule. Bu, söz konusu hizmetin aynı örneğinin o modüldeki her bileşene, hizmete vb. Aktarılmasını sağlayacaktır. https://angular.io/docs/ts/latest/guide/ngmodule.html#providers

GÜNCELLEME : Genel olarak Angular ve FE geliştirmesine çok şey oldu. @Noririco'nun bahsettiği gibi, NgRx gibi bir durum yönetim sistemi de kullanabilirsiniz: https://ngrx.io/


Açısal bileşenler arasında veri paylaşımı için altı yöntem: - angulartutorial.net/2017/12/…
Prashobh

Buraya gelirseniz, lütfen bir STATE Yönetim sistemi kullanmayı düşünün
noririco

Yanıtlar:


63

Servis teklisi güzel bir çözümdür. Diğer yol - data/events bindings.

İşte her ikisinin bir örneği:

class BazService{
  n: number = 0;
  inc(){
    this.n++;
  }
}

@Component({
  selector: 'foo'
})
@View({
  template: `<button (click)="foobaz.inc()">Foo {{ foobaz.n }}</button>`
})
class FooComponent{
  constructor(foobaz: BazService){
    this.foobaz = foobaz;
  }
}

@Component({
  selector: 'bar',
  properties: ['prop']
})
@View({
  template: `<button (click)="barbaz.inc()">Bar {{ barbaz.n }}, Foo {{ prop.foobaz.n }}</button>`
})
class BarComponent{
  constructor(barbaz: BazService){
    this.barbaz = barbaz;
  }
}

@Component({
    selector: 'app',
    viewInjector: [BazService]
})
@View({
  template: `
    <foo #f></foo>
    <bar [prop]="f"></bar>
  `,
  directives: [FooComponent, BarComponent]
})
class AppComponent{}

bootstrap(AppComponent);

Canlı izle


20
Bunu anladım. Yalnızca bir hizmet örneğini enjekte edersiniz - "uygulama" içinde. Aynı örnek, parametreyi alt yapıcılara eklerken otomatik olarak miras alınır :) Yeni örnekler oluşturan alt bileşenlere başka bir appInjector ekleme hatası yaptım.
Hornshøj-Schierbeck başına

1
@AlexanderCrush cevabınızı güncelleyebilir misiniz? Daha sonraki alfa sürümlerinden beri (alfa 30+) appInjector kaldırıldı . Şimdilik doğru cevap kullanmak olmalı viewInjector.
Eric Martinez

1
@EricMartinez teşekkürler, cevap ve plunker güncellendi.
Alexander Ermolov

1
Bunun neden ve nasıl çalıştığını anlamak için ilginç bir kaynak: blog.thoughtram.io/angular/2015/08/20/… .
soyuka

2
Aynı sorunu yaşıyordum çünkü Hizmeti ana uygulamada ve ayrıca kullanarak bileşenin kendisine enjekte ediyordum providers: [MyService]. Sağlayıcıları kaldırarak, uygulamanın tek örneği oldu
maufarinelli

43

@ Maufarinelli'nin yorumu kendi cevabını hak ediyor çünkü ben onu görene kadar @Alexander Ermolov'un cevabına rağmen bu konuyla hala kafamı duvara vuruyordum.

Sorun olduğunu bir eklerken providersadresinden Müşteri component:

@Component({
    selector: 'my-selector',
    providers: [MyService],
    template: `<div>stuff</div>`
})

Bu, servisinizin yeni bir örneğinin bir tekil olmak yerine enjekte edilmesine neden olur .

Öyleyse providers: [MyService]uygulamanızdaki tüm örneklerini kaldırın moduleve işe yarayacaktır!


2
Sadece bir yorum, asla bir tekil değildir - sadece etrafta dolaşan aynı örnek. Yine de yeni bir
mevki

10

Bir @Component dekoratörünün giriş ve çıkışlarını kullanmalısınız. İşte her ikisini de kullanmanın en temel örneği;

import { bootstrap } from 'angular2/platform/browser';
import { Component, EventEmitter } from 'angular2/core';
import { NgFor } from 'angular2/common';

@Component({
  selector: 'sub-component',
  inputs: ['items'],
  outputs: ['onItemSelected'],
  directives: [NgFor],
  template: `
    <div class="item" *ngFor="#item of items; #i = index">
      <span>{{ item }}</span>
      <button type="button" (click)="select(i)">Select</button>
    </div>
  `
})

class SubComponent {
  onItemSelected: EventEmitter<string>;
  items: string[];

  constructor() {
    this.onItemSelected = new EventEmitter();
  }

  select(i) {
    this.onItemSelected.emit(this.items[i]);
  }
}

@Component({
  selector: 'app',
  directives: [SubComponent],
  template: `
    <div>
      <sub-component [items]="items" (onItemSelected)="itemSelected($event)">
      </sub-component>
    </div>
  `
})

class App {
  items: string[];

  constructor() {
    this.items = ['item1', 'item2', 'item3'];
  }

  itemSelected(item: string): void {
    console.log('Selected item:', item);
  }
}

bootstrap(App);

7
İçe ngFor
Richard Hamilton

7

Üst Bileşen şablonunda:

<hero-child [hero]="hero">
</hero-child>

Alt Bileşende:

@Input() hero: Hero;

Kaynak: https://angular.io/docs/ts/latest/cookbook/component-communication.html


Olabilir, ancak bunun daha fazla ayrıntıya ihtiyacı olacaktır. Gerçek dünyada bu o kadar kolay değil. Birden çok bileşen arasında paylaşmak ve verilere erişmek istediğiniz bir sınıfınız olduğunu düşünün. Bu çalışmıyor.
sancelot

Bu yaklaşımı, birçok bileşen arasında veri paylaşmak için büyük bir çözümde kullanıyorum. Birçok çocuğunuz olabilir ve her biri aynı nesneyi alır. İşe yaramadığını söylemeden önce bunu yapmaya çalıştın mı?
Alexis Gamarra

Evet yaptım . işe yarayacak .... ama bazı sorunları çözmek için bazı "hacklemeler" ile. Cevabınız kimsenin kullanmasına izin vermiyor.
sancelot

2

Birçok yolu var. Bu, üst ve alt öğeler arasında yayılmanın kullanıldığı bir örnektir. Bu çok etkilidir.

İki form içinde veri bağlamanın iki yolunun kullanımını görüntülemeye izin veren bir örnek gönderdim. Biri bir plunkr örneği sağlayabilirse bu çok iyi olur ;-)

Bir servis sağlayıcı kullanarak başka bir yol arayabilirsiniz. Referans olarak bu videoya da göz atabilirsiniz: ( Açısal Bileşenler arasında Veri Paylaşımı )

mymodel.ts (paylaşılacak veriler)

// Some data we want to share against multiple components ...
export class mymodel {
    public data1: number;
    public data2: number;
    constructor(
    ) {
        this.data1 = 8;
        this.data2 = 45;
    }
}

Unutmayın: "mymodel" i alt bileşenlerle paylaşacak bir üst öğe olmalıdır.

Üst bileşen

import { Component, OnInit } from '@angular/core';
import { mymodel } from './mymodel';
@Component({
    selector: 'app-view',
    template: '<!-- [model]="model" indicates you share model to the child component -->
        <app-mychild [model]="model" >
        </app-mychild>'

        <!-- I add another form component in my view,
         you will see two ways databinding is working :-) -->
        <app-mychild [model]="model" >
        </app-mychild>',
})

export class MainComponent implements OnInit {
    public model: mymodel;
    constructor() {
        this.model = new mymodel();
    }
    ngOnInit() {
    }
}

Alt bileşen, mychild.component.ts

import { Component, OnInit,Input } from '@angular/core';
import { FormsModule }   from '@angular/forms'; // <-- NgModel lives here
import { mymodel } from './mymodel';

@Component({
    selector: 'app-mychild',
    template: '
        <form #myForm="ngForm">
            <label>data1</label>
            <input type="number"  class="form-control" required id="data1 [(ngModel)]="model.data1" name="data1">
            <label>val {{model.data1}}</label>

            label>data2</label>
            <input  id="data2"  class="form-control" required [(ngModel)]="model.data2" name="data2" #data2="ngModel">
            <div [hidden]="data2.valid || data2.pristine"
                class="alert alert-danger">
                data2 is required
            </div>

            <label>val2 {{model.data2}}</label>
        </form>
    ',
})

export class MychildComponent implements OnInit {
    @Input() model: mymodel ;  // Here keywork @Input() is very important it indicates that model is an input for child component
    constructor() {
    }
    ngOnInit() {
    }
}

Not: Bazı ender durumlarda, HTML kodu ayrıştırılırken hata alabilirsiniz, çünkü sayfa başlatıldığında model kullanıma "hazır" değildir. Bu durumda, HTML kodunun önüne ngIf koşulu ekleyin:

<div *ngIf="model"> {{model.data1}} </div>

1

Basit bir durum olup olmadığına göre değişir

a) A -> B -> C A'da iki çocuk B ve C vardır ve verileri A ile B veya A ve C arasında paylaşmak istiyorsanız (giriş / çıkış)

B ve C arasında paylaşmak istiyorsanız, (giriş / çıkış) da kullanabilirsiniz, ancak Hizmeti kullanmanız önerilir.

b) Ağaç büyük ve karmaşıksa. (çok fazla düzeyde ebeveyn ve çocuk bağlantıları varsa.) Ve bu durumda, verileri paylaşmak istiyorsanız, ngrx

Herhangi bir bileşenin abone olabileceği ve herhangi bir yarış koşulu oluşturmadan güncelleme yapabileceği bir müşteri tarafı deposu oluşturan akış mimarisini uygular.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.