Angular2 Просмотр не меняется после обновления данных
Я пытаюсь обновить свой просмотр после того, как событие websocket возвращает обновленные данные.
Я ввел услугу в свое приложение и вызвал метод getData() в службе. Этот метод выдает событие socket.io на мой NodeJS-сервер, который, в свою очередь, выполняет внешний вызов api и анализирует некоторые данные. Затем сервер NodeJS передает событие успеха с новыми данными, которые я слушаю в своей службе. Когда событие успеха возвращается, я затем обновляю свое свойство в службе, на которую ссылаются в моем представлении.
Однако независимо от того, что я пытаюсь, я не могу получить данные, которые будут отображаться после обновления свойства.
Я искал несколько дней, и все, что я нахожу, это сообщения в блоге, в которых говорится, что это изменение должно быть плавным, или что мне нужно каким-то образом включить zone.js или попробовать ту же логику с помощью форм (однако я пытаюсь сделайте это без взаимодействия с пользователем). Ничто не работает для меня, и я немного расстроен.
Например:
Скажем, я получаю массив строк, который я хочу создать несортированный список с.
app.ts
import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
import {MyService} from 'js/services/MyService';
// Annotation section
@Component({
selector: 'my-app',
viewInjector: [MyService]
})
@View({
templateUrl: 'templates/my-app.tpl.html',
directives: [NgFor]
})
class MyComponent {
mySvc:MyService;
constructor(mySvc:MyService) {
this.mySvc = mySvc;
this.mySvc.getData();
}
}
bootstrap(MyComponent, [MyService]);
MyService.ts
let socket = io();
export class MyService {
someList:Array<string>;
constructor() {
this.initListeners();
}
getData() {
socket.emit('myevent', {value: 'someValue'});
}
initListeners() {
socket.on('success', (data) => {
self.someList = data;
});
}
}
мой-app.tpl.html
<div>
<h2>My List</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
Интересно, что я обнаружил, что если я включу тайм-аут в своем компоненте, который обновит какое-то произвольное свойство, которое я установил в представлении после того, как свойство someList будет обновлено из обратного вызова успеха, тогда оба значения свойств будут обновлены правильно в одно и то же время.
Например:
new app.ts
import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
import {MyService} from 'js/services/MyService';
// Annotation section
@Component({
selector: 'my-app',
viewInjector: [MyService]
})
@View({
templateUrl: 'templates/my-app.tpl.html',
directives: [NgFor]
})
class MyComponent {
mySvc:MyService;
num:Number;
constructor(mySvc:MyService) {
this.mySvc = mySvc;
this.mySvc.getData();
setTimeout(() => this.updateNum(), 5000);
}
updateNum() {
this.num = 123456;
}
}
bootstrap(MyComponent, [MyService]);
new my-app.tpl.html
<div>
<h2>My List {{num}}</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
Итак, как мне следует получить angular2, чтобы узнать, что данные изменились после события "success" без обновления какого-либо другого свойства?
Есть ли что-то, что мне не хватает с помощью директивы NgFor?
Ответы
Ответ 1
Итак, я наконец нашел решение, которое мне нравится. После ответа в этом сообщении Как обновить представление после изменения в angular2 после запуска прослушивателя событий Google я обновил myList в зоне zone.run(), и теперь мои данные обновляются на мой взгляд, как и ожидалось.
MyService.ts
/// <reference path="../../../typings/tsd.d.ts" />
// Import
import {NgZone} from 'angular2/angular2';
import {SocketService} from 'js/services/SocketService';
export class MyService {
zone:NgZone;
myList:Array<string> = [];
socketSvc:SocketService;
constructor() {
this.zone = new NgZone({enableLongStackTrace: false});
this.socketSvc = new SocketService();
this.initListeners();
}
getData() {
this.socketSvc.emit('event');
}
initListeners() {
this.socketSvc.socket.on('success', (data) => {
this.zone.run(() => {
this.myList = data;
console.log('Updated List: ', this.myList);
});
});
}
}
Ответ 2
Просто переместите инициализацию socket.io в конструктор Service, и он будет работать.
Взгляните на этот пример:
import {Injectable} from 'angular2/core';
@Injectable()
export class SocketService {
socket:SocketIOClient.Socket;
constructor(){
this.socket = io.connect("localhost:8000");
}
public getSocket():SocketIOClient.Socket{
return this.socket;
}
}
Теперь, когда вы вводите эту услугу компоненту и используете сокет, ваше представление будет автоматически обновляться. Но если вы оставите его в глобальном масштабе, как и вы, вам придется что-то взаимодействовать, чтобы заставить просмотр обновиться.
Вот пример компонента, который использует эту службу:
export class PostsComponent {
socket: SocketIOClient.Socket;
posts: Array<Post> = [];
constructor(private _socketService:SocketService){
this.socket.on('new-post', data => {
this.posts.push(new Post(data.id, data.text));
});
}
Ответ 3
Очень простой способ сделать это просто запустить в зоне любую переменную, которую вы хотите обновить.
zone.run(()=>{
this.variable = this.variable;
});
Не похоже, что это может быть так просто, но просто назначая его самому, он будет обновлять его, если запускается в зоне. Я не знаю, все еще проблема в angular2, так как я запускаю немного более старую версию.
Ответ 4
UPDATE
plunker Я связал предоставленный базовый пример, но это меня разочаровывает в том, что я не могу показать весь "рабочий пример". Поэтому я создал github repo, который вы можете снести, чтобы увидеть полный рабочий пример фрагментов, о которых я говорил ниже.
Таким образом, в вашем реальном вопросе возникли две проблемы. У вас была опечатка в исходном коде в файле "MyService.ts"
self.someList = data;//should be this.someList, self is the browser window object
Другая проблема: Angular2 не распознает изменение обнаружения так, как вы ожидаете. Если он был установлен в 'this', я все равно не думаю, что он обновил бы представление вашего компонента.
В вашем ответе он работает, но вы как бы не решаете проблему. Что вы должны реализовать, это Observable в вашей службе.
Когда вы объедините эти две функции вместе, вы можете реализовать паруса довольно прямолинейно. Я создал пример plunker, но имейте в виду, что он фактически не подключается к серверу парусов, потому что plunker требует https, и я не собираюсь чтобы купить сертификат ssl для моей локальной машины. Код действительно отражает то, как вы должны осуществлять связь с парусами в Angular2.
Основная идея может быть найдена в файле src/io.service.ts
constructor() {
this._ioMessage$ = <Subject<{}>>new Subject();
//self is the window object in the browser, the 'io' object is actually on global scope
self.io.sails.connect('https://localhost:1337');//This fails as no sails server is listening and plunker requires https
this.listenForIOSubmission();
}
get ioMessage$(){
return this._ioMessage$.asObservable();
}
private listenForIOSubmission():void{
if(self.io.socket){//since the connect method failed in the constructor the socket object doesn't exist
//if there is a need to call emit or any other steps to prep Sails on node.js, do it here.
self.io.socket.on('success', (data) => {//guessing 'success' would be the eventIdentity
//note - you data object coming back from node.js, won't look like what I am using in this example, you should adjust your code to reflect that.
this._ioMessage$.next(data);//now IO is setup to submit data to the subscribbables of the observer
});
}
}
Ответ 5
Если вам интересно, могу ли я предложить ngrx/store с OnPush
обнаружением изменений. Я столкнулся с аналогичными проблемами, когда что-то произошло за пределами Angular (что бы это ни говорило точно), и мой взгляд не отражал изменения.
Использование шаблона Redux с диспетчерами событий и единственным состоянием, которое содержит мои данные в сочетании с обнаружением изменений OnPush
, решило эту проблему для меня. Я не знаю, почему и как он решает эту проблему. Cum grano salis.
Подробнее см. этот комментарий специально для более подробной информации.
Ответ 6
У меня была такая же проблема, и проблема была:
Я использовал "angular2": "2.0.0-beta.1"
Кажется, что есть ошибка, потому что после обновления до "angular2": "2.0.0-beta.15"
Он работает нормально.
Надеюсь, это поможет, я узнал это болезненным способом.