Spaß mit “this” in TypeScript

Eigentlich war der Plan ganz simpel. Eine kleine SharePoint Hosted App, die in Typescript geschrieben wird und die Daten einer Datenbank mit WebAPI bzw. Json abfragt. Um den Entwicklungsaufwand gering zu halten verwende ich jquery und knockout. Aber wie so oft, liegt der Stolperstein nicht dort, wo man ihn erwarten würde.

In der einfachen App soll eine TypeScript Klasse als ViewModel dienen. In der TypeScript Datei habe ich ein Interface für die Datenbankeinträge geschrieben. Es macht einfach mehr Spaß wenn die Objekte typisiert sind.

Das ViewModel besteht aus einer Liste, äh sorry, in JavaScript ist es ja ein Array und einem Property, das für die Datenabfrage verwendet wird. Im Beispiel ist es das Quartal, nachdem die Daten selektiert werden sollen. Die Liste (Array) und das Property sind in der Klasse wie folgt definiert:

   1:      GoalSheetItemList: KnockoutObservableArray<IGoalSheetItem> = ko.observableArray<IGoalSheetItem>();
   2:      Quartal: KnockoutObservable<string> = ko.observable<string>('Q1');

zusätzlich gibt es eine Funktion “FetchAllItems” die die WebAPI Abfrage ausführt und das Ergebnis in die Liste schreibt:

   1:      fetchAllItems() {
   2:          var url: string;
   3:          url = "http://localhost:61070/api/GoalSheet/?Quartal=" + this.Quartal();
   4:         $.getJSON(url,
   5:             it => {               
   6:                 this.GoalSheetItemList(it);
   7:              }
   8:             );       
   9:      }
 
 

zuletzt werfen wir auch noch einen Blick auf den View. Eine simple HTML Datei mit einer Input Box die ans Quartal gebunden wird und einer Tabelle um die Daten anzuzeigen:

   1:      <p>
   2:      Quartal: <input data-bind="value: Quartal" />
   3:      </p>
   4:   
   5:      <table>
   6:      <thead>
   7:          <tr>
   8:              <th>Quartal</th>
   9:              <th>Art</th>
  10:              <th>Trainer</th>
  11:              <th>Ziel</th>
  12:              <th>Ist</th>
  13:              <th></th>
  14:          </tr>
  15:      </thead>
  16:          <tbody data-bind="foreach: GoalSheetItemList">
  17:              <tr>
  18:                  <td data-bind="text: Quartal"></td>
  19:                  <td data-bind="text: Bezeichnung"></td>
  20:                  <td data-bind="text: TrainerLogin"></td>
  21:                  <td data-bind="text: Ziel"></td>
  22:                  <td data-bind="text: Ist"></td>
  23:              </tr>
  24:          </tbody>
  25:      </table>

Der Spaß begann, als ich bei einer Änderung des Quartals die Abfrage nochmals ausführen wollte um die Daten des entsprechenden Quartals anzuzeigen. KnockOut bietet mit den Observables ja die Möglichkeit Funktionen zu hinterlegen die bei Datenänderung aufgerufen werden. Somit habe ich im Konstruktor einfach die FetchAllItems Methode beim Quartal hinterlegt:

   1:      constructor() {
   2:          this.Quartal.subscribe(this.fetchAllItems);
   3:      }

Soweit eigentlich nichts böses. Bis zum ersten Testlauf!

image

Das Objekt “this” unterstützt die Methode Quartal nicht, obwohl Quartal doch als Member der Klasse definiert wurde und die Funktion ebenfalls Member der Klasse ist.

Das Problem tritt in der FetchAllItemns-Methode auf. Wobei die Methode sauber funktioniert, wenn sie direkt aufgerufen wird. Das Problem ist der Aufruf durch Änderung des Properties. Wird die Methode über diesen “Umweg” aufgerufen, zeigt this nicht mehr auf die Klasse!

TypeScript unterstützt jedoch auch closures. D.h. Funktionen die Zugriff auf Variablen der Umgebung haben. Formuliere ich die Zuweisung des Aufrufs der FetchAllItems Funktion als Closure, dann zeigt this in der FetchAllItems-Methode weiterhin auf die Klasse.

Mein Konstruktor muss also wie folgt geschrieben werden:

   1:      constructor() {
   2:          this.Quartal.subscribe((newVal) => {
   3:              this.fetchAllItems();
   4:          });
   5:      }

Die Art und Weise, wie im Konstruktor der Aufruf der “Quartal-Changed” Methode formuliert wird, entscheidet was in der Methode “this” ist.

Fun mit TypeScript Smiley

Kommentare sind geschlossen