Выражение 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.