locked
StorageDataSource: error with basic code?

    Question

  • Hello guys.

    I'm trying to use the StorageDataSource objeto to enumerate all the png files maintained inside a folder picked by the user. Here's the code I'm using:

    function obtemPastaEEnumera() {
            var picker = new Windows.Storage.Pickers.FolderPicker();
            picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.desktop;
            picker.fileTypeFilter.append("*");
            picker.pickSingleFolderAsync()
            .done(function (pasta) {
                if (!pasta) return;
                var queryOptions = new Windows.Storage.Search.QueryOptions(
                Windows.Storage.Search.CommonFileQuery.orderByName, [".png"]);
                var query = pasta.createFileQueryWithOptions(queryOptions);
                var options = {
                    mode: Windows.Storage.FileProperties.ThumbnailMode.picturesView,
                    requestedTumbnailSize: 190,
                    tumbnailOptions: Windows.Storage.FileProperties.ThumbnailOptions.useCurrentScale
                };
                var storage = new WinJS.UI.StorageDataSource(query, options);
                lista.itemDataSource = storage;
            });
        }


    can you see anything wrong with it? I'm getting an exception with the following info:

    Exception was thrown but not handled in user code at line 8460, column 17 in ms-appx://microsoft.winjs.1.0.rc/js/base.js

    0x800a13d5 - JavaScript runtime error: Cannot define property '_getObservable': object is not extensible

    If there is a handler for this exception, the program may be safely continued.

    I've noticed that the exception will only get thrown when there are compatible files inside the folder the user picked up.

    thanks.


    Luis Abreu

    Tuesday, June 12, 2012 5:27 PM

Answers

  • Thanks a lot for the repro code Luis, this is very helpful!

    The issue is in how you define the binding. When binding to StorageDataSource, you should use the built-in OneTime binding initializer (or your own binding function, if needed). The code would look like:

    <div id="tmpl" data-win-control="WinJS.Binding.Template">
        <span data-win-bind="innerHTML: name WinJS.Binding.oneTime"></span>
        criado em
        <span data-win-bind="innerHTML: dateCreated WinJS.Binding.oneTime"></span>
    </div>

    The reason for this is that StorageDataSource works off file system objects, which are immutable. When you do not provide a binding function, WinJS tries to augment the items with the _getObservable method, but fails because these objects cannot be modified.

    Hope this helps!

    Thursday, June 14, 2012 9:51 PM

All replies

  • anyone?

    Luis Abreu

    Wednesday, June 13, 2012 9:00 AM
  • Hi Luis,

    There is not enough information in your question to start investigating this.

    What are "compatible files inside the folder the user picked up"?

    How can this error be duplicated?

    Can you provide a simple repro of this?

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, June 14, 2012 5:32 PM
    Moderator
  • HEllo again Jeff.

    "compatible files" means that the error will only be generated when there are png files in the folder the user chose in the picker (that's what I'm trying to show).

    here's the code I'm using.

    markup:

    <body>
        <h1>Enumerar itens mantidos numa pasta</h1>
        <div id="tmpl" data-win-control="WinJS.Binding.Template">
            <span data-win-bind="innerHTML: name"></span>
            criado em
            <span data-win-bind="innerHTML: dateCreated"></span>
        </div>
        <button>Enumera itens</button>
        <div id="conteudos">
            <div id="lista" data-win-control="WinJS.UI.ListView"
                data-win-options="{itemTemplate: tmpl}">

            </div>
        </div>
    </body>

    and here's the js code:

    var app = WinJS.Application;
        var activation = Windows.ApplicationModel.Activation;
        WinJS.strictProcessing();
        var lista = null;
        app.onactivated = function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                WinJS.Utilities.query("button").listen("click", obtemPastaEEnumera);
                args.setPromise(
                   WinJS.UI.processAll()
                    .then(function () {
                        lista = document.getElementById("lista").winControl;
                    })
                    );
            }
        };

        function obtemPastaEEnumera() {
            var picker = new Windows.Storage.Pickers.FolderPicker();
            picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.desktop;
            picker.fileTypeFilter.append("*");
            picker.pickSingleFolderAsync()
            .done(function (pasta) {
                if (!pasta) return;
                var queryOptions = new Windows.Storage.Search.QueryOptions(
                Windows.Storage.Search.CommonFileQuery.orderByName, [".png"]);
                var query = pasta.createFileQueryWithOptions(queryOptions);
                var options = {
                    mode: Windows.Storage.FileProperties.ThumbnailMode.picturesView,
                    requestedTumbnailSize: 190,
                    tumbnailOptions: Windows.Storage.FileProperties.ThumbnailOptions.useCurrentScale
                };
                var storage = new WinJS.UI.StorageDataSource(query);
                lista.itemDataSource = storage;
            });
        }

        app.oncheckpoint = function (args) {
            // TODO: This application is about to be suspended. Save any state
            // that needs to persist across suspensions here. You might use the
            // WinJS.Application.sessionState object, which is automatically
            // saved and restored across suspension. If you need to complete an
            // asynchronous operation before your application is suspended, call
            // args.setPromise().
        };

        app.start();
    })();

    Can you reproduce this behavior? did I missed something?

    thanks.



    Luis Abreu

    Thursday, June 14, 2012 8:25 PM
  • Thanks a lot for the repro code Luis, this is very helpful!

    The issue is in how you define the binding. When binding to StorageDataSource, you should use the built-in OneTime binding initializer (or your own binding function, if needed). The code would look like:

    <div id="tmpl" data-win-control="WinJS.Binding.Template">
        <span data-win-bind="innerHTML: name WinJS.Binding.oneTime"></span>
        criado em
        <span data-win-bind="innerHTML: dateCreated WinJS.Binding.oneTime"></span>
    </div>

    The reason for this is that StorageDataSource works off file system objects, which are immutable. When you do not provide a binding function, WinJS tries to augment the items with the _getObservable method, but fails because these objects cannot be modified.

    Hope this helps!

    Thursday, June 14, 2012 9:51 PM
  • Hello again Mark.

    hum...one more question: does this mean that adding files to the folder won't result in a refresh of the data shown? I'm asking this because I've built an initialization binding function in other app and I've noticed that updates performed over the observable objects which were encapsulated in the List object stopped  being propagated.

    thanks


    Luis Abreu

    Friday, June 15, 2012 7:37 AM
  • Hi Luis,

    Adding, removing or modifying files will result in a refresh of the data shown when using StorageDataSource, but in general it is up to each data source to handle these changes, so if you are using a different data source to back the listview this may be the reason why you were not seeing these being propagated.

    Note that using onetime binding does not impact that scenario for StorageDataSource because the view is not refreshed through the binding, but rather entire items are re-rendered:

    • When the file query fires "contentschanged", StorageDataSource calls WinJS.UI.VirtualizedDataSource.ListDataNotificationHandler.invalidateAll, which prompts a full re-draw of the view
    • When the file query fires "propertiesupdated" on a given item, StorageDataSource calls WinJS.UI.VirtualizedDataSource.ListDataNotificationHandler.changed(item), which prompts a full re-rendering of that item

    Note that the "contentschanged" event only fires after StorageFileQueryResult.getFilesAsync has been called at least once - so in theory, if your app holds on to the query without actually retrieving items for a while, items could be added in the background without your app being notified.

    Cheers,

    Marc

    Friday, June 15, 2012 10:09 PM
  • Hello again Mark.

    Understood.

    going back to the onetime binding, the questions I was trying to refer are in this thread (still not answered):http://social.msdn.microsoft.com/Forums/en-US/winappswithhtml5/thread/0f4d9329-04c1-4ac6-adce-125dd597b7e8

    Basically, I'm complaining about the fact that initialization bindings will only work once! btw, why can't we only have converters instead of having initializers and converters? converters have been "common" across several platforms (they event existed in the asp.net ajax if I'm not mistaken), so I can't understand why WinJS decided to have initializers and converters. Even worse, converters don't receive the some parameters as an initializer, limiting its use.

    And this leads to my question in that post: how to I create a binding relationship that concatenates teh values of fields from the object and that do propagate the changes from object to control when the observable object changes?

    thanks.


    Luis Abreu

    Saturday, June 16, 2012 11:56 AM