Error on Javascript component.render() (TypeError: Cannot read property 'remove' of undefined)

Mark LMark L ✭✭✭✭
edited June 29, 2020 in Questions
I have a Queue component that I'm looking to re-render because I sorted the model data (via javascript sort function)

I'm just using .render() on the component to achieve this. It works fine for one of my Queue components, but not on the other. It's very perplexing.

I'm getting the error:
TypeError: Cannot read property 'remove' of undefined

The Queue is rendered and visible on the screen.

Any ideas as to why this would work for one Queue component but not the other? Any thoughts on this specific error message?

Thanks!

Comments

  • Mark LMark L ✭✭✭✭
    edited June 29, 2020
    I'm running SKUID version  12.1.7 and API v1

    Here's the XML for a simple example page where this error occurs when trying to re-render the Queue component. This should reproduce the error:

    <skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="false" showheader="false">  <models> <model id="QueueModel1" limit="20" query="true" createrowifnonefound="false" datasource="Ui-Only" processonclient="true">  <fields>   <field id="Index" displaytype="DOUBLE" ogdisplaytype="TEXT" precision="9" scale="0"/>  </fields>  <conditions/>  <actions/> </model> </models>  <components> <buttonset model="QueueModel1" uniqueid="sk-sPB-996">  <buttons>   <button type="multi" label="Sort Model" uniqueid="sk-sPB-1003">    <actions>     <action type="custom" snippet="SortModel"/>    </actions>   </button>   <button type="multi" label="Rerender Queue" uniqueid="sk-sPB-1007">    <actions>     <action type="custom" snippet="RerenderQueue1"/>    </actions>   </button>  </buttons> </buttonset> <queue model="QueueModel1" tagrendertype="template" searchbox="false" tokenizesearch="true" showsearchbydefault="false" hideheader="true" hidefooter="true" uniqueid="sk-sOu-839" title="Queue 1" searchmethod="server">  <rendertemplate>{{Index}}</rendertemplate>  <interactions>   <interaction type="tap"/>  </interactions> </queue> </components>  <resources>   <labels/>   <javascript> <jsitem location="inlinesnippet" name="SortModel" cachelocation="false">var params = arguments[0],  $ = skuid.$;  console.log(params);  params.model.data.sort((a, b) =&gt; (a.Index &gt; b.Index) ? 1 : -1);</jsitem> <jsitem location="inlinesnippet" name="RerenderQueue1" cachelocation="false">var params = arguments[0],  $ = skuid.$;  skuid.component.getById('sk-sOu-839').render(); </jsitem> </javascript>   <css/>   <actionsequences uniqueid="sk-WoY-562"> <actionsequence id="e68e38e5-fea6-4696-9bf9-446baa4c4fbc" label="PageRendered" type="event-triggered" event-scope="component" event-name="page.rendered">  <description/>  <actions>   <action type="createRow" model="QueueModel1" appendorprepend="append" defaultmodefornewitems="edit" affectedrows="context">    <defaults>     <default valuesource="fieldvalue" field="Index" enclosevalueinquotes="false" value="2"/>    </defaults>   </action>   <action type="createRow" model="QueueModel1" appendorprepend="append" defaultmodefornewitems="edit" affectedrows="context">    <defaults>     <default valuesource="fieldvalue" field="Index" enclosevalueinquotes="false" value="1"/>    </defaults>   </action>   <action type="createRow" model="QueueModel1" appendorprepend="append" defaultmodefornewitems="edit" affectedrows="context">    <defaults>     <default valuesource="fieldvalue" field="Index" enclosevalueinquotes="false" value="3"/>    </defaults>   </action>  </actions> </actionsequence> </actionsequences>  </resources>  <styles>   <styleitem type="background" bgtype="none"/>  </styles> </skuidpage> 
  • Mark LMark L ✭✭✭✭
    edited June 29, 2020
    I've discovered what's going on here. I believe this to be a bug.

    If you keep the "Hide Header" option for the queue unchecked, the .render() works fine, however if you have "Hide Header" for the queue checked, you get the error.
  • Luzie BaumgartLuzie Baumgart 🛠️ 
    edited June 29, 2020
    Hey Mark, thank you for providing Skuid version, page version (V1) and page XML, that was very helpful for us to reproduce the issue. We can confirm that enabling/disabling “Hide Header” in the Queue component properties makes the difference here. I’ll inform the dev team about it. There is a workaround, you could hide the Queue header with CSS (and disable the option to “Hide Header” in the Queue component properties). That way, the header will be hidden and you won’t run into the error message. If you want to hide the header of all Queue components on the page, you only need this CSS:

    .nx-queue-header {
        display:none;
    }

    In case you only want to hide the header of a specific Queue component, put the Queue component in a Wrapper component and add a CSS class to the Wrapper, e.g. "HideHeader", then use the following CSS:


    .HideHeader .nx-queue-header {
        display:none;
    }

    Note: We use a Wrapper component around the Queue component here, because if you assign the CSS class to the Queue component directly, it seems to affect only the Queue body, but not its header.

    We hope that this is helpful for you!
  • Mark LMark L ✭✭✭✭
    edited March 5, 2020
    Thanks Luzie!
  • Arne-Per HeurbergArne-Per Heurberg ✭✭✭✭
    edited June 29, 2020
    i am running into roughly the same thing getting error:skuid__SharedRuntimeJS:2 TypeError: Cannot read property 'render' of undefined regardless of which component it is attached to. An action on model query fires before the components are rendered.  
  • Mark LMark L ✭✭✭✭
    edited June 21, 2020
    If the component isn't visible on the screen you'll also get this error. Sometimes even you'll need to give it more time than just running the code at the same time as it appears on the screen.

    Because of this I've created a function called "waitForElement" that waits until an element is on the screen before running an action.

    // skuid.custom.waitForElement(fparams,callback)
    // wait for an element to be ready on the screen before performing the callback function
    // fparams = {
    //  initEle: function to initialize the element / check if the element is initialized,
    //      should return the initialized element. Takes context as a variable.
    //  context: passed context or defaults to window
    //  chunkTime: time between chunks, defaults to 250
    // }
    skuid.custom.waitForElement = function (fparams, callback) {
        fparams = fparams || {};
        const initEle = fparams.initEle || undefined;
        const element = fparams.element || undefined;
        const context = fparams.context || window;
        const chunkTime = fparams.chunkTime || 250;
        
        function doChunk () {
            let ele;
            if (initEle === undefined && element !== undefined) {
                ele = element;
            } else if (initEle !== undefined) {
                ele = initEle.call(context);
            }
            
            if (typeof ele !== 'undefined') {
                let error = false;
                try {
                    callback.call(context, ele);
                } catch (err) {
                    error = true;
                    console.log('Still waiting for element.. Error: ${err}');
                }
                
                if (!error) {
                    console.log('Wait for element complete.');
                    clearInterval(ourInterval);
                }
            } else {
                console.log('Still waiting for element..');
            }
        }
        
        // Keep trying to get the element and perform the callback until there isn't an error
        const ourInterval = setInterval(doChunk, chunkTime);
    };
    I would recommend only calling this function when the element is about to be on the screen, otherwise if you do it on let's say page render but the element is hidden behind a tab it will just keep running the waitforelement code over and over until it gets to the tab, better to put it as an action on the tab in that instance.
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!