How to transfer data between components in Angular 6 using a service

I'm facing an issue with passing data between the course-detail component and the course-play component. I tried using a shared service and BehaviorSubject, but it didn't work as expected. Strangely, there are no errors thrown, and the data remains unchanged. The only error message I see when I open course-detail.html is "ERROR ReferenceError: course is not defined". Although the data gets passed correctly to the course-detail component through the service, I struggle with getting the data back to the service so that course-play can also utilize it. Below are the relevant files:

course.ts

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

course.service

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

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{

  // JSON url to get data from
  private url = 'http://localhost:3000/courses';
  private courseUrl = 'http://localhost:3000/courses.json';

  // Subscribe data
  private courseData = new BehaviorSubject<ICourse>(null);
  public courseData$ = this.courseData.asObservable();

  // here we set/change value of the observable
  setData(course) {
    this.courseData.next(course)
  }

  constructor(private http: HttpClient) { }

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


}

course-detail.component

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

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


// Course-detail decorator
@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) {
  }

  // On start of the life cycle
  ngOnInit() {
    // get the current segment id to use it on the html file
    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;
       this.courseService.setData(course))
   }

   // When we click the back button in browser
   onBack(): void {
     this.router.navigate(['/courses']);
   }

}

course-play.component

import { Component, OnInit, Input} 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';


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

export class CoursePlayComponent implements OnInit {
  errorMessage: string;
  courseData: ICourse;

  constructor(private courseService: CourseService,
      private route: ActivatedRoute,
      private router: Router) {
        courseService.courseData$.subscribe( courseData => this.courseData = courseData)
  }

    // On start of the life cycle
    ngOnInit() {
        // get the current segment id to use it on the html file
        const id = +this.route.snapshot.paramMap.get('id');
      }

    // When we click the back button in browser
     onBack(): void {
       this.router.navigate(['/courses/:id']);
     }

}

Answer №1

You should not be calling the

this.courseService.setData(course)
function in this particular location.

Please update the code block in the course-detail.component file with the following:

  // Retrieve course details by ID
   getCourseDetails(id: number) {
     this.courseService.getCourse(id).subscribe(
       course =>  {
          this.course = course;
          this.courseService.setData(course);
       },
       error  => this.errorMessage = <any>error;
      )
   }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding a class to a child component layout from a parent component in Angular 12 and Typescript can be achieved by using the ViewChild decorator

Incorporating the child component into the parent component is an important step in the structure of my project. The dashboard component serves as the child element, while the preview component acts as the parent. Within the parent (preview) component.htm ...

Tips for moving between multiple router outlets on a page?

My search has lasted for half a day, but I am still unable to find a solution that works. Here are the routes I currently have: app-routing.module.ts const routes: Routes = [ { path: '', redirectTo: 'feature2', pathMatch ...

Ways to speed up the initial loading time in Angular 7 while utilizing custom font files

Storing the local font file in the assets/fonts folder, I have utilized 3 different types of fonts (lato, raleway, glyphicons-regular). https://i.stack.imgur.com/1jsJq.png Within my index.html under the "head" tag, I have included the following: <lin ...

Leveraging a React hook within a Next.js API route

I am looking for a way to expose the data fetched by a React.js hook as a REST endpoint using Next.js. To create a REST endpoint in Next.js, I can easily use the code below in pages/api/index.tsx export default function handler(req: NextApiRequest, res: N ...

At what point is the ngOnInit function invoked?

Here is the code snippet I am working with: <div *ngIf="hotels$ | async as hotels; else loadGif "> <div *ngFor="let hotel of hotels | hotelsFilter: _filteredType; first as f; index as i " appInit [hotel]="hotel " [first]="f "> < ...

What is the best way to pass a conditional true or false value to React boolean props using TypeScript?

I am currently utilizing the Material UI library in combination with React and Typescript. Whenever I attempt to pass a conditional boolean as the "button" prop of the component, I encounter a typescript error stating: Type 'boolean' is not assi ...

Protractor functions perfectly on localhost, but encounters a Timeout error when used remotely - the asynchronous callback was not executed within the specified timeout period

Running protractor protractor.conf.js --baseUrl=http://localhost:4200/ executes successfully by filling data, validating elements, etc. However, when attempting to test the same website through a remote URL protractor protractor.conf.js --baseUrl=http://t ...

Automatic renewal of bearer token in Angular 7

My AuthService has two key methods: getAuthToken (returns a Promise to allow lazy invocation or multiple invocations with blocking wait on a single set) refreshToken (also returns a Promise, using the refresh token from the original JWT to request a ...

Specialized type for extra restriction on enum matching

In my current project, I am dealing with two enums named SourceEnum and TargetEnum. Each enum has a corresponding function that is called with specific parameters based on the enum value. The expected parameter types are defined by the type mappings Source ...

Angular (TypeScript) time format in the AM and PM style

Need help formatting time in 12-hour AM PM format for a subscription form. The Date and Time are crucial for scheduling purposes. How can I achieve the desired 12-hour AM PM time display? private weekday = ['Sunday', 'Monday', &apos ...

detect the dismissal event in the modal controller from the main component

Within my MainPage, I invoke the create function in the ModalController, which displays the ModalPage. Upon clicking cancel, the dismiss function is called and we are returned to the MainPage. The process functions as expected. @Component({ selector: &a ...

Suggestions for developing a component library for angular/react modules

I'm currently leading an initiative at my workplace to implement the use of standardized components across all our projects in order to maintain a consistent look and functionality that aligns with our brand. My main concern is figuring out how to eff ...

Unleash the potential of a never-ending expansion for grid cells on Canvas

ts: templateStyle = { display: 'grid', 'grid-template-columns': 'calc(25%-10px) calc(25%-10px) calc(25%-10px) calc(25%-10px)', 'grid-template-rows': '150px auto auto', 'grid-gap ...

Comparing Angular 2's Seed and CLI: A closer look at

Exploring my options for beginning a fresh Angular 2 Project. I stumbled upon angular2-seed and angular-cli as potential tools for kickstarting the project. However, I am deliberating on which one to opt for and curious about their respective advantages a ...

The separator falls short of spanning the entire width of the page

For some reason, I can't seem to make the divider extend to the full length of the page. <TableRow> <TableCell className={classes.tableCell} colSpan={6}> <Box display="grid" gridTemplateColumn ...

Displaying the ngFor data in the HTML section

Struggling with passing an array from poll-vote.component.ts to poll-vote.component.html. The data involves radio buttons and I'm using ngFor loop with index, but it's not working as expected: Here is my poll-vote.component.ts code: import { Com ...

Efficiently search and filter items across multiple tabs using a single search bar in the Ionic 2

I am currently working on implementing a single search bar that can filter lists in 2 different tabs within Ionic 2. The search bar is functional, and I have a method for filtering through objects. However, my goal is to allow users to select different tab ...

What is the process for applying this specific style to a certain element?

Here is an example of an element in an Angular2 app: <div class="ticket-card" [ngStyle]="{'background': 'url(' + ticketPath + ')' , 'background-size': 'cover'}"> I would like to enhance the style b ...

Encountering difficulty when determining the total cost in the shopping cart

I am currently working on a basic shopping cart application and I am facing an issue when users add multiple quantities of the same product. The total is not being calculated correctly. Here is my current logic: Data structure for Products, product = { ...

When you use map to transform the value, Observable will consistently return as undefined

Can anyone help me figure out why, when I transform the observable, it returns undefined in the console log even though there are assigned values? HTTP Request getLibraryCardPeople(bookName: String): Observable<people[]> { return this.http.get<Li ...