web-dev-qa-db-de.com

Angular 6 Weitergabe von Daten zwischen zwei nicht miteinander verbundenen Komponenten

Ich habe eine Kurs-Detail-Komponente, die Daten (benannten Kurs) aus meiner Backend-App enthält, und ich möchte diese Daten an eine andere Komponente (Course-Play) übergeben, die nicht mit der Komponente zusammenhängt. Ich möchte die gleichen Daten anzeigen, die ich aus meinem Backend in diesen beiden Komponenten erhalten habe. Dies sind die relevanten Dateien:

app-Routing-Modul:

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

kurse/Kurs (Schnittstelle)

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

kurse/kursdienst:

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


}

kurse/Kursdetails/Kursdetails.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']);
   }

}

kurse/kursspiel/kursspiel.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']);
     }

}

2
Ofir Sasson

Angular gibt einige Möglichkeiten an, mit denen Komponenten miteinander kommunizieren können, ohne andere Bibliotheken einziehen zu müssen. Dokumentation

Da es sich bei Ihren Komponenten nicht um Eltern/Kind, sondern um Geschwister handelt, sind die Optionen noch eingeschränkter.

  1. Lassen Sie eine gemeinsame übergeordnete Komponente die Daten an beide Kinder weitergeben
  2. Speichern Sie die Daten in einem gemeinsam genutzten Dienst

Basierend auf dem gezeigten Code glaube ich, dass # 2 die beste Option ist. So können Sie Ihrer CourseService eine Eigenschaft hinzufügen:

public selectedCourse: ICourse;

Dann können Sie in beiden Komponenten darauf zugreifen:

this.courseService.selectedCourse;

Die Probleme bei diesem Ansatz sind, dass Sie dann einen pseudo-globalen Status verwalten und sicherstellen müssen, dass der Dienst nur einmal injiziert/bereitgestellt wird (andernfalls erhält jede Komponente eine eigene Instanz des Dienstes und Sie können keine Daten freigeben).


Wie in einem Kommentar zu der Frage von Pavan erwähnt, sollten Sie wahrscheinlich eine Observable verwenden und diese abonnieren. Mit dem oben genannten Ansatz erhalten die Komponenten keine Benachrichtigungen, wenn sich der Wert ändert, und müssen proaktiv nach Änderungen beim Laden suchen.

2
Vlad274

Wenn Sie eine ID in Ihrem Pfad haben, versuchen Sie dies

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']);
    }

}
0
Pavan Shukla