locked
Binding WinJS.UI.ListView to data source

    Question

  • Hi,

    I've been trying to get a WinJS.UI.ListView to work with a data source and I have followed the instructions here about how to do it. What I don't get is how it populates the items on load as it isn't calling the itemsFromIndex method when it's bound.

    I have looked at this article too.

    Here is my data.js file:

    (function () {
        "use strict";
    
    	//http://64.4.30.156/en-us/library/windows/apps/hh770849.aspx
        var dataAdapter = WinJS.Class.define(function () { },
    	{
    		itemsFromIndex: function (requestIndex, countBefore, countAfter) {
    			var requestStr = "http://localhost:47317/api/values",
    				that = this;
    
    			return WinJS.xhr({ url: requestStr }).then(function (request) {
    				var items = JSON.parse(request.responseText),
    					results = [];
    
    				for (var i = 0, itemsLength = items.length; i < itemsLength; i++) {
    					/*
    					IItem: http://64.4.30.156/en-us/library/windows/apps/br212592.aspx
    
    					data:	Gets or sets the item's data.
    					handle:	Gets the temporary ID of the item.
    					index:	Gets the item's index in the IListDataSource.
    					key:	Gets or sets the key the identifies the item.
    
    					*/
    					var Item = {
    						key: i.toString(),
    						data: {
    							TopLine: items[i].TopLine
    						}
    					}
    
    					results.push(Item);
    				}
    
    
    				/*
    				IFetchResult: http://64.4.30.156/en-us/library/windows/apps/br212548.aspx
    
    				absoluteIndex:	Gets or sets the index of the requested item in the IListDataAdapter object's data source.
    				atEnd:			Gets or sets a value that indicates whether this IFetchResult contains the last items
    								at the end of the IListDataAdapter object's data source.
    				atStart:		Gets or sets a value that indicates whether this IFetchResult contains the first items at the beginning of the IListDataAdapter object's data source.
    				items:			Gets or sets the items returned by the fetch operation.
    				offset:			Gets or sets the location of the requested item within the items array.
    				totalCount:		Gets or sets the number of items in the IListDataAdapter object's underlying data source.
    				*/
    				var FetchResult = {
    					items: results,
    					offset: 0,
    					totalCount: results.length
    				}
    
    				return FetchResult;
    
    			}, function (request) {
    				return WinJS.UI.FetchError.noResponse;
    			});
    		},
    		getCount: function () {
    			var requestStr = "http://localhost:47317/api/values",
    				that = this;
    
    			return WinJS.xhr({ url: requestStr }).then(function (request) {
    				var obj = JSON.parse(request.responseText);
    				try {
    					return obj.length;
    				} catch (err) {
    					return WinJS.Promise.wrapError(new WinJS.ErrorFromName(WinJS.UI.FetchError.noResponse));
    				}
    			})
    		}
    	});
    
        var dataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function () {
        	this._baseDataSourceConstructor(new dataAdapter());
        });
    
        WinJS.Namespace.define("appData", { dataSource: dataSource });
    
    })();
    

    Monday, April 23, 2012 10:36 AM

All replies

  • How do you define your ListView in html?
    I think you should create an instance of dataSource like: new appData.dataSource()

    Tuesday, April 24, 2012 9:39 AM
  • Hi,

    I define it like this:

    <div data-win-control="WinJS.UI.ListView" 
            data-win-options="{itemDataSource : appData.dataSource, itemTemplate: select('#template'), selectionMode: 'single'}}">
        </div>
    Even if I define it in the code it doesn't run an initialization to get the items.

    @williamparry

    Tuesday, May 1, 2012 7:00 AM
  • Hi William,

    Just to make sure I completely understand, your data is not populating correct?

    After you populate the data source you should assign that datasource and the template to the list view.  This should trigger your code.

    This sample is the implementation of that walktrough code and it would be helpful if you inspected this and compared it to your code:

    http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128\

    You can trace through this working sample and perhaps discover what you are doing differently.

    -Jeff


    Jeff Sanders (MSFT)

    Tuesday, May 1, 2012 6:34 PM
    Moderator
  • This sample is the implementation of that walktrough code and it would be helpful if you inspected this and compared it to your code:

    http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128\

    This is a very useful sample.  However, getCount() on the datasource is implemented as an additional API call to the service.  Is that the best we can do?  Many search APIs provide the total count alongside the results - so implementing things this way would mean making the same call twice.  

    Wednesday, May 2, 2012 5:10 PM
  • This sample is the implementation of that walktrough code and it would be helpful if you inspected this and compared it to your code:

    http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128\

    This sample doesn't implement oniteminvoked for the photos coming back from the api.  In the boilerplate code, this method uses the index that was clicked against a static datasource to find which item was invoked.  In the case of a list bound to an api, how should you do it?  I assume we don't need to go back to the api, right?

    Wednesday, May 2, 2012 9:19 PM
  • I've matched it up with the Bing example and gone through the tutorial on how to use a custom data source.

    I've stepped through the Bing example and my own code. Both process getCount() and then return the results from the service call, but mine doesn't get to:

    function postFetch() {
            if (!fetchesPosted) {
                fetchesPosted = true;
                msSetImmediate(function () {
                    fetchesPosted = false;
                    fetchItemsForAllSlots();
                });
            }
        }

    (line 3802 in ui.js)


    @williamparry

    Sunday, May 6, 2012 3:51 AM
  • Hi William,

    Just to make sure I completely understand, your data is not populating correct?

    -Jeff


    Jeff Sanders (MSFT)

    Monday, May 7, 2012 6:06 PM
    Moderator
  • Hi Jeff,

    It's not populating and I'm not sure why it's not reaching the postFetch() method like the Bing example does.


    @williamparry

    Monday, May 7, 2012 10:27 PM
  • This sample is the implementation of that walktrough code and it would be helpful if you inspected this and compared it to your code:

    http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128\

    This sample doesn't implement oniteminvoked for the photos coming back from the api.  In the boilerplate code, this method uses the index that was clicked against a static datasource to find which item was invoked.  In the case of a list bound to an api, how should you do it?  I assume we don't need to go back to the api, right?

    FWIW, Someone pointed me at an example which shows how to handle iteminvoked correctly: http://code.msdn.microsoft.com/windowsapps/ListView-basic-usage-sample-fcc451db

    Basically, in the event object, there is a details object, with an itemPromise object, which will in turn return the invoked item:

    eventObject.detail.itemPromise.then(function (invokedItem) {
                        // Access item data from the itemPromise
                        nav.navigate("/html/groupDetailPage.html", { group: invokedItem.data });
                    });

    Tuesday, May 8, 2012 6:55 PM
  • William,

    Can you send me a simplified sample with the problem?  http://blogs.msdn.com/jpsanders/contact.aspx

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, May 10, 2012 3:23 PM
    Moderator
  • Hi MSAD(metro style app dev),

    I'm using the same approach(VirtualizedDataSource and dataAdapter for listview as mentioned in sample app titled "working with data source" which fetches data from Bing). I'm getting data from remote data source . But only if i "Continue" the execution of program.

    The error is " JavaScript runtime error: Unable to get value of the property 'uniqueID': object is null or undefined ".

    I dont understand what exactly is this ?

    It is at >    _recordFromElement [ui.js] Line 9720 

    Can anybody please tell me how to get rid of this weird bug ? Maybe, I'm doing something wrong..

    Thanks,

    {Web-Farmer}

    Wednesday, May 23, 2012 1:06 PM
  • I don't know what you mean by "I'm getting data from remote data source . But only if i "Continue" the execution of program.".

    Can you give us more details?  Does the sample work OK for you unmodified?  If so, what exactly are you doing differently in your code?  That may give you a clue as to what is failing.

    -Jeff


    Jeff Sanders (MSFT)

    Wednesday, May 23, 2012 1:11 PM
    Moderator
  • hi Jeff,

    thanks for your prompt reply..

    Well, yes i have modified that sample to use our set of api instead of calling bing image services.

    What i'm doing is same as William as mentioned in first post in this ongoing thread.

    When i run this app, its throws an exception and  shows me an error  " JavaScript runtime error: Unable to get value of the property 'uniqueID': object is null or undefined ". If there is a handler for this exception, the program may be safely continued."

    So i clicked "Continue" and i get the result data from API that i'm calling using VirtualizedDataSource and iDataAdapter.

    The problem  is that error i'm not able to figuring out what causes that error..

    THanks,


    Wednesday, May 23, 2012 2:12 PM
  • If you can give me a simple repro I can look into this further.

    http://blogs.msdn.com/jpsanders/contact.aspx

    -Jeff


    Jeff Sanders (MSFT)

    Wednesday, May 23, 2012 2:15 PM
    Moderator
  • I have created default metro style app for javascript with blank template..

    1) this is in gridData.js

    // global for the datasource definition
    var iMatchDataSource = iMatchDataSource || {};
    
    (function () {
    
        // Definition of the data adapter
        var iMatchDataAdapter = WinJS.Class.define(
            function (devkey, query) {
    
                // Constructor
                this._minPageSize = 10;  // based on the default of 10
                this._maxPageSize = 50;  // max request size for bing images
                this._maxCount = 1000;   // limit on the bing API
                this._devkey = devkey;
                this._query = query;
                this._SUCCESS_ = 1;
                this._ERROR_ = 2;
            },
    
            // Data Adapter interface methods
            // These define the contract between the virtualized datasource and the data adapter.
            // These methods will be called by virtualized datasource to fetch items, count etc.
            {
                // This example only implements the itemsFromIndex and count methods
    
                // Called to get a count of the items
                // The value of the count can be updated later in the response to itemsFromIndex
                getCount: function () {
                    var that = this;
    
                    return that._maxCount;
                },
    
                // Called by the virtualized datasource to fetch items
                // It will request a specific item and hints for a number of items either side of it
                // The implementation should return the specific item, and can choose how many either side
                // to also send back. It can be more or less than those requested.
                //
                // Must return back an object containing fields:
                //   items: The array of items of the form items=[{ key: key1, data : { field1: value, field2: value, ... }}, { key: key2, data : {...}}, ...];
                //   offset: The offset into the array for the requested item
                //   totalCount: (optional) update the value of the count
                itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                    var that = this;
                    
                    //Build up the URL for the request
                    var requestStr = "my url is here";
    
                    //Return the promise from making an XMLHttpRequest to the server
                    return WinJS.xhr({ url: requestStr }).then(
    
                        //Callback for success
                        function (request) {
                            var results = [], count;
                            console.log("resp");
                             // Use the JSON parser on the results, safer than eval
                            var obj = JSON.parse(request.responseText);
                          
                            console.log(obj.DATA.resData);
                            // Verify if the service has returned images
                            if (true) {
                                var items = [], itemsLength;
                                items = obj.DATA.resData;
    
                                // Data adapter results needs an array of items of the shape:
                                // items =[{ key: key1, data : { field1: value, field2: value, ... }}, { key: key2, data : {...}}, ...];
                                // Form the array of results objects
                                for (var i = 0, itemsLength = items.length; i < itemsLength; i++) {
                                    var dataItem = items[i];
                                    results.push({
                                        key: i.toString(),
                                        data: {
                                            title: dataItem.user_company_name,
                                            thumbnail: dataItem.advertiser_company_logo_path,
                                            linkurl: dataItem.noofcoupons
                                        }
                                    });
                                }
                                // Get the count from the json
                                count = obj.DATA.total;
    
                                return {
                                    items: results, // The array of items
                                    offset: 0, // The offset into the array for the requested item
                                    totalCount: 10 // Total count of records, bing will only return 1000 so we cap the value
                                };
                            } else {
                                return WinJS.UI.FetchError.doesNotExist;
                            }
                        },
    
                        //Called on an error from the XHR Object
                        function (request) {
                            return WinJS.UI.FetchError.noResponse;
                        });
                }
    
                // setNotificationHandler: not implemented
                // itemsFromStart: not implemented
                // itemsFromEnd: not implemented
                // itemsFromKey: not implemented
                // itemsFromDescription: not implemented
            });
    
        iMatchDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (devkey, query) {
            this._baseDataSourceConstructor(new iMatchDataAdapter(devkey, query));
        });
    })();

    2) this is in default.js

    WinJS.UI.processAll().done(function () {
                    var listview = id("listview1").winControl;
                    var myTemplate = id("itemTemplate");
    
                    //Create the bing itemDataSource
                    var myDataSrc = new iMatchDataSource("1", "2");
    
                    // Set the properties on the list view to use the itemDataSource
                    listview.itemDataSource = myDataSrc;
                    listview.itemTemplate = myTemplate;
                });
    3) and here goes default.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Application1</title>
    
        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
        <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
        <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>
    
        <!-- Application1 references -->
        <link href="/css/default.css" rel="stylesheet">
        <script src="/js/gridData.js"></script>
        <script src="/js/default.js"></script>
    </head>
    <body>
        <p>Content goes here</p>
         <div class="item" id="scenarioOutput">
            <div id="itemTemplate" data-win-control="WinJS.Binding.Template">
                <div class="itemTempl">
                    <img data-win-bind="src: thumbnail" alt="Databound image" />
                    <span class="content" data-win-bind="innerText: title"></span>
                </div>
            </div>
            <div id="listview1" class="box"
                data-win-control="WinJS.UI.ListView"
                data-win-options="{ selectionMode: 'none', tapBehavior: 'none', swipeBehavior: 'none', layout: { type: WinJS.UI.GridLayout, maxRows: 10 } }"
            ></div>
        </div>
    </body>
    </html>
    


    Wednesday, May 23, 2012 2:31 PM
  • I am sorry I was not clear.  I want to run your repro here and debug it. 

    Jeff Sanders (MSFT)

    Wednesday, May 23, 2012 2:34 PM
    Moderator
  •  I want to make a data grid control which fetch data from remote data services and it would load the data as user scroll the view(list view).. a kinda of lazy loading of data or infinite scroll...

    So i'm experimenting above code.

    Wednesday, May 23, 2012 2:36 PM
  • Jeff, can you please look this issue when you view this thread again ?

    Or is there any other way without using datasource and data adapter along with listView.

    I'm thinking just to use XHR only and when it returns promises then i will populate the listview with WinJS.Binding.List(arg); where arg is the result from remote web services..normally an array or json.

    So every time when user scroll the listview, i will call the XHR and push the data into my "xyz" list.

    This is what i'm planning to do now..

    If you get anything that help me to achieve this scenario then please share it..

    Thanks

    Thursday, May 24, 2012 5:53 AM
  • Again, I need a physical repro of this.

    Jeff Sanders (MSFT)

    Thursday, May 24, 2012 12:31 PM
    Moderator