我有一个课程详细信息组件,其中包含来自后端应用程序的数据(名为课程),我想将该数据传递给与该组件无关的另一个组件(课程播放)。我想在这两个组件中显示从后端获取的相同数据。这些是相关文件:
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']);
}
}
答案 0 :(得分:2)
在不引入任何其他库的情况下,Angular为组件之间的相互通信指定了几种方法。 Documentation
由于您的组件不是父/子,而是兄弟姐妹,因此选项更加受限制。
根据您显示的代码,我相信#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的服务。服务本应是自力更生的,不应严重依赖其他服务或组件。这将消除所有循环依赖项警告的可能性。