web-dev-qa-db-ja.com

データが更新された後、Angular2ビューが変更されない

Websocketイベントが更新されたデータを返した後、ビューを更新しようとしています。

アプリにサービスを挿入し、サービスでgetData()メソッドを呼び出します。このメソッドは、socket.ioイベントをNodeJSサーバーに送信します。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;
        });
    }
 }

my-app.tpl.html

<div>
    <h2>My List</h2>
    <ul>
        <li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
    </ul>
</div>

興味深いことに、someListプロパティが成功コールバックから更新された後にビューに設定した任意のプロパティを更新するタイムアウトをコンポーネントに組み込むと、両方のプロパティ値が同時に正しく更新されることがわかりました。

例えば:

新しい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]);

新しいmy-app.tpl.html

<div>
    <h2>My List {{num}}</h2>
    <ul>
        <li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
    </ul>
</div>

では、他のプロパティを更新せずに「success」イベント後にデータが変更されたことをangular2に認識させるにはどうすればよいですか?

NgForディレクティブの使用で不足しているものはありますか?

29
John Gainfort

だから私は最終的に私が好きな解決策を見つけました。この投稿の答えに従って Googleイベントリスナーが起動した後、angular2を変更した後にビューを更新する方法 zone.run()内でmyListを更新しました。

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);
            });
        });
    }
 }
35
John Gainfort

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));
        });  
}  
5
Master Kwoth

これを行う非常に簡単な方法は、更新したい変数をゾーンで実行することです。

zone.run(()=>{
    this.variable = this.variable;
});

それはそれほど簡単ではないように見えますが、それを自分自身に割り当てるだけで、ゾーンで実行されている場合は更新されます。私は少し古いバージョンを実行しているため、これがまだangular2の問題であるかどうかはわかりません。

[〜#〜] update [〜#〜]

plunker リンクは基本的な例を提供しましたが、「実際の例」全体を表示できなかったことにイライラします。そこで私は github repo を作成しました。これをプルダウンして、以下で説明した部分の完全な実例を見ることができます。


あなたの実際の質問には2つの問題がありました。 「MyService.ts」ファイルの元のコードにタイプミスがありました

self.someList = data;//should be this.someList, self is the browser window object

もう1つの問題は、Angular2が 変更検出 を認識していないことです。 「これ」に設定されていた場合、コンポーネントビューが更新されたとは思わない。

あなたの答えでは、それは動作しますが、あなたは問題を間違った方法で回避しているようなものです。実装する必要があるのは、サービスの Observable です。

これら2つの機能を組み合わせると、かなり簡単にセイルを実装できます。サンプルを作成しました 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
    });
  }
}
1
Dan Simon

私は同じ問題を抱えていましたが、問題は次のとおりでした:

私は「angular2」を使用していました:「2.0.0-beta.1」「angular2」に更新した後:「2.0.0-beta.15」が正常に動作しているため、バグがあるようです。

私はそれが助けになることを願っています、私はそれを痛みを伴う方法で学びました

0
Alan Wagner

興味のある方は、OnPush変更検出で ngrx/store を使用することをお勧めします。 Angular(それが正確に何を意味するにせよ)以外で何かが発生し、私の見解が変更を反映しなかった同様の問題に遭遇しました。

イベントディスパッチャとOnPush変更検出と組み合わせてデータを保持する単一の状態でReduxパターンを使用すると、この問題を解決できました。ただし、この問題をなぜまたはどのように解決するかはわかりません。兼グラノサリス。

詳細については、 このコメント を参照してください。

0
dchacke