Выражение ngShow вычисляется слишком рано

У меня есть угловой компонент и контроллер, которые выглядят так:

export class MyController{

    static $inject = [MyService.serviceId];

    public elements: Array<string>;
    public errorReceived : boolean;

    private elementsService: MyService;

    constructor(private $elementsService: MyService) {
        this.errorReceived = false;
        this.elementsService= $elementsService;
    }

    public $onInit = () => {
        this.elements =  this.getElements();
        console.log("tiles: " + this.elements);
    }


    private getElements(): Array<string> {
        let result: Array<string> = [];
        this.elementsService.getElements().then((response) => {
            result = response.data;
            console.log(result);
        }).catch(() => {
            this.errorReceived = true;
        });
        console.log(result);
        return result;
    }
}

export class MyComponent implements ng.IComponentOptions {
    static  componentId = 'myId';

    controller = MyController;
    controllerAs = 'vm';

    templateUrl = $partial => $partial.getPath('site.html');
}

Реализация MyService выглядит следующим образом:

export class MyService {

    static serviceId = 'myService';

    private http: ng.IHttpService;

    constructor(private $http: ng.IHttpService) {
        this.http = $http;
    }

    public getElements(): ng.IPromise<{}> {
        return this.http.get('./rest/elements');
    }

}

Проблема, с которой я сталкиваюсь, состоит в том, что элементы массива содержат пустой массив после вызова onInit(). Однако позже я вижу, что данные были получены, поскольку вызывается функция успеха в getELements() и элементы записываются в консоль.

элементы, которые я использовал в своем шаблоне, чтобы решить, должен ли быть показан конкретный элемент:

<div>
    <elements ng-show="vm.elements.indexOf('A') != -1"></elements>
</div>

Проблема сейчас в том, что vm.elements сначала содержит пустой массив, и только позже массив заполняется фактическим значением. Но тогда это выражение в шаблоне уже было оценено. Как я могу это изменить?

1 ответ

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

Проблема с вашей текущей реализацией заключается в том, что ваш метод init немедленно возвращает пустой массив. Он не возвращает результат вызова службы, поэтому свойство в вашем контроллере просто снова связывается с пустым массивом, а это не то, что вам нужно.

Взгляните на следующее:

export class MyController {
   elements: string[] = [];

   $onInit = () => {
     this.getElements()
       .then(elements => {
         this.elements = elements;
       });
  };

  getElements() {
    return this.elementsService
      .getElements()
      .then(response => response.data)
      .catch(() => {
        this.errorReceived = true;
      });
   } 
}

Вы можете сделать это более читабельным, используя async/await

export class MyController {
   elements: string[] = [];

   $onInit = async () => {
     this.elements = await this.getElements();
   };

   async getElements() {
     try {
       const {data} = await this.elementsService.getElements();
       return data;
     } 
     catch {
       this.errorReceived = true;
     }
   } 
}

Обратите внимание, что вышеизложенное позволяет использовать стандартные try/catch синтаксис. Это одно из многих преимуществ async/await,

Еще одна вещь, которую стоит отметить, заключается в том, что ваши службы данных должны развернуть ответ, data свойство и вернуть его, чтобы ваш контроллер не интересовался семантикой службы HTTP.

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