Angular HttpClient не возвращает вполне ожидаемые объекты - PullRequest
0 голосов
/ 04 мая 2018

Допустим, у меня есть

class Foo {
    bar: string;

    public doSomething() {}

}

В моем сервисе я получаю массив таких сообщений с сервера:

this.http.get<Foo[]>(...)

Это работает, я получаю все ожидаемые значения. НО, когда я беру один из полученных объектов и пытаюсь вызвать на нем doSomething (), я получаю сообщение об ошибке, в котором говорится, что у foo (типа Foo) нет метода doSomething ().

Похоже, что хотя http.get правильно проанализировал JSON и назначил все свойства моих объектов, он не стал настраивать для них надлежащие прототипы, так что по сути они являются именами Foos, им не хватает всех методов Фу.

Это нормально или я что-то не так делаю? Спасибо.

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

Вы также можете сделать это по наследству:

this.http.get<Foo>(...)
    .pipe(Foo.serializeResponseMap());

определение класса:

export class Foo extends SerializableMap {
    static instanceType = Foo;

    constructor() { super(); }

    bar: string;

    public doSomething() {}
}


class SerializableMap {
  static instanceType: any;

  static serializeResponseMap(): any {
    const createInstance = (r) => {
      return Object.assign(new this.instanceType(), r);
    };

    return map((respValue: any) => {
      if (Array.isArray(respValue)) {
        return respValue.map(r => createInstance(r));
      }
      return createInstance(respValue);
    });
  }
}
0 голосов
/ 04 мая 2018

Эффективно. В конце концов, вы просто анализируете JSON, и результатом анализа JSON являются просто простые объекты JavaScript, а не пользовательские классы. this.http.get<Foo[]> - это всего лишь подсказка для компилятора, но универсальный параметр ничего не делает. Он не конвертирует один объект в другой и не дает им типы.

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

Тем не менее, можно достичь того, чего вы хотите, если вам действительно нужно, чтобы ваши объекты были класса Foo и имели метод doSomething:

this.http.get<Foo[]>(...).do(items => items.forEach(item => Object.setPrototypeOf(item, Foo.prototype)));

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

Другим вариантом будет дать Foo конструктор:

class Foo {
    constructor(item: Foo) {
        Object.assign(this, item);
    }

    bar: string;

    public doSomething() {}
}

Сейчас:

this.http.get<Foo[]>(...).pipe(map(items => items.map(item => new Foo(item)))).subscribe(...);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...