I'm trying to serialise a model object to local storage (or IndexedDB), however it contains signals.
// An example of the model I'm dealing with
export class ExampleModel {
canSerialise = "";
cantSerialise = signal("");
}
// What I'd like to be able to do
let exampleModel = new ExampleModel();
localStorage.setItem("example", JSON.stringify(exampleModel));
However this will not serialise cantSerialise
as signals are technically functions. I'd like to avoid manually writing serialisation functions for all models that just have a signal in them.
JSON.stringify()
:
'{"canSerialise":"","cantSerialise":X}'
where X
can be anything as long as it serialises back into the original value as a signal (in this case signal("")
)'{"canSerialise":""}'
EDIT:
I should clarify the goals is for the model to be serialised and that said model happens to have signals in it, I'm given the model and can't change it. A the same time i would like to stress that deserialisation does need to result in having signals in the same places as before serialisation (e.g. cantSerialise
should be wrapped in a signal during deserialisation but canSerialise
should not) as the code dealing with the model (which again I don't have control over) expects those signals. The signals don't need to be the exact same signals per se i.e. just storing their value during serialisation and wrapping them in a new signal during deserialisation is acceptable.
This is not perfect code, but you can write a custom toString
method that executes the signal before writing to an object. Then we can call JSON.stringify
to convert the object which contains only the values into a string.
Please ignore my TypeScript mistakes, since it's the best I can do; it might look ugly but it gets the job done.
export class ToStringConverter {
toString() {
let output: any = {};
const that: ToStringConverter = this as never as ToStringConverter;
for (let key in that) {
if (that!.hasOwnProperty(key)) {
const lookupKey: keyof ToStringConverter =
key as keyof ToStringConverter;
if ((that[lookupKey] as any) instanceof Function) {
output[lookupKey] = (that[lookupKey] as Function)() as any;
} else {
output[lookupKey] = that[lookupKey];
}
}
}
return JSON.stringify(output);
}
}
Full code:
import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
export class ToStringConverter {
toString() {
let output: any = {};
const that: ToStringConverter = this as never as ToStringConverter;
for (let key in that) {
if (that!.hasOwnProperty(key)) {
const lookupKey: keyof ToStringConverter =
key as keyof ToStringConverter;
if ((that[lookupKey] as any) instanceof Function) {
output[lookupKey] = (that[lookupKey] as Function)() as any;
} else {
output[lookupKey] = that[lookupKey];
}
}
}
return JSON.stringify(output);
}
}
// An example of the model I'm dealing with
export class ExampleModel extends ToStringConverter {
canSerialise = '';
cantSerialise = signal('');
constructor(canSerialize = '', cantSerialize = '') {
super();
this.canSerialise = canSerialize;
this.cantSerialise = signal(cantSerialize);
}
}
@Component({
selector: 'app-root',
standalone: true,
template: `
<h1>Hello from {{ name }}!</h1>
<a target="_blank" href="https://angular.dev/overview">
Learn more about Angular
</a>
`,
})
export class App {
name = 'Angular';
ngOnInit() {
// What I'd like to be able to do
let exampleModel = new ExampleModel('working', 'also working');
localStorage.setItem('example', exampleModel.toString());
}
}
bootstrapApplication(App);