Добавление десериализованного интерфейса к модели вызывает ошибку - PullRequest
0 голосов
/ 06 апреля 2020

Все еще плохо знаком с TypeScript и Modeling, но я получаю сообщение об ошибке, когда добавляю десериализуемый интерфейс к моим моделям. Вот мои макеты json данные:

{
  "isPayrollAdmin": true,
  "contacts": [
    {
      "name": "Contact 1",
      "phone": "555-555-1212",
      "phoneExt": 351,
      "email": "user1@test.com",
      "implementationRole": true,
      "operationsRole": false,
      "userId": 123
    },
    {
      "name": "Contact 2",
      "phone": "555-555-1212",
      "phoneExt": 225,
      "email": "user2@test.com",
      "implementationRole": false,
      "operationsRole": true,
      "userId": 456
    }
  ]
}

Вот мои две модели:

export class ClientProfile implements Deserializable {
  isPayrollAdmin: boolean;
  contacts: Contact[];

  deserialize(input: any): this {
    Object.assign(this, input);

    this.contacts = input.contacts.map((contact) =>
      new Contact().deserialize(contact)
    );

    return this;
  }
}

А вот и Контакт Модель:

export class Contact implements Deserializable {
  name: string;
  phone: string;
  phoneExt?: number;
  email: string;
  implementationRole: boolean;
  operationsRole: boolean;
  userId?: number;

  deserialize(input: any): this {
    return Object.assign(this, input);
  }
}

И десериализуемый интерфейс:

export interface Deserializable {
  deserialize(input: any): this;
}

Сервис довольно прост:

import profile from '../../../shared/utils/client-profile.json';
import { ClientProfile } from '../models/client-profile.model';

export class ClientProfileService {
  public profile: ClientProfile;

  constructor() {}

  getClientProfile(): ClientProfile {
    return profile;
  }
}

И мой компонент называет его так:

profile: ClientProfile;

ngOnInit() {
    this.profile = this.clientProfileService.getClientProfile();
    this.createForm(); // builds a dynamic form
  }

Это ошибка:

Property 'deserialize' is missing in type '{ "isPayrollAdmin": boolean; "contacts": { "name": string; "phone": string; "phoneExt": number; "email": string; "implementationRole": boolean; "operationsRole": boolean; "userId": number; }[]; }' but required in type 'ClientProfile'.

Не означает ли это, что мои данные действительно не смоделированы правильно? Если я проверяю ответ, это то, что я вижу, прямо JSON:

enter image description here

Естественно, если я удалю десериализацию 1034 * метод то все работает просто отлично. Итак, что происходит? Я испортил моделирование? Спасибо за любые полезные советы.

1 Ответ

1 голос
/ 06 апреля 2020

Решение для ошибки

Похоже, ошибка в вашей службе

import profile from '../../../shared/utils/client-profile.json';
import { ClientProfile } from '../models/client-profile.model';

export class ClientProfileService {
  public profile: ClientProfile;

  constructor() {}

  getClientProfile(): ClientProfile {
    return profile; // <- this is the profile you loaded from the .json file 
    // so it does not have `.deserialize()` which is where the error is coming from
  }
}

Если вы хотите вернуть профиль службы, вам нужно this.profile, но, похоже, вам сначала нужно сопоставить этот объект, чтобы не получить ошибку времени выполнения.

import profile from '../../../shared/utils/client-profile.json';
import { ClientProfile } from '../models/client-profile.model';

export class ClientProfileService {
  public profile: ClientProfile;

  constructor() {
    // create a new ClientProfile from the loaded .json file
    // JSON.parse() may not be necessary
    this.profile = new ClientProfile().deserialize(JSON.parse(profile));
  }

  getClientProfile(): ClientProfile {
    return this.profile; // <- change to `this.profile`
  }
}

Это должно сработать (или, по крайней мере, приблизить вас).

Практика моделирования

Когда дело доходит до моделирования в Typescript, лучшим решением (IMO) является использование interfaces для определения ваших "POJO" (Plain Old JavaScript Objects). Причина в том, что при загрузке данных из бэкэнда ваш http-клиент преобразует ответ в объект JavaScript за кулисами. Это также избавит вас от необходимости беспокоиться о создании классов new повсюду.

Для вашего конкретного сценария я бы применил это изменение следующим образом:

пример структуры папок (ПРИМЕЧАНИЕ. Мне нравится хранить мои службы в отдельной папке так как они обычно предоставляются в root)

.
├── src
└── app
    ├── ...
    ├── services
    │   └── client-profile.service.ts
    ├── client-profile
    │   ├── client-profile.component.html
    │   └── client-profile.component.ts
    └── models
        └── client-profile.model.ts

client-profile.model.ts

/* I like to prefix my interface delcarations with an `I` */
export interface IClientProfile {
  isPayrollAdmin: boolean;
  contacts: IContact[];
}

export interface IContact {
  name: string;
  phone: string;
  phoneExt: number;
  email: string;
  implementationRole: boolean;
  operationsRole: boolean;
  userId: number;
}

Тогда вы просто скажете машинописи, что тип объекта, который вы ожидаете, и он будет использовать ваше определение. Так что в вашем client-profile.service.ts вам не нужно будет создавать новый класс.

client-profile.service.ts

import profile from '../../../shared/utils/client-profile.json';
import { IClientProfile } from '../models/client-profile.model';

export class ClientProfileService {
  public profile: IClientProfile;

  constructor() {
    // you don't need to create a new object because you already have a JavaScript object
    // you just need to tell typescript what kind of object you have
    this.profile = JSON.parse(profile) as IClientProfile;
  }

  getClientProfile(): IClientProfile {
    return this.profile; 
  }
}

Когда вы приступите к выполнению http-запросов на данные, вам не придется делать никаких дополнительных сопоставлений с построить дополнительные объекты.

client-profile.service.ts пример реализации http

import { HttpClient } from '@angular/common/http';
import profile from '../../../shared/utils/client-profile.json';
import { IClientProfile } from '../models/client-profile.model';

export class ClientProfileService {
  public profile: IClientProfile;

  constructor(private http: HttpClient) { /* ... */ }

  /** example http request where you tell typescript what kind of object you are expecting */
  loadAllProfiles(): Observable<IClientProfile[]> {
    return this.http.get<IClientProfile[]>('api/profiles');
  }

  // ... 
}

Старайтесь избегать создания или добавления методов к данным, которые вы получаете от бэкэнд Вы хотите, чтобы ваши модели данных были максимально чистыми.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...