Challenges of implementing dark mode with a checkbox and local storage

I'm experiencing an issue with local storage. When I enable the dark mode, everything functions properly and the local storage 'dark' is set to true. However, upon refreshing the page, the local storage remains true but the toggle switches back to unchecked and the theme reverts to white. For the dark theme, I have included bootstrap-dark.

app.sample.component.html:

  
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" (change)="toggleDarkTheme()">
<label class="form-check-label" for="flexSwitchCheckDefault">Dark Mode</label>
</div>

sample.component.ts:

    import { Component, OnInit } from '@angular/core';
     import { Observable } from 'rxjs';
     import { ThemeService } from 'src/app/service/theme.service';

     @Component({
       selector: 'app-sample',
       templateUrl: './sample.component.html',
      styleUrls: ['./sample.component.css']
     })
     export class SampleComponent implements OnInit {
       checked: boolean = false;

       constructor(
         private themeService: ThemeService
       ) {}

       ngOnInit() {
       }

       toggleDarkTheme() {
        this.checked = !this.checked;
         his.themeService.setDarkTheme(this.checked);
         console.log("Dark Theme > ", this.checked);

     }

     }        

theme.service.ts:

    import { Injectable } from '@angular/core';
     import { Subject } from 'rxjs';

     @Injectable({
       providedIn: 'root'
     })
     export class ThemeService {
       constructor() { }

       private _themeDark: Subject<boolean> = new Subject<boolean>();

       isThemeDark = this._themeDark.asObservable();

       setDarkTheme(isThemeDark: boolean) {

         this._themeDark.next(isThemeDark);

         if (isThemeDark == true) {
           console.log('Dark Used');
           document.body.className = 'bootstrap-dark';
           localStorage.setItem('dark', 'true');
         }
         else {
          console.log('Light Used');
           document.body.className = 'bootstrap';
           localStorage.setItem('dark', 'false');
         }

       }



     }        

Answer №1

To ensure the theme is read from local storage, it is recommended to implement this logic within the constructor of your service. Additionally, consider converting this into a BehaviorSubject to provide the latest emitted value to new subscribers:

@Injectable({
  providedIn: 'root',
})
export class ThemeService {
  private _themeDark: BehaviorSubject<boolean>;
  public isThemeDark: Observable<boolean>;
  constructor() {
    this._themeDark = new BehaviorSubject(localStorage.getItem("dark") == 'true');
    this.isThemeDark = this._themeDark.asObservable();
    this.setDarkTheme(this._themeDark.value);
  }

  public toggleDarkTheme(): void {
    this.setDarkTheme(!this._themeDark.value);
  }

  // rest of your code here
}

To synchronize the checkbox with the theme observable, bind the checked attribute to the emitted value of the observable:

<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" (change)="toggleDarkTheme()" [checked]="themeService.isThemeDark | async">

Since the themeService is now accessed in the template, it should be declared as public in the constructor of your SampleComponent:

constructor(
  public themeService: ThemeService
) {}

Avoid using the checked flag directly in the component and delegate this functionality to the service. Instead, call the toggleDarkTheme method from the service within the component:

toggleDarkTheme(): void {
  this.themeService.toggleDarkTheme();
}

You may also consider making the setDarkTheme method within the service private.

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

Angular 11 Import Guide for MAT_DATE_LOCALE

I am struggling with importing 'mat-date-locale' in Angular 11 modules. The link I followed didn't provide a solution, as mentioned here: Cannot find name "MAT_DATE_LOCALE" with Material.angular Datepicker. In my project, I have A ...

Is CORS necessary when using npm, and is Putty a viable tool for web application development?

Currently, I am immersed in a web application project that relies on the following technologies: Express.js + Node.js MySQL Angular 4 PM2 (Process manager) Here are the libraries utilized on the backend: express body-parser jsonwebtoken bcrypt-nodejs ...

Referencing another document in Cloud Firestore: a comprehensive guide

Currently utilizing Angularfire2. Let's say there are users and comments collections. When a comment is added, what is the proper way to reference the user who made the comment? Initially, I considered creating a comment with a structure like this: ...

"Encountering a module not found issue while trying to

Attempting to test out 3 node modules locally by updating their source locations in the package.json files. The modules in question are sdk, ng-widget-lib, and frontend. ng-widget-lib relies on sdk, while frontend depends on ng-widget-lib. To locally build ...

Exploring ways to expand the theme.mixins feature in MUI 5

Currently, I am in the process of updating Material UI from version 4 to 5 and encountering challenges with my existing theming. Since we are using typescript, it is important to include the appropriate types when extending themes. I intend to include th ...

What is the best way to obtain a reference to an instance of my Angular 2 directive?

Angular 2 rc 5 was written using typescript 1.9 I am trying to access the instance of my attribute directive. Although I am using ViewChild, which typically works with components, it is giving me a handle to the element containing the directive. template ...

Learn the process of synchronously loading forms and data in Angular

Looking to synchronize the loading of data and form... Let's start by reviewing the code: ngOnInit() { if (this.data.fromCalendar) { this.singleTraining(); } }, 200); this.formControl(); } formControl() { this.gib ...

Testing reactive streams with marble diagrams and functions

Upon returning an object from the Observable, one of its properties is a function. Even after assigning an empty function and emitting the object, the expectation using toBeObservable fails due to a non-deep match. For testing purposes, I am utilizing r ...

Unable to organize birth dates by using the datetime feature with the moment plugin in Angular 2 ventures

A table is in place showcasing id, name, and date of birth: <table id="example" class="display nowrap" style="width:100%"> <tr> <td>id</td> <td>name</td> <td>date of birth</td> </tr> ...

Why is my RxJS timer not waiting for the specified time?

I'm diving into the world of RxJS and trying to grasp its concepts. During some testing, I encountered a puzzling issue that has me stumped. Below is the snippet in question : let item = { id: 1, name: 'chair' }; const asyncItem = timer(20 ...

Combining the JSON code coverage reports generated by both Cypress and Karma does not yield an accurate outcome

In my angular project, I am testing it using the built-in unit testing tool (karma) and Cypress. My goal is to combine the code coverage reports from both tests. I have successfully set up the coverage configurations and merged the outputs using `nyc merg ...

Conceal the React button once it has been pressed

In my checklist of questions, I have set up a system where the first button is shown if any checkboxes are selected. If no checkbox is selected, then the second "Submit" button is displayed. Upon clicking submit, a message appears inside. Additionally, for ...

Is Angular UI's data binding more of a push or pull mechanism? How can I optimize its speed?

Suppose I have a variable a that is displayed in HTML as {{a}}. If I then update its value in TypeScript using a = "new value";, how quickly will the new value be reflected in the user interface? Is there a mechanism that periodically checks all bound var ...

Discover the latest DOM elements using Webdriverio 5

Currently, I am developing a REact based CMS application that features a form with multiple carousels. I am encountering an issue where the webdriverio implementation is unable to locate an element, even though the same xpath works fine when tested manua ...

Exploring project references in TypeScript 3 with distinct `outDir` configurations

I am eager to utilize the project references functionality in TypeScript 3.1. Initially, my project's directory structure post-compilation appears as follows: . ├── A │ ├── a.ts │ ├── dist │ │ ├── A │ │ ...

Encountering a 404 error when trying to access the rxjs node_module

While attempting to compile an angular2 application, I encountered the following issue: Error: XHR error (404 Not Found) loading http://localhost:3000/node_modules/rxjs(…) systemjs.config.js (function(global) { // map tells the System loader whe ...

Execute tap() function without subscribing

Can the tap() pipe function be executed without subscribing to it? observer$:BehaviorSubject<number[]> = new BehaviorSubject<number[]>([1]) getData(page:number):void{ of(page).pipe( tap({ next: (data) => this.observe ...

Is it possible to import the identical file twice consecutively using html and typescript?

I encountered an issue with an input element in my HTML file. Here's what it looks like: <input type="file" (change)="receiveFile($event)" id="inputFileButton" hidden /> This input element is designed for users to import files. Wh ...

Is there a way to ensure that the observer.next(something) received the value before executing observer.complete()?

I have been working on an Angular app where I am using a resolver to preload data in the component. Below is the code for the resolver: resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): void { return Observable.create(observer => { ...

The compatibility between jQuery serialize and Angular Material tabs is not optimal

Can anyone help with an issue I'm facing? I have angular material tabs embedded within a form tag, and you can view my code example here. The problem arises when I attempt to serialize the form using jQuery in my submit function: submit(f: HTMLElemen ...