locked
Getting "NotFoundError" when using WinJS Repeater a second time

    Question

  • Hi,

    I have a repeater control on a page and when I try to use it a second time (to refresh the data) it throws a "NotFoundError":

    Unhandled exception at line 27148, column 25 in ms-appx://microsoft.winjs.2.0/js/ui.js
    0x800a139e - JavaScript runtime error: NotFoundError

    Does anyone know why it throws the error and what can I do to get the repeater to work properly?

    I have managed to replicate the problem on two different PCs:
    - Create a new navigation app and replace the following:

    The home.js file:

    (function () {
        "use strict";
    
        WinJS.UI.Pages.define("/pages/home/home.html", {
            ready: function (element, options) {
                createRepeater();
    
                var resetButton = WinJS.Utilities.query(".reset")[0];
                resetButton.addEventListener("click", function () {
                    console.log("Reset button pressed.");
                    createRepeater();
                });
            }
        });
    
        function createRepeater() {
            console.log("Calling 'createRepeater'.");
    
            var repeater = WinJS.Utilities.query(".repeater")[0].winControl;
            var template = WinJS.Utilities.query(".template")[0];
    
            if (repeater)
                console.log("Repeater controller exists.");
    
            if (template)
                console.log("Template exists.");
    
            var data = new WinJS.Binding.List();
    
            for (var i = 0; i < 15; i++) {
                data.push({ text: "My text " + i });
            }
    
            console.log("Data created.");
    
            repeater.template = template;
            repeater.data = data;
        }
    
    })();


    The <body>-tag in the home.html file:

    <body>
        <!-- The content that will be loaded and displayed. -->
        <div class="fragment homepage">
            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to App14!</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
                <button class="reset">Reset repeater</button>
                <div class="template" data-win-control="WinJS.Binding.Template">
                    <div data-win-bind="textContent: text"></div>
                </div>
                <div class="repeater" data-win-control="WinJS.UI.Repeater"></div>
            </section>
        </div>
    </body>


    Any help is appreciated.

    The repeater documentation: WinJS Repeater

    Wednesday, November 20, 2013 9:42 AM

Answers

  • thanks for your clear instructions. Offhand, I don't see anything wrong with your code, so I'm checking with the WinJS team. The crash is happening down in ui.js in a function called _unloadRepeatedDOM, where it looks to be attempting to remove the repeater's first child node twice, hence the "not found" error. There is the suspicious code "id (!!shouldDisposeElements)" with the double !! that could be the bug.

    In the meantime, you can workaround the problem by creating a new Repeater control instead of replacing the data on the existing one. Do this by setting the existing .winControl property in repeater.element to null (disconnecting the old WinJS control), then creating a new WinJS.UI.Repeater using the parent element. I'd call the old one's dispose() here too for good measure:

    repeater.dispose();
    repeater.element.winControl = null;
    newRepeater = new WinJS.UI.Repeater(repeater.element)

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press. First edition (for Windows 8) also available.


    • Marked as answer by Aratys Thursday, November 21, 2013 7:44 AM
    Wednesday, November 20, 2013 11:21 PM
  • Glad that works. I did confirm the bug with the feature team, where we identified that this problem occurs specifically when you're using a separately declared template, as in your example. Besides the recreate workaround that I already offered, the simpler solution is to declare your template inline, directly as a child of the repeater. In your HTML file:

    <div class="repeater" data-win-control="WinJS.UI.Repeater">
        <div data-win-bind="textContent: text"></div>
    </div>

    In home.js, then, you can just change the data source if you don't need to replace the template. To condense the code a bit:

        function createRepeater(recreate) {
            var data = new WinJS.Binding.List();
    
            for (var i = 0; i < 15; i++) {
                data.push({ text: "My text " + i });
            }
    
            WinJS.Utilities.query(".repeater")[0].winControl.data = data;
        }
    

    The origin of the problem is that when you reset a repeater's data or template properties, WinJS is clearing out the repeater's children and attempting to restore the original inline template structure in the DOM before setting the new data source. It's in that process that the bug exists, and you can avoid it with the inline template.

    If you need to change the template dynamically, then you'll need to recreate the Repeater as before.

    And just to note, the !! syntax is valid. It turns a "truthy" or "falsy" value, I'm told, to a real true or false. One of those odd characteristics of JavaScript!

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press. First edition (for Windows 8) also available.

    Thursday, November 21, 2013 2:49 PM

All replies

  • thanks for your clear instructions. Offhand, I don't see anything wrong with your code, so I'm checking with the WinJS team. The crash is happening down in ui.js in a function called _unloadRepeatedDOM, where it looks to be attempting to remove the repeater's first child node twice, hence the "not found" error. There is the suspicious code "id (!!shouldDisposeElements)" with the double !! that could be the bug.

    In the meantime, you can workaround the problem by creating a new Repeater control instead of replacing the data on the existing one. Do this by setting the existing .winControl property in repeater.element to null (disconnecting the old WinJS control), then creating a new WinJS.UI.Repeater using the parent element. I'd call the old one's dispose() here too for good measure:

    repeater.dispose();
    repeater.element.winControl = null;
    newRepeater = new WinJS.UI.Repeater(repeater.element)

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press. First edition (for Windows 8) also available.


    • Marked as answer by Aratys Thursday, November 21, 2013 7:44 AM
    Wednesday, November 20, 2013 11:21 PM
  • Thanks Kraig! :)

    Your workaround is working great.

    Thursday, November 21, 2013 7:44 AM
  • Glad that works. I did confirm the bug with the feature team, where we identified that this problem occurs specifically when you're using a separately declared template, as in your example. Besides the recreate workaround that I already offered, the simpler solution is to declare your template inline, directly as a child of the repeater. In your HTML file:

    <div class="repeater" data-win-control="WinJS.UI.Repeater">
        <div data-win-bind="textContent: text"></div>
    </div>

    In home.js, then, you can just change the data source if you don't need to replace the template. To condense the code a bit:

        function createRepeater(recreate) {
            var data = new WinJS.Binding.List();
    
            for (var i = 0; i < 15; i++) {
                data.push({ text: "My text " + i });
            }
    
            WinJS.Utilities.query(".repeater")[0].winControl.data = data;
        }
    

    The origin of the problem is that when you reset a repeater's data or template properties, WinJS is clearing out the repeater's children and attempting to restore the original inline template structure in the DOM before setting the new data source. It's in that process that the bug exists, and you can avoid it with the inline template.

    If you need to change the template dynamically, then you'll need to recreate the Repeater as before.

    And just to note, the !! syntax is valid. It turns a "truthy" or "falsy" value, I'm told, to a real true or false. One of those odd characteristics of JavaScript!

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press. First edition (for Windows 8) also available.

    Thursday, November 21, 2013 2:49 PM
  • It's very helpful, Thank you
    Thursday, November 21, 2013 3:25 PM
  • For completeness' sake, you can also work around the bug by using a separate template with its extractChild property set to true. That is, the following also works just fine:

    <div class="template" data-win-control="WinJS.Binding.Template" data-win-options="{ extractChild: 'true' }">
        <div data-win-bind="textContent: text"></div>
    </div>
    
    <div class="repeater" data-win-control="WinJS.UI.Repeater" data-win-options="{ template: select('.template') }">
    </div>
    

    Tuesday, November 26, 2013 5:21 PM