Làm cách nào để chia sẻ dữ liệu giữa các thành phần trong Angular 2?


84

Trong Angular 1.xx, bạn chỉ cần yêu cầu cùng một dịch vụ và bạn kết thúc với cùng một phiên bản, giúp bạn có thể chia sẻ dữ liệu trong dịch vụ.

Bây giờ trong Angular 2, tôi có một thành phần có tham chiếu đến dịch vụ của tôi. Tôi có thể đọc và sửa đổi dữ liệu trong dịch vụ, điều này thật tốt. Khi tôi cố gắng đưa cùng một dịch vụ vào một thành phần khác, có vẻ như tôi nhận được một phiên bản mới.

Tôi đang làm gì sai? Có phải chính mô hình đó là sai (sử dụng dịch vụ để chia sẻ dữ liệu) hay tôi cần đánh dấu dịch vụ là một đơn vị (trong một phiên bản của ứng dụng) hay điều gì đó?

Tôi trên 2.0.0-alpha.27/ btw

Tôi chèn một dịch vụ thông qua appInjector(chỉnh sửa: bây giờ providers) trong @Componentchú thích và sau đó lưu một tham chiếu trong hàm tạo. Nó hoạt động cục bộ trong thành phần - không phải giữa các thành phần (chúng không chia sẻ cùng một phiên bản dịch vụ) như tôi nghĩ.

CẬP NHẬT : Kể từ Angular 2.0.0, chúng tôi hiện có @ngModule, nơi bạn sẽ xác định dịch vụ theo thuộc providerstính trên @ngModule. Điều đó sẽ đảm bảo cùng một phiên bản của dịch vụ đó được chuyển đến từng thành phần, dịch vụ, v.v. trong mô-đun đó. https://angular.io/docs/ts/latest/guide/ngmodule.html#providers

CẬP NHẬT : Rất nhiều điều đã xảy ra đối với sự phát triển Angular và FE nói chung. Như @noririco đã đề cập, bạn cũng có thể sử dụng hệ thống quản lý nhà nước như NgRx: https://ngrx.io/


Sáu phương pháp để chia sẻ dữ liệu giữa các thành phần góc: - angulartutorial.net/2017/12/...
Prashobh

Nếu bạn nhận được ở đây xin vui lòng xem xét sử dụng một hệ thống quản lý nhà nước
noririco

Câu trả lời:


63

Một singleton dịch vụ là một giải pháp tốt. Mặt khác - data/events bindings.

Đây là một ví dụ của cả hai:

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

Xem trực tiếp


20
Tôi đã hiểu rồi. Bạn chỉ đưa vào một phiên bản dịch vụ - trong 'ứng dụng'. Phiên bản tương tự đó được kế thừa tự động khi thêm tham số vào các hàm tạo con :) Tôi đã nhầm lẫn khi thêm một appInjector khác vào các thành phần con tạo ra các phiên bản mới.
Per Hornshøj-Schierbeck

1
@AlexanderCrush, bạn có thể cập nhật câu trả lời của mình không? Vì trong các phiên bản alpha sau (alpha 30+) appInjector đã bị loại bỏ . Câu trả lời chính xác, bây giờ, nên được sử dụng viewInjector.
Eric Martinez

1
@EricMartinez cảm ơn, câu trả lời và plunker đã được cập nhật.
Alexander Ermolov

1
Tài nguyên thú vị để hiểu tại sao và cách thức hoạt động của điều này: blog.thoughtram.io/angular/2015/08/20/… .
đậu nành,

2
Tôi đang gặp vấn đề tương tự, vì tôi đã đưa Dịch vụ vào ứng dụng chính và cả trong chính thành phần đang sử dụng providers: [MyService]. Xóa các nhà cung cấp, nó trở thành trường hợp duy nhất của ứng dụng
maufarinelli 02/02

43

Nhận xét của @maufarinelli xứng đáng có câu trả lời cho riêng mình bởi vì cho đến khi tôi nhìn thấy nó, tôi vẫn đang đập đầu vào tường với vấn đề này ngay cả với câu trả lời của @Alexander Ermolov.

Vấn đề là khi bạn thêm một providersvào component:

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

Điều này khiến một phiên bản mới của dịch vụ của bạn được đưa vào ... chứ không phải là một phiên bản đơn lẻ .

Vì vậy, hãy xóa tất cả các trường hợp của bạn providers: [MyService]trong ứng dụng của bạn ngoại trừ trong modulevà nó sẽ hoạt động!


2
Chỉ là một nhận xét, nó không bao giờ là một đơn lẻ - nó chỉ là một ví dụ tương tự đang được chuyển xung quanh. Bạn vẫn có thể yêu cầu một phiên bản mới ...
Per Hornshøj-Schierbeck

10

Bạn phải sử dụng đầu vào và đầu ra của trình trang trí @Component. Đây là ví dụ cơ bản nhất của việc sử dụng cả hai;

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
Không cần để nhập khẩu ngFor,
Richard Hamilton

7

Trong mẫu Thành phần mẹ:

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

Trong Thành phần con:

@Input() hero: Hero;

Nguồn: https://angular.io/docs/ts/latest/cookbook/component-communication.html


Có thể được, nhưng điều này sẽ cần thêm chi tiết. Trong thế giới thực không dễ dàng như vậy. hãy tưởng tượng bạn có một lớp mà bạn muốn chia sẻ giữa nhiều thành phần và truy cập dữ liệu. điều đó không hoạt động.
sancelot, 19/07/2017

Tôi đang sử dụng cách tiếp cận này trong một giải pháp lớn để chia sẻ dữ liệu giữa nhiều thành phần. Bạn có thể có nhiều con và mỗi con nhận cùng một đối tượng. Bạn đã thử làm điều này trước khi nói rằng nó không hoạt động?
Alexis Gamarra,

Vâng, tôi đã làm. nó sẽ hoạt động .... nhưng với một số "hack" để giải quyết một số vấn đề. Câu trả lời của bạn không cho phép bất kỳ ai sử dụng nó.
sancelot,

2

Có rất nhiều cách. Đây là một ví dụ sử dụng sự lan truyền giữa các phần tử cha và con. Điều này rất hiệu quả.

Tôi đã gửi một ví dụ cho phép xem việc sử dụng hai cách liên kết dữ liệu trong hai biểu mẫu. Nếu ai đó có thể cung cấp một mẫu plunkr, điều này sẽ rất tốt ;-)

Bạn có thể tìm cách khác bằng cách sử dụng nhà cung cấp dịch vụ. Bạn cũng có thể xem video này để tham khảo: ( Chia sẻ dữ liệu giữa các thành phần trong Angular )

mymodel.ts (dữ liệu để chia sẻ)

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

Hãy nhớ: Phải có một phụ huynh sẽ chia sẻ "mymodel" cho các thành phần con.

Thành phần chính

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() {
    }
}

Thành phần con, 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() {
    }
}

Lưu ý: Trong một số trường hợp hiếm hoi, bạn có thể gặp lỗi khi mã HTML được phân tích cú pháp, vì mô hình chưa "sẵn sàng" để sử dụng khi khởi chạy trang. Trong trường hợp này, hãy đặt tiền tố mã HTML với điều kiện ngIf:

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

1

Nó phụ thuộc, nếu có một trường hợp đơn giản

a) A -> B -> C A có hai con B và C và nếu bạn muốn chia sẻ dữ liệu giữa A và B hoặc A và C thì hãy sử dụng (input / output)

Nếu bạn muốn chia sẻ giữa B và C thì bạn cũng có thể sử dụng (đầu vào / đầu ra) nhưng bạn nên sử dụng Dịch vụ.

b) Nếu cây to và phức tạp. (nếu có rất nhiều cấp độ kết nối cha mẹ và con cái.) Và trong trường hợp này nếu bạn muốn chia sẻ dữ liệu thì tôi đề xuất ngrx

Nó thực hiện kiến ​​trúc thông lượng tạo ra một cửa hàng phía máy khách mà bất kỳ thành phần nào cũng có thể đăng ký và có thể cập nhật mà không tạo ra bất kỳ điều kiện đua nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.