Blocking on a javascript Promise.

Vote:
 

I've a PropertyList where editors can select a further list of items, and in Edit Mode, it displays multiple [object Object] output, so I created a Dojo script to inspect and replace that output with a page title. My problem is I'm using 

"epi-cms/core/PermanentLinkHelper"

which takes a URL (which I have already) and returns something like a javascript promise (perhaps dojo promise?).

define([
    "dojo/_base/array", "dojo/_base/declare",
    "dojo/_base/lang", "dojo/Deferred", "epi/dependency",
    "epi-cms/contentediting/editors/CollectionEditor",
    "epi-cms/core/PermanentLinkHelper",
],
    function (
        array, declare, lang, Deferred, 
        dependency, CollectionEditor, PermanentLinkHelper
    ) {
        async function awaitResolveContentData(urlStr) {
            let result = await PermanentLinkHelper.getContent(urlStr);
            console.log("awaiting : " + JSON.stringify(result));
            return result;
        };

        return declare([CollectionEditor], {            
            _getGridDefinition: function () {
                var that = this;
                var result = this.inherited(arguments);

                // where 'sectionItems' is the problematic collumn
                result.sectionItems.formatter = function (value) {
                    console.log("before Resolve " + value[0].link);
                    var r = awaitResolveContentData(value[0].link);
                    console.log("after Resolve " + JSON.stringify(r));
                };
                return result;
            }
        });
    });

which then outputs....

:90 before Resolve /link/fe1e795f89a5435d9fecbf7fc84fc7f8.aspx
:92 after Resolve {}
:30 awaiting : {"isPreferredLanguageAvailable":true, [... other object data snipped ..]}

...which demonstrates the promise is resolving AFTER the value is actually required. the "awaiting" is returning all the data I need, but too late :-(

has anyone encountered this issue before? how do I block on this promise? all the javascript guru's tell me once you're in 'asynchronous' mode that javascript can't return to synchronous mode very easily, because even implementing a 'while' loop to wait for the promise would block the promise and wont give it an oppurtunity to resolve the promise anyway. So I presume I need to restructure my code somehow? any help would be appreciated.

#201445
Edited, Feb 19, 2019 15:57
Vote:
 

so too partially answer my own question, I found an article... (https://gregwiechec.com/2015/12/propertylist-with-images/)

"Unfortunately, grid formatters doesn’t allow to return deferred results from formatters, so code that returns promise won’t work"

my code is returning a promise. :-(

#201472
Feb 20, 2019 10:23
Vote:
 

Answering my own question, had to change my approach, as you cant use Dojo in this instance to load content, you load the content in c# and add it as metadata which Dojo can then read. Article I posted above explains most of what you need. Here is a quick summary of what I see as the important bits:

Making extra data available to Dojo:

public class PropertyListUrlExtenderAttribute : Attribute, IMetadataAware
{
    var extendedMetadata = (ExtendedMetadata)metadata;
    /* extendedMetadata.Model and extendedMetadata.Parent.Model *may* contain model data */

     var urlCollection = new List<PropertyListItem>();
    /* 
        snip, fill urlCollection 
        where class PropertyListItem { string Id { get; set; } string Url { get; set; } }
    */
    if (!extendedMetadata.EditorConfiguration.ContainsKey("mappedUrls"))
    {
        extendedMetadata.EditorConfiguration.Add("mappedUrls", urlCollection);
    }
}

in the above code, extendedMetadata DID NOT contain model data, and instead we save data in a cookie (the ID of the current model), which was used when filling urlCollection with data from the current model (load cookie, read content ID, load content from db). remember the whole initial problem was that we could not load content about the current model from Dojo without using asynchronous code. The data was stored via the block's controller (in a cookie), and ONLY when in editmode.  if (PageEditing.PageIsInEditMode) { /* store currentBlock's ContentLink.ID in a cookie*/ }

Reading the extra data using Dojo (and using _getGridDefinition to display extra data):

define([
    "dojo/_base/array",
    "dojo/_base/declare",
    "dojo/_base/lang",
    "epi-cms/contentediting/editors/CollectionEditor",
    "app/editors/extendedFormatters"
],
    function (
        array,
        declare,
        lang,
        CollectionEditor,
        extendedFormaters
    ) {
        console.log("ExtendedCollectionEditor");

        return declare([CollectionEditor], {
            _getGridDefinition: function () {
                var result = this.inherited(arguments);
                console.log('Loaded...');                

                if (this.mappedUrls != null && this.mappedUrls != undefined) {
                    for (var i = 0; i < this.mappedUrls.length; i++) {
                        if (this.mappedUrls[i].id != null && this.mappedUrls[i].url != null) {
                            extendedFormaters.setMapping(this.mappedUrls[i].id, this.mappedUrls[i].url);
                        }
                    }

                    result.link.formatter = extendedFormaters.urlFormatter;
                }

                return result;
            },

            onExecuteDialog: function () {
                var item = this._itemEditor.get("value");
                
				if (this._editingItemIndex !== undefined) {
					this.model.saveItem(item, this._editingItemIndex);
				} else {
					this.model.addItem(item);
				}                
            }
        });
    });
	
------------------extendedFormaters.js---------------------------------

define([
    // dojo
    "dojo/_base/lang",
    "dojo/Deferred"
],
    function (
        // dojo
        lang,
        Deferred       
    ) {        

        var elementUrls = {};

        var extendedFormatters = {

            urlFormatter: function (value) {

                if (!value) {
                    return '-';
                }
                if (!elementUrls[value]) {
                    return value;
                }

                return elementUrls[value];
            },

            setMapping: function (contentLink, url) {
                elementUrls[contentLink] = url;
            }
        }

        return extendedFormatters;
    });

in the javascript above, _getGridDefinition is where the magic happens, it's a function that is called to format the contents of the propertyList (so we replace that function), and in result.Link above, Link refers to a property on our EpiServer block, so you would have named your property you're trying to style differently probably. 

#201888
Edited, Mar 06, 2019 11:24
Vote:
 

Hi Noel,

I think this is virtually the same problem I had and the same solution I came to here: https://jakejon.es/blog/showing-the-friendly-url-of-a-content-reference-or-url-property-in-a-propertylist

It's worth mentioning that Episerver don't officially support using a Url in a PropertyList so a block is normally the preferred approach.

/Jake

#201903
Mar 06, 2019 15:49
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.