Utilizing LocalStorage with Angular 6 BehaviorSubject

I'm struggling with retaining data after refreshing a page. My approach involves using a shared service to transfer data between unrelated components. Despite extensive research on LocalStorage implementation and usage, I have not been able to find a suitable solution for my project due to the multitude of options available. I have a course-detail component that passes the course ID to the service, and a course-play component that retrieves this ID and makes an HTTP request using it. However, every time I refresh the course-play page, the data disappears. I need guidance on utilizing LocalStorage effectively to persist this data across refreshes (and updating the ID when switching to a different course). I will include the relevant code snippets below:

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 retrieve data from
  private url = 'http://localhost:3000/courses';
  private courseUrl = 'http://localhost:3000/courses.json';

  // Subscribing to data
  private courseId = new BehaviorSubject(1);
  public courseId$ = this.courseId.asObservable();

  // Updating the observable value
  setId(courseId) {
    this.courseId.next(courseId)
  }

  constructor(private http: HttpClient) { }
  
  // Error handling method
  private handleError(error: HttpErrorResponse) {

    if (error.error instanceof ErrorEvent) {
      console.error('An error occurred:', error.error.message);
    }
    
    else {
      console.error(
        'Backend returned code ${error.status}, ' +
        'body was ${error.error}');
    }

    return throwError('Something went wrong; please try again later.');
  }

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

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

// Fetching a single course by id
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';


@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,
route: ActivatedRoute,
router: Router) {
}

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

courseService.setId(id);
getCourse(id);
}

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

}

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


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

export class CoursePlayComponent implements OnInit {
errorMessage: string;
course: ICourse;
courseId: number;

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

ngOnInit() {
const segment_id = + route.snapshot.paramMap.get('segment_id');

console.log(courseId);
getCourse(courseId);
}


getCourse(id: number) {
console.log(id);
courseService.getCourse(id).subscribe(
course => this.course = course,
error  => this.errorMessage = <any>error
);
}

}

Answer №1

It's important to note an error in the setId() method - it should accept the course ID as an argument, not the subject.

After calling next() on the subject, make sure to update the local storage and then reload the saved data from storage in the service constructor. Here is an example:

const COURSES_IN_STORAGE = 'courses_in_storage';

@Injectable()
export class CourseService{
...
  courseIdState: string;

  // This method is called by the component  
  setId(courseIdValue: string) {
    this.courseId.next(courseIdValue);
    localStorage.setItem(COURSES_IN_STORAGE, courseIdValue);
  }

 constructor() {
    this.courseIdState = localStorage.getItem(COURSES_IN_STORAGE) || {}; 
  }
}

Answer №2

If you're looking to store your information in local storage, follow these steps:

Save your data by using the code:

localStorage.setItem('variablename', JSON.stringify('data you want to store'));

Retrieve the stored data like this:

this.anyvariable = JSON.parse(localStorage.getItem('savedvariablename'));

That's all there is to know about local storage!

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

Verifying the outcomes of a spied function with the callThrough method

Is there a way to validate the outcomes of a spied function in nestjs using jasmine? I have set up a jasmine spy on a method to monitor its arguments and response/exception, but I'm unable to access the return value of the spied method. For example, ...

The web server is now serving Index.html instead of main.js for AngularJS 2

Transitioning from an angular1 application to an angular2 one, I encountered an error after removing the ng-app directive and adding the system.config: Error: SyntaxError: Unexpected token < Evaluating https://localhost:8080/contents/app/main.js Error ...

` `angular2 get the element reference of a component that is not a direct descendant` `

Within my application, the structure is as follows: <fin-header></fin-header> <main></main> <fin-footer></fin-footer> I would like to implement scrolling within the header to a specific component within main. However, ...

Angular: utilizing two instances of the <router-outlet> within a single component

Disclaimer: Despite finding a similar question on Stack Overflow with an accepted answer that did not solve my issue, I am still facing a problem. The challenge I am encountering is needing to use the same "router-outlet" twice in my main component. Howev ...

The NgSwitch is behaving unexpectedly, throwing an exception with the message "No provider for NgSwitch"

My HTML code snippet is shown below: <ng-container *ngIf="col.data !== ''"> <ng-template [ngSwitch]="col.data"> <ng-container *ngSwitchCase="'Page'"> <div>{{g ...

Utilizing Angular to Fetch JSON Data from a Server

I am currently retrieving data from a JSON file that resides on a server and is updated regularly. It's crucial for me to access this JSON content in order to consistently showcase the most recent information on my website. At present, I have stored ...

"Prevent further button clicks by disabling it after the initial click with ActionRowBuilder in Discord.Js

Encountering a puzzling issue where I am unable to disable a button after it has been clicked. The option to disable the button does not seem to appear. When attempting to deactivate the button, I utilize the following function: const row = new ActionRowBu ...

How can I use Typescript to define a function that accepts a particular string as an argument and returns another specific string?

I've been working on this code snippet: const Locales = { en_gb: 'en-gb', en_us: 'en-us', } as const type ApiLocales = typeof Locales[keyof typeof Locales] type DatabaseLocales = keyof typeof Locales function databaseLanguage ...

Execute service operations simultaneously and set the results in the sequence they are received

I am faced with a challenge involving multiple service methods that fetch data from various servers. The responses from these APIs come in at different times, and I need to store the responses in variables as soon as they are received. Here are my service ...

Troubleshooting import errors with Typescript for C3 and D3 libraries

I have recently started working on a project using the C3 graphing library within an Ionic2/Angular2 TypeScript setup. After installing C3 via npm and the type definitions via tsd, I imported it into my own TypeScript file like this: import {Component} fr ...

What are some effective ways to manage repetitive HTML elements like headers and footers in Angular 4?

Within my Angular web project, I find myself using a variety of different headers on multiple pages. Is there a way to streamline this process by coding the header and footer once and having them automatically included in one or more pages? I am looking ...

Tips for showcasing individual row information in a popup window with Angular 9

Here is the code snippet for utilizing the open dialog method in the component: import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { AuthService } from '../_services/auth.se ...

Verifying data types in TypeScript

When working with TypeScript in the browser, I often find myself writing code like this: const button = document.getElementById(id); if (!(button instanceof HTMLButtonElement)) { throw new Error("TODO -- insert better error message here"); } bu ...

Typescript issue: variable remains undefined even after being assigned a value in the constructor

In my code, I declared num1: number. constructor(){ ... this.num1 = 0; } This is all inside a class. However, when I try to log the value of this.num1 or typeof this.num1 inside a function using console.log(), it returns undefined in both cases. I ...

"From time to time, reimport React when saving to ensure all necessary imports are

When working with TypeScript in *.tsx files, particularly when copying code around, I frequently encounter the issue of an additional import line being added. This can be seen below: import React from "react"; // ? On Save "editor ...

Utilizing Angular: Integrating the Http response output into a map

I have a situation where I am making multiple HTTP calls simultaneously from my Angular application. The goal is to store the responses of these calls in a Map. data: Map<number, any> = new map<number,any>(); --------------------------------- ...

The directive [ngTemplateOutet] is functioning properly, however, the directive *ngTemplateOutlet is not functioning as expected

I have been struggling to create a component using ngTemplateOutlet to select an ng-template, but for some reason *ngTemplateOutlet is not working in this case (although [ngTemplateOutlet] is functioning correctly). Below is the code I am currently using: ...

TypeScript combines strong typing for arrays into a unified array of objects

I developed a JavaScript function that can merge multiple arrays into an array of objects based on provided key names. Here’s an example: const mergeArraysToSeries = (arrs, keys) => { const merged = []; for (let dataIndex = 0; dataIndex < arrs ...

Utilizing Angular2 to access NPM package (Googleapis)

I am currently developing an Angular2 application that utilizes Webpack for the build process. I want to implement a Google oauth login feature in my application, so I have added the googleapi package from npm. However, I am facing difficulties when trying ...

Waiting for Angular to finish multiple HTTP requests

I am encountering a similar issue to the one described in this post: Angular wait for multiple http requests to complete and then fire the last one My goal is to accomplish exactly what is shown below: forkJoin( this.data.getCodes('medical'), ...