thisの扱いに要注意!!
removeSeatメソッドの箇所で、以下のエラーメッセージが表示。
app.ts:42 Uncaught TypeError: Cannot read property 'remove' of undefined
removeSeat = (seat: SeatReservation) => { this.seats.remove(seat); }
原因
clickイベントに登録されたメソッド(ここでは、removeSeat
のこと)のthisは、イベントの発生源のオブジェクトを参照してしまうことが原因でした。
対策
そこで、上記のメソッドをプロパティに置き換え、アロー関数を使って初期化する方法を採用しました。
removeSeat = (seat: SeatReservation) => { this.seats.remove(seat); }
このようにすることで、下記コードのように、コンパイルされたJavaScript結果は、 thisを「_this」に退避 していることがわかります。 これにより、clickイベントに登録されたメソッドであっても、thisが期待値どおり「this.seats」を指し示すことができます。
var ReservationsViewModel = (function () { function ReservationsViewModel() { var _this = this; this.seats = ko.observableArray(); // Non-editable catalog data - would come from the server this.availableMeals = [ { mealName: "Standard (sandwich)", price: 0 }, { mealName: "Premium (lobster)", price: 34.95 }, { mealName: "Ultimate (whole zebra)", price: 290 } ]; this.removeSeat = function (seat) { _this.seats.remove(seat); }; this.seats.push(new SeatReservation("Steve", this.availableMeals[0])); this.seats.push(new SeatReservation("Bert", this.availableMeals[1])); this.seats.push(new SeatReservation("Tom", this.availableMeals[2])); } // Operations ReservationsViewModel.prototype.addSeat = function () { this.seats.push(new SeatReservation("", this.availableMeals[0])); }; return ReservationsViewModel; })();
- ソースコード全文を以下に記載します
/// <reference path="scripts/typings/knockout/knockout.d.ts" /> // Class to represent a row in the seat reservations grid class SeatReservation{ meal: KnockoutObservable<availableMeals> = ko.observable<availableMeals>(); constructor(public name: string, item: availableMeals) { this.meal(item); } } interface availableMeals{ mealName: string; price: number; } // Overall viewmodel for this screen, along with initial state class ReservationsViewModel { seats: KnockoutObservableArray<SeatReservation> = ko.observableArray<SeatReservation>(); // Non-editable catalog data - would come from the server availableMeals = [ { mealName: "Standard (sandwich)", price: 0 }, { mealName: "Premium (lobster)", price: 34.95 }, { mealName: "Ultimate (whole zebra)", price: 290 } ]; constructor() { this.seats.push(new SeatReservation("Steve", this.availableMeals[0])); this.seats.push(new SeatReservation("Bert", this.availableMeals[1])); this.seats.push(new SeatReservation("Tom", this.availableMeals[2])); } // Operations addSeat() { this.seats.push(new SeatReservation("", this.availableMeals[0])); } removeSeat = (seat: SeatReservation) => { this.seats.remove(seat); } } window.onload = () => { var viewModel = new ReservationsViewModel(); ko.applyBindings(viewModel); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>KnockOut And TypeScript</title> <link rel="stylesheet" href="app.css" type="text/css" /> <script src="Scripts/knockout-3.4.0.js"></script> <script src="app.js"></script> </head> <body> <h2>Your seat reservations</h2> <table> <thead> <tr> <th>Passenger name</th> <th>Meal</th> <th>Surcharge</th> <th></th> </tr> </thead> <tbody data-bind="foreach: seats"> <tr> <td><input data-bind="value: name" /></td> <td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td> <td data-bind="text: meal().price"></td> <td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td> </tr> </tbody> </table> <button data-bind="click: addSeat">Reserve another seat</button> </body> </html>
参考サイト
この問題解決には、以下のサイトを拝見させていただき、解決することができました。 本当にありがとうございます。