Сериализация модели с сигналами в Angular 18

Я пытаюсь сериализовать объект модели в локальное хранилище (или IndexedDB), однако он содержит сигналы.

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

Однако это не будет сериализоваться cantSerialise, поскольку сигналы технически являются функциями. Я бы хотел избежать ручного написания функций сериализации для всех моделей, в которых есть только сигнал.

  • Попробовал просто сериализовать с помощью JSON.stringify():
    • Желаемый результат: '{"canSerialise":"","cantSerialise":X}' где X может быть чем угодно, при условии, что оно сериализуется обратно в исходное значение в качестве сигнала (в данном случае signal(""))
    • Результаты (как я боялся/ожидал) через '{"canSerialise":""}'
  • Мысль об использовании IndexedDB, но она также не сможет сериализовать сигналы, поскольку алгоритмы структурированного клонирования имеют то же ограничение, что и функции сериализации nog.

Обновлено: Я должен уточнить, что цель состоит в том, чтобы модель была сериализована, и в этой модели есть сигналы, мне дана модель, и я не могу ее изменить. В то же время я хотел бы подчеркнуть, что десериализация действительно должна приводить к появлению сигналов в тех же местах, что и до сериализации (например, cantSerialise должен быть обернут в сигнал во время десериализации, а canSerialise не должен), поскольку код, работающий с моделью (который опять же, я не могу контролировать) ожидает этих сигналов. Сигналы не обязательно должны быть одними и теми же сигналами сами по себе, т.е. допустимо просто сохранить их значение во время сериализации и обернуть их в новый сигнал во время десериализации.

Можете ли вы объяснить свой вариант использования, почему вы хотите сохранить сигнал в localStorage?

Chellappan வ 21.07.2024 08:32

Вы используете TS, но не указали соответствующий тег в своем вопросе, с другой стороны, вы используете теги signals и angular-signals. Также обратите внимание на комментарий @Chellappanவ, чтобы устранить проблему XY.

Jerry 23.07.2024 10:03

@Chellappan வ Я добавил некоторые пояснения выше.

Jarno 23.07.2024 20:37
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
3
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не идеальный код, но вы можете написать собственный метод toString, который выполняет сигнал перед записью в объект. Затем мы можем вызвать JSON.stringify, чтобы преобразовать объект, содержащий только значения, в строку.

Пожалуйста, игнорируйте мои ошибки TypeScript, поскольку это лучшее, что я могу сделать; это может выглядеть некрасиво, но свою работу выполняет.

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

Полный код:

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

Демо-версия Stackblitz

Это решение частично удается тем, что оно сериализует сигналы, но нет различия между значением, которое было заключено в сигнал, и значением, которое не было заключено в сигнал, что означает, что десериализация не может обернуть сигналы вокруг значений там, где это необходимо. Как я писал в моем вопросе, его необходимо десериализовать как сигнал. Я использовал модифицированную версию вашего решения, в которой я добавляю к ключам/свойствам префикс func_ или other_, чтобы при десериализации я мог просто удалить префикс и при необходимости включить новый сигнал.

Jarno 23.07.2024 20:59

astebin.com/H8VyspgA

Jarno 23.07.2024 21:20

Другие вопросы по теме