TTMSFNCTileList.Items not recognized by TMSFNCPersistence.FromJSON in JavaScript

Hello,

I’m following up to let you know that I was able to deserialize the JSON in an editor by using the command RegisterClass(TTMSFNCTileListItem). However, I am encountering the same issue when working with a IntraWeb server that is fed by JavaScript code. It seems that deserialization of TTMSFNCTileListItems is also problematic in this context.

Could you please advise me on a temporary workaround for this issue?
I am sharing the code that allows me to perform the deserialization.

PrepareWidget_JsonCollection(AWidget,AJsonObj,ACollectionName) {
if(!AWidget)
return;
if(!AJsonObj)
return;
if(!AJsonObj[ACollectionName])
return;
if(!ACollectionName)
return;

    const collection = AWidget["F" + ACollectionName];
    if(!collection)
        return;

    let item = null;
    collection.Clear();
    for(let jsonItem of AJsonObj[ACollectionName]) {
        item = collection.Add();
        pas["WEBLib.TMSFNCTypes"].TTMSFNCObjectHelper.FromJSON.call(item,JSON.stringify(jsonItem));
    }
}

Thank you in advance for your help.

Best regards.

Can you specify exactly which error you get? Also, the idea is to serialize the component, but as I see you are serializing each item individually?

Hello, here is what I notice:

I was able to render the items in a VCL application thanks to the instruction RegisterClass(TTMSFNCTileListItem); otherwise, as soon as I made a modification to the component, it disappeared.

However, on a Web Client, I notice that the script concerning IWLabel.TextEx does contain the item collections and the JSON provided to IWLabel.TextEx does include the collections too, but we see that the result is simply the component with its background color and without the items.

Here is the JSON provided to IWLabel.TextEx before deserialization:

jsoncardweb.txt (14.0 KB)

We can clearly see that the item collection is present in the JSON.
And here is the IWLabel.TextEx script available in the DOM:

DOM TileList.txt (16.3 KB)

Is there a collection setting I forgot to configure? Is there an issue with the Canvas?

Thanks in advance.

MENANT Didier

Can you try using BeginUpdate/EndUpdate around the code to update the tile list?

This is exactly what I did in my Delphi code before sending the JSON to the JavaScript :

procedure TIWUtilShow.TreeShow_SetShapeFNC_Card(ANode: TIWCGJQRegion; AItem: TSLideItem; AList: TStringlist);  // V10.101(SN)
var
  Card:                  TTMSFNCTileList;
  DataList:              TList<Integer>;
  i, RecordCount, IdRef: Integer;
begin
  try
    if not assigned(ANode) then
      exit;
    if not assigned(AItem) then
      exit;
    if not AItem.Data.PrepareCdsDataExt then
      exit;

    // V10.101(SN) Récupération du FNC
    Card := ANode.Owner.FindComponent(ANode.Name + '_FNCControl') as TTMSFNCTileList;
    if not assigned(Card) then
      exit;

    RecordCount := AItem.Data.CdsDataExt.RecordCount;

    // V10.101(SN) Affectation des valeurs (autant de cadres que de RecordCount)
    Card.BeginUpdate;
    try

      // V10.101(SN) Supprimer les cadres en trop
      while Card.Items.Count > RecordCount do
        Card.Items.Delete(Card.Items.Count - 1);

      // V10.101(SN) Ajouter les cadres manquants
      while Card.Items.Count < RecordCount do
        Card.Items.Add;

      // V10.101(SN) Scroll entre les pages
      Card.NavigationMode := tlnmScroll;

      // V10.101(SN) Récpération des ID des CpRef
      DataList := TList<Integer>.Create;
      try

        AItem.Data.CdsDataExt.First;
        while not AItem.Data.CdsDataExt.Eof do begin
          try
            IdRef := AItem.Data.CdsDataExt.FieldByName('ID').AsInteger;
            DataList.Add(IdRef);
          finally
            AItem.Data.CdsDataExt.Next;
          end;
        end;

        for i := 0 to DataList.Count - 1 do begin
          IdRef := DataList[i];

          Card.Items[i].Title       := RemplaceBaliseMulti(AItem, IdRef, Card.Items[i].Title);
          Card.Items[i].Description := RemplaceBaliseMulti(AItem, IdRef, Card.Items[i].Description);
          Card.Items[i].Badge       := RemplaceBaliseMulti(AItem, IdRef, Card.Items[i].Badge);
          Card.Items[i].ColumnSpan  := 1;
          Card.Items[i].RowSpan     := 1;

        end;

      finally
        FreeAndNil(DataList);
      end;

    finally
      Card.EndUpdate;
    end;

    // V10.101(SN) Dessin
    TreeShow_DrawShapeFNC(ANode, AItem, AList, 'TVmFNCTileListFactory');

  except
    on E: Exception do
      CT_ERR.PushErrorAndSave(self.Classname + '.TreeShow_SetShapeFNC_Card > ' + E.Message);
  end;
end;

and in the main function of my JavaScript :

LoadWidget(AHtmlId,ANode,AJson,AInitObj) {
        try {
            if(!RtlHasBeenRun)
                FNC_TryRunRTL();
        
            if(!this.PrepareWidget_Create(AHtmlId)) 
                return null;

            const widget = form[AHtmlId];
            const container = form[AHtmlId + '_DivContainer'];

            widget.IWRefreshDt = Date.now();
            widget.IWNode = ANode;
            widget.DataLoaded = AInitObj.DataLoaded;
            widget.Enabled = AInitObj.Enabled; // V10.101(SN)
            widget.RefreshEnCours = true;

            widget.BeginUpdate();
            try {
                this.PrepareWidget_Json(widget,AJson);
                this.PrepareWidget_Position(widget,container);
                this.PrepareWidget_Events2(widget);
                this.PrepareWidget_Style(widget,AInitObj);
                this.LoadValue_Enabled(widget,AInitObj);
                this.LoadValue_Action(widget, AInitObj); // V10.101(SN)
            } finally {
                widget.Invalidate();
                widget.EndUpdate();
                widget.RefreshEnCours = false;
            }
        } catch(E) {
            console.error(E);
        }
    }

and here is the code for loading the values ​​into the items :

class TVmFNCTileListFactory extends TVmFNCBaseFactory {
    collectionList = ["Items"];

    LoadValue_General(AWidget, AValueObj) {
        if(!AWidget || !AValueObj || !AValueObj.Items)
            return;

        // V10.101(SN) Suppression des items supplémentaires
        while (AWidget.FItems.GetCount() > AValueObj.Items.length)
            AWidget.FItems.RemoveItem(AWidget.FItems.FList[AWidget.FItems.GetCount() - 1]);

        // V10.101(SN) Ajout des items manquants
        while (AWidget.FItems.GetCount() < AValueObj.Items.length)
            AWidget.FItems.Add();

        // V10.101(SN) Affectations des valeurs
        for(let i = 0; i < AValueObj.Items.length; i++) {
            let itm = AWidget.FItems.FList[i];
            let data = AValueObj.Items[i];

            itm.FTitle = data.Title || "";
            itm.FDescription = data.Description || "";
            itm.FBadge = data.Badge || "";

            itm.FColumnSpan = data.ColumnSpan || 1;
            itm.FRowSpan = data.RowSpan || 1;
        }

        // V10.101(SN) Met à jour l'affichage
        AWidget.FItems.Update(AWidget.FItems);
    }
}

According to this code

collection.Clear();
    for(let jsonItem of AJsonObj[ACollectionName]) {
        item = collection.Add();
        pas["WEBLib.TMSFNCTypes"].TTMSFNCObjectHelper.FromJSON.call(item,JSON.stringify(jsonItem));
    }

You are trying to load JSON into the collection item, which collection is this? Is this the tile list collection? Note that the JSON is the full component JSON meaning it should be loaded directly at tile-list root level (TMSFNCTileList1.JSON). If you only want to persist the collection, use TMSFNCTileList1.Items.JSON

This code allows me to update the AWidget.FItems collection with the items present in the JSON transmitted by the Delphi code, looping through each item in this collection. Am I doing something wrong? This code works fine on a TTMSFNCWidgetMultiProgress for CircleItems, for example.

Thank you.

It's unclear without some kind of test application demonstrating the issue. The JSON I noticed in the DOM is the JSON of the whole tile-list. Meaning, you need to assign this JSON to the tile-list, not the collection. if you only want to persist the collection, that's possible too, but you need to save the initial JSON starting from the Items collection by doing TMSFNCTileList1.Items.JSON. I can't see a reason why the tile-list is blank, is the collection filled with items after restoring it?

Hello, the code mentioned above works perfectly for other FNC objects with collections
in a web application; it allows a collection to be assigned to the widget variable.

Thus, I can clearly see the four items in the script of an IWLabel in the DOM. I've attached the DOM composition. It seems there's an issue with the canvas of this FNC object; it doesn't respond to the 'visibility: hidden' instruction, for example.

Do you have any insight into this issue?

Thank you for your time.

DOM Content.txt (16.6 KB)

Could you perhaps throw a little sample together?

I couldn't be more explicit: to display the items in a VCL application, I had to use a RegisterClass(TTMSFNCTileListItem) during initialization.

Then, in a web application (IntraWeb), I noticed that my display variables contained the correct number of items (for example, 1 item here) but weren't displayed. I'd like to know if there was a problem with the canvas drawing these items. Indeed, in a WebCore project, the items appear correctly.

I'm therefore providing you with a screenshot of the expected result (a functional VCL application) and a screenshot of the current web result (a non-functional one). I've examined the content of my display, and it seems correct, but if you see anything abnormal in the DOM, please let me know.

DOM Content IntraWeb.txt (14.7 KB)

So the JSON is sent from VCL to WEB? or does each app has it's own persistence mechanism?

The JSON is indeed sent from VLC to the Web except that the collections are not correctly transmitted, hence the overhead of retrieving the items from the desired collection and this in JavaScript.

Do you get JavaScript errors? I've tested this here and seen some issues restoring JSON, something related to the HTML5 canvas. I've yet to investigate deeper into the issue.

I've created a mini WebCore project in which I want to pass the JSON from the first TileList to the second using a button. I'm sending you the project; it seems there's an issue in the WEBLib.Graphics unit.

Thank you.

TileLists WebCore Deserialization.zip (2.2 MB)

Thanks for the sample, we are able to reproduce this and are looking for a solution. Stay tuned.

There seems to be an issue restoring properties in TMS WEB Core, we'll need to allocate more time to investigate. The sample can be fixed by using the following code

procedure TForm1.TMSFNCTileList2CanLoadProperty(Sender, AObject: TObject;
  APropertyName: string; APropertyType: TTypeKind; var ACanLoad: Boolean);
begin
  ACanLoad := (TTMSFNCUtils.IndexOfTextInArray(APropertyName, TTMSFNCPersistence.ExcludeProperties) = -1);
  ACanLoad := ACanLoad and not (TTMSFNCUtils.IndexOfTextInArray(APropertyName, ['ElementPosition', 'ElementID', 'Left', 'Top']) <> -1);
end;

We have meanwhile fully addressed the issue. The latest version of TMS FNC Core should have the fixes included.

Hello, thank you for your patience and perseverance.

Best Regards,

MENANT Didier

Hello,

I'm getting back to you regarding the serialization of the TMSFNCTileList component. The problem I'm finally encountering, and which I've just discovered, is the following: serializing TileList2 from TileList1 doesn't work if TileList2 doesn't initially contain any items. Could you please suggest a solution to make it work despite this constraint?

Best regards.

MENANT Didier