Martin's Blog - Developer Infos zu SharePoint, ASP.NET, BI und anderen Technologien




powered by Component Software GmbH

Event-based Async-Pattern in SharePoint hosted Apps mit TypeScript

TypeScript erlaubt richtige Software-Entwicklung im JavaScript-Umfeld. Klassen, Vererbung, Interfaces und Polymorphie ist nun auch in der Webentwicklung möglich. In Verbindung mit dem SharePoint Client Object Modell kann TypeScript auch genutzt werden, da seit einiger Zeit umfangreiche Type-Definition-Dateien existieren.

Allerdings beruht das CSOM auf einer asynchronen Ausführung des Codes. Sobald versucht wird, die Datenzugriffe auf SharePoint in TypeScript Klassen zu verpacken um mehr Übersichtlichkeit im Code zu erhalten, steht man vor dem Problem ein Asynchronous Pattern (z.B: Event-based Asynchronous Pattern (EAP))  zu implementieren.

Der Kernpunkt hierfür sind Events. Allerdings müssen diese in TypeScript erst selbst implementiert werden, da die Sprache hierfür noch keine Unterstützung anbietet.

Meine Implementation eines Events baut auf einer internen Invokation-List auf. Ein Array von Funktionen mit einer simplen Signatur. Um flexibel zu bleiben, werden Generics verwendet. Somit kann ich als Parameter jedes Objekt übergeben. Dann noch drei public-Functions um Handler zu registrieren, abzumelden und um das Event auszulösen. Fertig ist meine Event-Klasse Zwinkerndes Smiley

   1:  module Events {
   2:     export  class EventClass<T> {
   3:         private functions: { (param?: T): void; }[] = [];
   4:         public add(func: { (param?: T): void; }) {
   5:              this.functions.push(func);
   6:          }
   7:   
   8:         public remove(func: { (param?: T): void; }) {
   9:              this.functions = this.functions.filter(f => f !== func);
  10:          }
  11:   
  12:         public fireEvent(param?: T) {
  13:              if (this.functions) {
  14:                  this.functions.forEach(f => f(param));
  15:              }
  16:          }
  17:      }
  18:  } 

Die SharePoint Seite

Das Ziel ist, eine typische SharePoint Aktion, wie zB einen Task in einer Taskliste zu erstellen, in eine Klasse zu verpacken. Die Liste liegt im HostWeb und um das Handling mit dem ClientContext für das Host Web einfacher zu gestalten, sind zunächst zwei Hilfsklassen sinnvoll:

   1:  class Url {
   2:      public  static getUrlVars  () : string[] {
   3:          var vars:string[] = [], hash;
   4:          var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
   5:          for (var i = 0; i < hashes.length; i++) {
   6:              hash = hashes[i].split('=');
   7:              vars.push(hash[0]);
   8:              vars[hash[0]] = hash[1];
   9:          }
  10:          return vars;
  11:      }
  12:   
  13:      public static getUrlVar (name:string):string {
  14:          return this.getUrlVars()[name];
  15:      }
  16:  }
  17:   
  18:  class SPContextHelper {
  19:      public hostContext: SP.AppContextSite;
  20:      constructor(public clientContext: SP.ClientContext) {
  21:          this.hostContext = this.getHostWebCtx(this.clientContext);
  22:      }
  23:   
  24:      private getHostWebCtx(ctx: SP.ClientContext): SP.AppContextSite {
  25:          var url = decodeURIComponent(Url.getUrlVar('SPHostUrl'));
  26:          return new SP.AppContextSite(ctx, url);
  27:      }
  28:  }

Die genaue Erklärung wie das mit dem Zugriff auf das Hostweb so läuft gibt’s hier.

Die Codezeilen um einen Task anzulegen habe ich der Klasse “TaskHelper” gekapselt. Die Feldwerte des Tasks liegen in der Klasse “Task”. Der Taskhandler legt im Konstruktor einen Verweis auf die SharePoint Liste an und initialisiert die die vorher gezeigte SPContextHelper-Klasse.

   1:  class Task {
   2:      public Beschreibung: string;
   3:      public Faellig: Date;
   4:  }
   5:   
   6:  class TaskHandler {
   7:      private contextHelper: SPContextHelper;
   8:   
   9:      private liste: SP.List;
  10:   
  11:      public onSuccess: Events.EventClass<string> = new Events.EventClass<string>();
  12:      public onError: Events.EventClass<string> = new Events.EventClass<string>();
  13:   
  14:      constructor(public listenName: string) {
  15:          this.contextHelper = new SPContextHelper(SP.ClientContext.get_current());
  16:          this.liste = this.contextHelper.hostContext.get_web().get_lists().getByTitle(listenName);
  17:      }
  18:   
  19:      public AddTaskToSharePoint(task: Task) {
  20:          var neuesItem: SP.ListItem = this.liste.addItem(new SP.ListItemCreationInformation());
  21:          neuesItem.set_item('Title', task.Beschreibung);
  22:          neuesItem.set_item('DueDate', task.Faellig);
  23:          neuesItem.update();
  24:   
  25:          this.contextHelper.clientContext.executeQueryAsync(
  26:              (sender, args) => { this.onSuccess.fireEvent('its OK');},
  27:              (sender, args) => { this.onError.fireEvent(args.get_message());}
  28:              );
  29:      }
  30:  } 

In Zeile 11 und 12 werden die beiden Event onSuccess und onError angelegt. Es sind public Objekte um werden mit dem Type “string” angelegt. D.h. die Message des Events ist ein simpler String.

In Zeile 26 und 27 sieht man das Auslösen des Events. Das sind die beiden Rückruf-Funktionen der executeQueryAsync-Methode des Client Context.

Der App-Code

Zu guter Letzt sollte dieser Code nun auch in der App verwendet werden.

Die TaskHandler-Instanz wird global initialisiert. In der Document-Ready Funktion werden die beiden EventHandler, OKHandler und ErrorHandler an die beiden Events gehängt.

In der Funktion “addTask” wird dann nur noch die “AddTaskToSharePoint”-Methode des Handlers aufgerufen.

   1:  var taskHandler: TaskHandler = new TaskHandler("Aufgaben");
   2:  $(document).ready(function () {
   3:      taskHandler.onSuccess.add(OKHandler);
   4:      taskHandler.onError.add(ErrorHandler);
   5:  });
   6:   
   7:  function OKHandler(txt: string) {
   8:      alert(txt);
   9:  }
  10:   
  11:  function ErrorHandler(txt: string) {
  12:      alert(txt);
  13:  }
  14:   
  15:  function addTask() {
  16:      var titel: string = $('#txtBeschreibung').val();
  17:   
  18:      var t: Task = new Task();
  19:      t.Beschreibung = titel;
  20:      taskHandler.AddTaskToSharePoint(t);
  21:  } 
Kommentare sind geschlossen

Copyright © 2019 Martin Groblschegg - Impressum