Javascript in WebViews (statt VBScript)

Combit kann nun Chromium Browser für WebViews.

Wo gibt es eine Dokumentation, wie das Skripten nun mit Javascript funktioniert? (Ich kann Javascript, aber ich finde keine Objekte-Übersicht, Bibliothek o.ä. um die Combit Daten anzusprechen.

Wer kann hier helfen?

Das Objektmodell ist exakt dasselbe, ebenso die restlichen Konzepte. Lediglich ist der Einstiegspunkt ins Objektmodell ein anderer und Sie müssen die Methoden synchron aufrufen (await) und die Objekte müssen zu einem definierten Zeitpunkt (am besten spiegelbildlich zu ihrer Erzeugung) mittels .Dispose aufgeräumt werden. Das war es schon.

Details hierzu finden Sie in der SDK-Dokumentation im Kapitel „Info-Zentrale, Web-Ansichten, Web-Elemente, Web-Panel“ (oder im bei Ihnen installierten PDF-Dokument „Programmier-Referenz“).

Beispielcode aus der Large, Ansicht „Web-Elemente“ - Erzeugung einer Grafik aus einer Berichtsvorlage (am Beispiel einer QR-Codeerzeugung):

Ein paar Unterschiede gibts wohl schon, z.B. sind die Objekte meist gar keine, sondern Funktionen. So liefern typeof oProject und typeof(cRMView) den Typ function und nicht object.

Ich schreibe in VSCode und verwende einen Browser zum Debuggen.
Auf das Chromium webview.hostObjects habe ich wohl nur Zugriff, wenn ich den Combit-internen Browser verwende. Ein externer Browser kennt das nicht.

Auch ist mir das .Dispose unklar: wann muss ich denn Objekte „entsorgen“? Ein Chromium Browser hat eigentlich einen garbage collector …

Ich würde mir eine ausführliche Dokumentation wünschen.

Auf das Chromium webview.hostObjects habe ich wohl nur Zugriff, wenn ich den Combit-internen Browser verwende.

Das ist korrekt, bei Web-Elementen, Info-Zentrale, Web-Panel und Web-Views handelt es sich ausschließlich um Fenster innerhalb der combit CRM Programmoberfläche. Nur bei innerhalb der Programmoberfläche (und entsprechend initialisiertem Berechtigungssystem etc.) dargestellten HTML-Code besteht Zugriff auf das combit CRM COM-Objektmodell. Die „Einstiegspunkte“ (window.chrome.webview.hostObjects) werden dabei von uns „injected“. Ein prozessübergreifender COM-Zugriff von einem beliebigen Chrome-Browser auf das COM-Modell eines anderen laufenden Prozesses (combit CRM) lässt das Google-Chrome-Sicherheitsmodell überhaupt nicht zu.

So liefern typeof oProject und typeof(cRMView) den Typ function und nicht object .

Dazu kann ich ad-hoc nichts sagen, falls das wichtig ist, kann ich das mal recherchieren lassen.

Praktische Auswirkung wird es nicht haben, denn nichtsdestotrotz programmieren Sie die Geschäftslogik im Script exakt analog zu VBScript: Sie haben Objekte (aus logischer Sicht, ob typeof in JavaScript da grad was anderes sagt, lass ich jetzt mal außen vor), welche Eigenschaften und Methoden besitzen. Eigenschaften aber auch Methoden können wiederum Objekte zurückliefern usw. Die Details sehen Sie in der SDK Dokumentation (Programmierreferenz), dort gibt es auch ein Diagramm mit der gesamten Objekthierarchie.

Die Vorgehensweise ist für VBScript und JavaScript identisch (wir haben auch gar keine zwei unterschiedliche COM-Objektmodelle, es gibt nur eines) und dementsprechend sieht der Quellcode auch analog aus, sprachsyntaktische Erfordernisse wie leere Klammern bei Methodenaufrufen ohne Parameter in JavaScript versus gar keine Klammern in VBScript u. ä. mal außen vorgelassen.

wann muss ich denn Objekte „entsorgen“? Ein Chromium Browser hat eigentlich einen garbage collector …

Das stimmt, aber wann der Garbage Collector läuft, ist nicht festgelegt. Das bedeutet aber im Umkehrschluss, dass bis dahin an den abgerufenen combit CRM COM-Objekten durch das Script (vielmehr den Chromium Browser) dann solange Referenzen offengehalten werden, bis das Objekt irgendwann einmal durch den Garbage Collector dann letztenendes weggeräumt wird. Wenn Sie also in einer Ansicht ein Web-Element haben, das z.B. ein Chart darstellt, und etwaige dazu benutzte Objekte nicht .Disposed werden, sobald das Bild erzeugt wurde, dann bleiben offene Referenzen auf die Objekte und die Ansicht könnte dann vom Anwender solange nicht geschlossen werden, falls es sich um visuelle Objekte handelt, da die Anwendung ja nicht weiß, dass da kein Zugriff mehr erfolgen wird (es ist halt noch 1 Referenz „offen“ und der Garbage Collector halt noch nicht gelaufen). Außerdem räumt der Garbage Collector die Objekte nicht in umgekehrter Erstellreihenfolge auf, sondern willkürlich. Das kann bei Objekten, die in andere Objekte verschachtelt sind, zu Problemen führen, wenn das „äußere Objekt“ vor dem „inneren“ aufgeräumt wird. Um diese Probleme alle zu umgehen und definierte Zeitpunkte zu haben, braucht es die .Dispose Methode (angelehnt an .NET), um den definierten Zeitpunkt zu forcieren, an dem einem combit CRM Objekt „erklärt“ wird, dass es nicht mehr benötigt wird. Das entspricht in VBScript einem set oObjekt = Nothing (und intern einem ReleaseRef auf das dahinterliegende COM-Objekt).

Danke für die ausführlichen Infos. Ich versuche mich mit dem Umsetzen von Grundlagen, wie einen Feldwert auslesen und in der HTML-Seite anzeigen. Das geht mit:

// script.js
document.addEventListener(„DOMContentLoaded“, async () => {
let
cRM = window.chrome.webview.hostObjects.combitCRM,
cRMContext = window.chrome.webview.hostObjects.combitCRM_Context,
cRMView = cRMContext.View();

if (cRMView) {
try {
let oProject = await cRM.CurrentProject();
let oViewConfig = await oProject.ViewConfigs.ItemByName(await cRMView.Name);
let view = await cRMView.Name;
let oRecord = await cRMView.CurrentRecordSet().CurrentRecord();


  if (oRecord) {
    let value = (await oRecord.GetContentsByName("Year")) || "nothing";
    console.log(value);
    year.textContent = value
  } else {
    console.log("no record");
  }
} catch (error) {
  console.log(error);
}
} else {
console.log(„no view“);
}
});
<!-- das HTML -->
<time id="year">nothing</time>

Soweit, so gut.

Gibt es eine Systematik, wann die Combit Objekte als Methode verwendet werden und wann als Referenz? Beispiel:

let view = await cRMView.Name; // Call by Reference 
let oRecord = await cRMView.CurrentRecordSet().CurrentRecord(); // Call as a function chain`

Ist es auch möglich, Eingaben über HTML-Formulare in Combit zu speichern?

Ich versuche mich mit dem Umsetzen von Grundlagen, wie einen Feldwert auslesen und in der HTML-Seite anzeigen. Das geht mit:

Tipp: nutzen Sie GetContentsValueByName (Betonung auf Value) dann haben Sie bspw. direkt im Falle eines Datumsfeldes ein JavaScript-Datumsobjekt, anderenfalls haben Sie immer eine Zeichenkette, womit dann Lokalisierungsformatierungen u.U. zum Tragen kommen, was ich vermeiden würde, da diese ja u.U. von den Client-Einstellungen abhängig sind.

Gibt es eine Systematik, wann die Combit Objekte als Methode verwendet werden und wann als Referenz

Sie sollten eine Kaskade möglichst vermeiden. Die Methoden und Properties, die wiederum ein Objekt zurückliefern, sollten dieses dann einer Objektvariablen zuweisen und diese dann am Ende des Scopes per .Dispose() freigeben. Ansonsten wird für jedes zurückgegebene Objekt ein temporäres Objekt erzeugt, und wenn Sie dann gleich mehrfach solche Kaskaden hintereinanderweg nutzen, also bspw.

oFieldContents1 = cRMView.CurrentRecordSet().CurrentRecord().GetContentsValueByName("Feld1");
oFieldContents2 = cRMView.CurrentRecordSet().CurrentRecord().GetContentsValueByName("Feld2");
oFieldContents3 = cRMView.CurrentRecordSet().CurrentRecord().GetContentsValueByName("Feld3");

würden jedes mal wieder neue temporäre Objekte generiert, hier 6 Stück, nämlich 3 x (1 CurrentRecordSet + 1 CurrentRecord). Das ist extrem unperformant und außerdem wäre das explizite Dispose so nicht möglich, d.h. hier käme es dann ggf. auf den GarbageCollector an. (Problem dabei siehe oben.)

In Beispielcodes der Dokumentation kürzen wir das zugunsten der didaktischen Lesbarkeit als Kaskade oft ab, damit man nicht erstmal zig Zeilen an Deklarationen vor sich hat, bevor dann endlich mal die zu demonstrierende Methode/Eigenschaft ins Spiel kommt.

Ist es auch möglich, Eingaben über HTML-Formulare in Combit zu speichern?

Klar:
Beispiel für das Bearbeiten eines Datensatzes

Sofern der Kontext die Veränderung des in der Eingabemaske gerade angezeigten und bearbeiteten Datensatzes ist, dann passt Datensatzinhalte verändern während Bearbeitung besser.

1 „Gefällt mir“

Das sind Gut-zu-wissen-Grundlagen, danke! Ich arbeite mich Schritt für Schritt voran.
Das lesen von Daten aus der aktuellen Maske geht schon mal gut.

Nun stoße ich nach dem Umstellen auf den neuen Browser nach und nach auf verschiedene Artefakte:

Die Webviews, die mit VBScript geschrieben sind gehen (natürlich) nicht mehr, der neue Browser versteht ja kein VBScript.
Gibt es denn Skripte, die bereits mit dem neuen Browser funktionieren?

Der Aufruf eines XMLHttpRequests funktioniert auch nicht mehr. Die Zeile Set xmlhttp = CreateObject("MSXML2.ServerXMLHTTP.6.0") liefert kein gültiges Objekt mehr, auch wenn die Zeile in einem Skript steht, die mit Browserausgabe nichtsmehr zu tun hat.
Wie kann ich das denn nun lösen?

Welche anderen Aufrufe von Objekten mit implementierter Browseranbindung gibt es noch?

Wir arbeiten momentan noch mit Combit Version 11, wird sich das bei einer Umstellung auf die Version 12 ändern?

Alle Web-Elemente, Web-Ansichten (Kanban/Info-Board und Gantt und Ressource-View) und die Info-Zentrale der Large Solution, die mit cRM11 ausgeliefert wird, sind JavaScript-basiert und arbeiten auf WebView2 (Edge/Chromium).

image
image
image

Die Zeile Set xmlhttp = CreateObject("MSXML2.ServerXMLHTTP.6.0") liefert kein gültiges Objekt mehr

Es gibt in JavaScript keine CreateObject-Funktion. Das hat nichts mit combit CRM zu tun. Das müssen Sie dann anders lösen, wenn das Teil eines Scripts sein soll, das im Kontext eines WebView2/Edge/Chromium-Fensters läuft und daher JavaScript sein muss…

Im cRM11/12 SDK gibt es für WebRequests entsprechende HTTP-Request-Methoden, die Ihnen (vor allem VBScript-Entwickler:innen) das Leben im Zusammenhang mit Webservices erleichtern. In JavaScript können Sie Webservices allerdings eh direkt aufrufen.