父/子组件通信角度2 [英] Parent / Child component communication angular 2

查看:40
本文介绍了父/子组件通信角度2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我未能在 child_1 组件中实现操作按钮,但事件处理程序在子子组件 child_2 中,如以下代码所示:

app.component.html(父 Html)

<h1>欢迎来到{{title}}!<app-navigation></app-navigation><!-- Child1-->

app.component.html(父组件)

import { Component } from '@angular/core';从'./productservice'导入{产品服务};从'./product'导入{产品};@成分({选择器:'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'],})导出类 AppComponent {title = 'MobileShirtShoeApp';}

app.module.ts(主模块)

import { BrowserModule } from '@angular/platform-b​​rowser';从'@angular/core' 导入 { NgModule };从'@angular/http'导入{HttpModule};从'./product'导入{产品};从'./productservice'导入{产品服务};从 './app.component' 导入 { AppComponent };从 './navigation/navigation.component' 导入 { NavigationComponent };import { DataTemplateComponent } from './data-template/data-template.component';@NgModule({声明:[AppComponent,NavigationComponent,DataTemplateComponent],进口:[BrowserModule,HttpModule],提供者:[产品服务],引导程序:[AppComponent]})导出类 AppModule { }

navigation.component.html(子 1 HTML)

<legend>导航</legend><div><button (click)="loadMobiles()">Mobiles</button><!--Child_1 动作-->

<app-data-template></app-data-template></fieldset>

navigation.component.ts(子 1 Component.ts)

import { Component, OnInit } from '@angular/core';从'../productservice'导入{产品服务};从'../product'导入{产品};从'../data-template/data-template.component'导入{DataTemplateComponent};@成分({选择器:'应用程序导航',templateUrl: './navigation.component.html',styleUrls: ['./navigation.component.css']})导出类 NavigationComponent 实现 OnInit {错误:字符串;产品数组:产品[];构造函数(私有 myService:ProductService){this.myService = myService;}dataTemplateComponent: DataTemplateComponent = new DataTemplateComponent(this.myService);ngOnInit() {}加载移动(){返回 this.dataTemplateComponent.loadMobiles();}}

data-template.component.html(Child 2 HTML)(不显示数据)

<legend>请求的数据</legend>欢迎<div><ul><li *ngFor="let product of productArray">{{product.id}} {{product.name}} {{product.price}}<img src="{{product.url}}">

</fieldset>

data-template.component.ts(Child 2 Component)(包含产品服务调用代码)

import { Component} from '@angular/core';从'../product'导入{产品};从'../productservice'导入{ProductService};@成分({选择器:'应用程序数据模板',templateUrl: './data-template.component.html',styleUrls: ['./data-template.component.css']})导出类 DataTemplateComponent {错误:字符串;产品数组:产品[];构造函数(私有产品服务:产品服务){this.productService = productService;}加载移动(){让 promise = this.productService.fetchMobiles();promise.then(productArr => {返回 this.productArray = productArr;}).catch((错误) => {this.error = 错误;});}}

ProductService.ts

import 'rxjs/add/operator/toPromise';从 '@angular/http' 导入 {Http, HttpModule};从@angular/core"导入 {Injectable};从'./product'导入{产品};@Injectable()导出类 ProductService{http:http;构造函数(http:Http){this.http = http;控制台日志(http);}fetchMobiles(): Promise{让 url = "https://raw.githubusercontent.com/xxxxx/Other/master/JsonData/MobileData.json";返回 this.http.get(url).toPromise().then((response) => {返回 response.json().mobiles 作为 Product[];}).catch(this.handleError);}private handleError(error: any): Promise{console.error('发生错误', error);返回 Promise.reject(error.message || error);}}

抱歉,如果代码打扰您.所以基本上我无法在 child_1.html 中执行操作时在 child_2.html 中显示服务数据.服务工作正常,名称是 ProductService,它使用 Product.ts 作为对象以获取 JSON 格式的数据.任何形式的帮助表示赞赏.

解决方案

这不起作用,因为您在应用程序导航中实例化的 DataTemplateComponent 与页面上的 DataTemplateComponent 实例不同.它是您实例化的一个全新的,完全没有绑定到页面.您要实现的是组件通信.具体来说,父/子组件通信.有多种方法可以做到这一点,最简洁、最灵活/可扩展的方法是使用共享服务模式.基本上,您声明了一个服务,其中包含一个可观察对象,您将其注入到两个服务中,一个更新可观察对象,而另一个订阅它,如下所示:

@Inject()导出类 MyComponentCommunicationService {private commSubject: 主题<任何>= 新主题();comm$: Observable= this.commSubject.asObservable();通知() {this.commSubject.next();}}

然后根据需要在应用模块或可能在父组件中提供此服务,然后在应用导航中:

constructor(private commService: MyComponentCommunicationService) {}加载移动(){this.commservice.notify();}

并在数据模板中:

constructor(private commService: MyComponentCommunicationService, private productService: ProductService) {}ngOnInit() {this.commSub = this.commService.comm$.subscribe(e => this.loadMobiles());}ngOnDestroy() { this.commSub.unsubscribe();}//总是清理订阅

这可能有点不必要,因为您已经在那里获得了产品服务.您可能只需将 load mobiles 逻辑移动到产品服务中并触发一个可观察到的数据模板服务订阅,并让导航组件调用产品服务上的 load mobile 方法,但这只是为了说明概念.

我可能会这样做:

@Inject()出口类产品服务{私人产品主题:主题<产品[]>= 新主题<产品[]>();products$: Observable= this.productSubject.asObservable();加载移动(){this.fetchMobiles().then(productArr => {this.productSubject.next(productArr);}).catch((错误) => {this.productSubject.error(err);});}}

然后导航组件:

loadMobiles() {this.myService.loadMobiles();}

然后是数据模板:

ngOnInit() {this.productSub = this.productService.products$.subscribe(产品 =>this.productArray = 产品,错误 =>this.error = 错误);}ngOnDestroy() { this.productSub.unsubscribe();}//总是清理订阅

I am failing to implement action button in child_1 component but the event handler is in sub child component child_2 as shown in the following code:

app.component.html (Parent Html)

<div style="text-align:center">
  <h1>
    Welcome to {{title}}!
  </h1>
  <app-navigation></app-navigation> <!-- Child1-->
</div>

app.component.html (Parent Component)

import { Component } from '@angular/core';
import { ProductService } from './productservice';
import {Product} from './product';

@Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
      title = 'MobileShirtShoeApp';
    }

app.module.ts (Main Module)

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { Product } from './product';
import { ProductService } from './productservice';
import { AppComponent } from './app.component';
import { NavigationComponent } from './navigation/navigation.component';
import { DataTemplateComponent } from './data-template/data-template.component';

@NgModule({
  declarations: [AppComponent,NavigationComponent,DataTemplateComponent],
  imports: [BrowserModule,HttpModule],
  providers: [ProductService],
  bootstrap: [AppComponent]
})

export class AppModule { }

navigation.component.html (Child 1 HTML)

<fieldset>
  <legend>Navigate</legend>
  <div>
     <button  (click)="loadMobiles()">Mobiles</button> <!--Child_1 Action-->
  </div>
  <app-data-template></app-data-template>
</fieldset>

navigation.component.ts (Child 1 Component.ts)

import { Component, OnInit } from '@angular/core';
import { ProductService } from '../productservice';
import {Product} from '../product';
import {DataTemplateComponent} from '../data-template/data-template.component';


@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
  error: string;
  productArray: Product[];

  constructor(private myService: ProductService){
    this.myService = myService;
  }

  dataTemplateComponent: DataTemplateComponent = new DataTemplateComponent(this.myService);

  ngOnInit() {
  }

  loadMobiles() {
   return this.dataTemplateComponent.loadMobiles();
  }
}

data-template.component.html (Child 2 HTML) (NOT DISPLAYING DATA)

<fieldset>
    <legend>Requested Data</legend>
    Welcome
    <div>
      <ul>
            <li *ngFor="let product of productArray">
                {{product.id}} {{product.name}} {{product.price}}
                <img src="{{product.url}}">
            </li>
        </ul>
    </div>
</fieldset>

data-template.component.ts (Child 2 Component) (Contains Product service calling code)

import { Component} from '@angular/core';
import {Product} from '../product';
import {ProductService} from '../productservice';

@Component({
  selector: 'app-data-template',
  templateUrl: './data-template.component.html',
  styleUrls: ['./data-template.component.css']
})

export class DataTemplateComponent  {
  error: string;
  productArray: Product[];
  constructor(private productService: ProductService) {
    this.productService = productService;
  }

  loadMobiles(){
    let promise  = this.productService.fetchMobiles();
    promise.then(productArr => {
        return this.productArray = productArr;
      }).catch((err) => {
       this.error = err;
      });
  }
}

ProductService.ts

import 'rxjs/add/operator/toPromise';
import {Http, HttpModule} from '@angular/http';
import {Injectable} from '@angular/core';
import {Product} from './product';


@Injectable()
export class ProductService{
  http: Http;
  constructor(http: Http){
  this.http = http;
  console.log(http);
 }

fetchMobiles(): Promise<Product[]>{
  let url = "https://raw.githubusercontent.com/xxxxx/Other/master/JsonData/MobileData.json";
  return this.http.get(url).toPromise().then((response) => {
        return response.json().mobiles as Product[];
    }).catch(this.handleError);
}

private handleError(error: any): Promise<any> {
  console.error('An error occurred', error);
  return Promise.reject(error.message || error);
}
}

Sorry if the code bothers you. So basically i am failing to display service data in child_2.html when an action made in child_1.html.The service working fine and name is ProductService which uses Product.ts as an object to get the data in JSON format. Any kind of help is appreciated.

解决方案

This doesn't work because the DataTemplateComponent you're instantiating in app-navigation isn't the same instance of DataTemplateComponent as the one on the page. It's a brand new one that you instantiated and that isn't bound to the page at all. What you're trying to achieve is component communication. Specifically, parent / child component communication. There are a number of ways to do this, the cleanest and most flexible / extensible way is with a shared service pattern. Basically, you declare a service with an observable in it that you inject into both services and one updates the observable while the other is subscribed to it, like this:

@Inject()
export class MyComponentCommunicationService {
    private commSubject: Subject<any> = new Subject();
    comm$: Observable<any> = this.commSubject.asObservable();
    notify() {
        this.commSubject.next();
    }
}

Then provide this service, either at the app module or possibly at the parent component depending on needs then in app navigation:

constructor(private commService: MyComponentCommunicationService) {}
loadMobiles() {
   this.commservice.notify();
}

and in data template:

constructor(private commService: MyComponentCommunicationService, private productService: ProductService) {}
ngOnInit() {
    this.commSub = this.commService.comm$.subscribe(e => this.loadMobiles());
}
ngOnDestroy() { this.commSub.unsubscribe(); } // always clean subscriptions

This is probably a little unneccessary since you already have the product service there. You could probably just move the load mobiles logic into the product service and have that trigger an observable that the data template service is subscribed to, and have the nav component call the load mobile method on the product service, but this is just meant to illustrate the concept.

I'd probably do it like this:

@Inject()
export class ProductService {
    private productSubject: Subject<Product[]> = new Subject<Product[]>();
    products$: Observable<Product[]> = this.productSubject.asObservable();
    loadMobiles() {
        this.fetchMobiles().then(productArr => {
            this.productSubject.next(productArr);
          }).catch((err) => {
            this.productSubject.error(err);
          });
    }
}

then nav component:

loadMobiles() {
   this.myService.loadMobiles();
}

then data template:

ngOnInit() {
    this.productSub = this.productService.products$.subscribe(
        products => this.productArray = products,
        err => this.error = err
    );
}
ngOnDestroy() { this.productSub.unsubscribe(); } // always clean subscriptions

这篇关于父/子组件通信角度2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆