読者です 読者をやめる 読者になる 読者になる

knockoutjsでforeachのオブジェクトを部分的に変更

knockoutjs TypeScript

今更ながらknockoutjsネタです。
最近、ASP.NET MVC + TypeScript + SignalR + knockoutjs(+Knockout-ES5)でリッチなWebアプリを開発しております。

そこで、knockoutjsを使ってて順調だったんですが、foreachで詰みました。
バインディングしたオブジェクトのプロパティの一部を更新した際にUI反映されませんでした。

HTML

<lu id="dataview" data-bind="foreach: DataList">
    <li>
        <span data-bind="text: UserID"></span>
        <span data-bind="text: UserName"></span>
    </li>
</lu>

TypeScript

class UserListItem {
    UserID: string;
    UserName: string;
    constructor(userid: string, username: string) {
        this.UserID = userid;
        this.UserName = username;
    }
}
class UserListViewModel {
    UserList: Array<UserListItem>;

    constructor() {
        this.UserList = new Array<UserListItem>();

        ko.track(this);

        //ダミーのユーザー追加処理
        this.UserList.push(new UserListItem("yamada", "山田"));
        this.UserList.push(new UserListItem("sato", "佐藤"));

        //とりあえず5秒後にUsreNameをマスクする
        setTimeout(() => {
            this.UserList[0].UserName = "●●●";
        }, 5000);
    }
}
ko.applyBindings(new UserListViewModel(), $('#dataview')[0]);

原因は、オブジェクトのプロパティを変更と検知しない挙動でした。

解決策としては、pushするオブジェクトを監視対象とする

Knockout-ES5を使用しているので、ko.observableArrayを使用していません。
ko.track(this);でViewModelを監視対象にしています。
これと同じように、オブジェクトにもko.trackを実行します。

//ダミーのユーザー追加処理
this.UserList.push(ko.track(new UserListItem("yamada", "山田")));
this.UserList.push(ko.track(new UserListItem("sato", "佐藤")));

初期化したUserListItemをそのままko.trackに食わせちゃうと。
これで、5秒後にUI反映されるようになりました。