Подтвердить что ты не робот

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?

4b9b3361

Ответ 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" Он работает нормально.

Надеюсь, это поможет, я узнал это болезненным способом.