Ofir Sasson
Ofir Sasson

Reputation: 671

Angular 6 BehaviorSubject using LocalStorage

I'm having a problem saving data after refreshing a page. I'm using a shared service to pass data between unrelated components. I searched over all Google about LocalStorage and how to use it and I didn't get an answer. there are so many different implements of LocalStorage that I don't know what fits my project. I have course-detail component which pass course id to the service, and course-play component which get that id and request http get with this id. every time I refresh course-play page the data disappear. I need help with what to write and where when using LocalStorage to save this data after refresh (and update the id when I'm in a different course). I'll attach the relevant code:

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 courseId = new BehaviorSubject(1);
  public courseId$ = this.courseId.asObservable();

  // here we set/change value of the observable
  setId(courseId) {
    this.courseId.next(courseId)
  }

  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 course id to use it on the html file
    const id = +this.route.snapshot.paramMap.get('id');

    // set curretn course Id in the service to use it later
    this.courseService.setId(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
        );
      }

}

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;
  course: ICourse;
  courseId: number;

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

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

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


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

}

Upvotes: 1

Views: 8175

Answers (2)

Asher Butt
Asher Butt

Reputation: 1

You can save your data into localstorage by this:

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

and to get data back from localstorage by this:

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

that's it about localstorage:

Upvotes: 0

Yakov Fain
Yakov Fain

Reputation: 12376

First of all, you have an error in the setId() method - it has to have a value of the course ID as an argument and not the subject.

You need to update the local storage right after you invoke next() on the subject, and reload the saved data from the storage in the constructor of the service. For example,

const COURSES_IN_STORAGE = 'courses_in_storage';

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

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

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

Upvotes: 1

Related Questions