Angular 6在两个不相关的组件之间传递数据

时间:2018-07-19 13:24:41

标签: angular typescript routing observable

我有一个课程详细信息组件,其中包含来自后端应用程序的数据(名为课程),我想将该数据传递给与该组件无关的另一个组件(课程播放)。我想在这两个组件中显示从后端获取的相同数据。这些是相关文件:

app-routing-module:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { CourseListComponent } from './courses/course-list/course-list.component';
import { CourseDetailComponent } from './courses/course-detail/course-detail.component';
import { CoursePlayComponent } from './courses/course-play/course-play.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

const appRoutes: Routes = [
  { path: '', redirectTo: '/courses', pathMatch: 'full' },
  { path: 'courses', component: CourseListComponent,  pathMatch: 'full' },
  { path: 'courses/:id', component: CourseDetailComponent, pathMatch: 'full'},
  { path: 'courses/:id/:id', component: CoursePlayComponent, pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent, pathMatch: 'full' }];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})

export class AppRoutingModule {  }

课程/课程(界面)

export interface ICourse {
  course_id: number;
  title: string;
  autor: string;
  segments: ISegment[];
}

export interface ISegment {
  segment_id: number;
  unit_id: number;
  unit_title: string;
  name: string;
  type: string;
  data: string;
}

courses / course.service:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { Observable, throwError } from 'rxjs';
import { catchError, groupBy } from 'rxjs/operators';

import { ICourse } from './course';

// Inject Data from Rails app to Angular app
@Injectable()
export class CourseService{
  constructor(private http: HttpClient) {  }

  private url = 'http://localhost:3000/courses';
  private courseUrl = 'http://localhost:3000/courses.json';

  // Handle Any Kind of Errors
  private handleError(error: HttpErrorResponse) {

    // A client-side or network error occured. Handle it accordingly.
    if (error.error instanceof ErrorEvent) {
      console.error('An error occured:', error.error.message);
    }

    // The backend returned an unsuccessful response code.
    // The response body may contain clues as to what went wrong.
    else {
      console.error(
        'Backend returned code ${error.status}, ' +
        'body was ${error.error}');
    }

    // return an Observable with a user-facing error error message
    return throwError(
      'Something bad happend; please try again later.');
  }

  // Get All Courses from Rails API App
  getCourses(): Observable<ICourse[]> {
  const coursesUrl = `${this.url}` + '.json';

  return this.http.get<ICourse[]>(coursesUrl)
      .pipe(catchError(this.handleError));
  }

  // Get Single Course by id. will 404 if id not found
  getCourse(id: number): Observable<ICourse> {
    const detailUrl = `${this.url}/${id}` + '.json';

    return this.http.get<ICourse>(detailUrl)
        .pipe(catchError(this.handleError));
  }


}

courses / course-detail / course-detail.ts:

import { Component, OnInit, Pipe, PipeTransform } from '@angular/core';
import { ActivatedRoute, Router, Routes } from '@angular/router';

import { ICourse } from '../course';
import { CourseService } from '../course.service';

@Component({
  selector: 'lg-course-detail',
  templateUrl: './course-detail.component.html',
  styleUrls: ['./course-detail.component.sass']
})

export class CourseDetailComponent implements OnInit {
  course: ICourse;
  errorMessage: string;

  constructor(private courseService: CourseService,
        private route: ActivatedRoute,
        private router: Router) {
  }

  ngOnInit() {
    const id = +this.route.snapshot.paramMap.get('id');
    this.getCourse(id);
    }

   // Get course detail by id
   getCourse(id: number) {
     this.courseService.getCourse(id).subscribe(
       course => this.course = course,
       error  => this.errorMessage = <any>error);
   }

   onBack(): void {
     this.router.navigate(['/courses']);
   }

}

courses / course-play / course-play.ts:

import { Component, OnInit} from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { MatSidenavModule } from '@angular/material/sidenav';

import { ICourse } from '../course';
import { CourseService } from '../course.service';

@Component({
  selector: 'lg-course-play-course-play',
  templateUrl: './course-play.component.html',
  styleUrls: ['./course-play.component.sass']
})

export class CoursePlayComponent implements OnInit {
  courseId: number;
  errorMessage: string;
  private sub: any;

  constructor(private courseService: CourseService,
      private route: ActivatedRoute,
      private router: Router) {

    }

    ngOnInit() {


      }


     onBack(): void {
       this.router.navigate(['/courses/:id']);
     }

}

5 个答案:

答案 0 :(得分:2)

在不引入任何其他库的情况下,Angular为组件之间的相互通信指定了几种方法。 Documentation

由于您的组件不是父/子,而是兄弟姐妹,因此选项更加受限制。

  1. 具有共享的父组件将数据传递给两个孩子
  2. 将数据存储在共享服务中

根据您显示的代码,我相信#2是您的最佳选择。因此,您可以将属性添加到CourseService

public selectedCourse: ICourse;

然后您可以在两个组件中访问它:

this.courseService.selectedCourse;

此方法的问题在于,您随后必须管理伪全局状态,并确保仅将服务注入/提供一次(否则每个组件都将获得其自己的服务实例,并且您无法共享数据)。


如Pavan对问题的评论中所述,您可能应该使用Observable并订阅它。使用上述方法,当值更改时,组件将不会收到通知,并且需要主动检查负载的更改。

答案 1 :(得分:0)

如果您的路径中有ID,请尝试

import { Component, OnInit} from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { MatSidenavModule } from '@angular/material/sidenav';

import { ICourse } from '../course';
import { CourseService } from '../course.service';

@Component({
    selector: 'lg-course-play-course-play',
    templateUrl: './course-play.component.html',
    styleUrls: ['./course-play.component.sass']
})

export class CoursePlayComponent implements OnInit {
    courseId: number;
    errorMessage: string;
    private sub: any;

    constructor(private courseService: CourseService,
        private route: ActivatedRoute,
        private router: Router) {

    }

    ngOnInit() {
        const id = +this.route.snapshot.paramMap.get('id');
        this.getCourse(id);
    }

    // Get course detail by id
    getCourse(id: number) {
        this.courseService.getCourse(id).subscribe(
        course => this.course = course,
        error  => this.errorMessage = <any>error);
    }

    onBack(): void {
        this.router.navigate(['/courses/:id']);
    }

}

答案 2 :(得分:0)

    //inside service
    export class LocalService {
      obj: BehaviorSubject<any>;
      constructor() {
        this.obj = new BehaviorSubject(this.obj);
      }
      loadData(selectedObj: any): void {
        this.obj.next(selectedObj);
      }
    }

    //inside component1
    const data = {
          object_id: 7444,
          name: 'Relaince'
    };
    this.localService.nextCount(data);

    //inside component2 - subscribe to obj
    this.localService.obj.subscribe(data => {
         this.selectedObj = data;
    });

   // inside component2 html
    <div class="pl-20 color-black font-18 ml-auto">
                    {{selectedObj?.name}}  
         <span><b>{{selectedObj?.object_id}}</b></span>
    </div>

答案 3 :(得分:0)

对于不相关的组件,我们可以利用Mediator Pattern

使用中介模式,对象之间的通信是 封装在中介对象中。对象不再通信 彼此直接交流,而是通过 调解人。

我已经看到“无关组件”一词用于描述有或没有共同父代的组件。


如果有一个共同父母(兄弟姐妹),则该父母可以用作调解人。假设我们具有以下组件的层次结构

-Component A
--Component B
--Component C

,我们希望将数据从组件B传递到组件C。此方法的工作方式是将数据从子组件传递到父组件(组件B到组件A),然后将数据从父组件传递到子组件(组件A到组件B)。 )。

因此,在使用Output绑定的组件B中,使用@Output装饰器,我们发出一个事件(EventEmitter),并且组件A接收该事件的有效负载。然后,组件A调用事件处理程序,而组件C使用@Input装饰器接收数据。

否则(如果没有没有共同的父母),我们可以使用一项服务。在这种情况下,只需将服务注入组件并订阅其事件即可。

答案 4 :(得分:0)

您可以使用静态属性(或方法)在多个组件之间共享数据。下面是一个示例:

标题组件:

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
   public static headerTitle: string = "hello world";
}

页脚组件:

import { HeaderComponent } from '../header/header.component';

@Component({
  selector: 'app-footer',
  templateUrl: './footer.component.html',
  styleUrls: ['./footer.component.scss'],
})
export class FooterComponent {
   public footerText: string = HeaderComponent.headerTitle;
}

尽管使用此方法而不是创建服务可以大大减少开销,但不建议使用更复杂的逻辑。如果逻辑变得更加复杂,您可能会遇到循环依赖性警告和耦合问题。例如,标头是否需要页脚中的数据,反之亦然。对于复杂的逻辑,请使用带有rxjs的BehaviorSubject的服务。服务本应是自力更生的,不应严重依赖其他服务或组件。这将消除所有循环依赖项警告的可能性。