locked
How to bind to ListView when data comes from async call?

    Question

  • I'm writing my first Win8 Html/Javascript app.  It is a simple photo viewer that uses the grid template.  The first page that is invoked is the groupedItems page that uses the WinJS.UI.ListView.  The HTML Implementation is:

    <div class="itemtemplate" data-win-control="WinJS.Binding.Template">
    	<div class="item">
    		<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: year" />
    		<div class="item-overlay">
    			<h4 class="item-title" data-win-bind="textContent: year"></h4>
    			<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: year"></h6>
    		</div>
    	</div>
    </div>
    
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment groupeditemspage">
    	<header aria-label="Header content" role="banner">
    		<button data-win-control="WinJS.UI.BackButton"></button>
    		<h1 class="titlearea win-type-ellipsis">
    			<span class="pagetitle">PhotoDB App For HTML</span>
    		</h1>
    	</header>
    	<section aria-label="Main content" role="main">
    		<div id="YearList" class="groupeditemslist win-selectionstylefilled"
    			 aria-label="List of groups"
    			 data-win-control="WinJS.UI.ListView"
    			 data-win-options="{
    				layout: {type: WinJS.UI.GridLayout, groupHeaderPosition: 'top'},
    				selectionMode: 'none',
    				currentItem: {type: WinJS.UI.ObjectType.item, index: 0, hasFocus: true},
    				groupHeaderTemplate: select('.headertemplate'),
    				itemDataSource: ViewModel.AllEventYears.dataSource,
    				itemTemplate: select('.itemtemplate'),
    				ongroupheaderinvoked: select('.pagecontrol').winControl.groupHeaderInvoked,
    				oniteminvoked: select('.pagecontrol').winControl.itemInvoked
    			}">
    		</div>
    	</section>
    </div>

    http://msdn.microsoft.com/en-us/library/windows/apps/hh465496.aspx
    describes how one connects data to the ListView.  Following the example, my javascript looks like:

    var dataArray = [
    	{ key: 2014, year: 2014, backgroundImage: "/images/itemsBackground.png" }
    ];
    yearList = new WinJS.Binding.List(dataArray);

    That is sufficient to successfully bind to the the ItemDataSource property of the YearList div above.
    itemDataSource: ViewModel.AllEventYears.dataSource,

    My ViewModel.js looks like this:

    (function () {
        "use strict";
        var yearList = null;
        function getAllYears(acctId) {
            var dataArray = [
                { key: 2014, year: 2014, backgroundImage: "/images/itemsBackground.png" }
            ];
            yearList = new WinJS.Binding.List(dataArray);
        };
        getAllYears(2);
        WinJS.Namespace.define("ViewModel", {       
                AcctId: 2,
                AllEventYears: yearList     
        });
    })();
    

    But it doesn't work.  When I run it, an exception is thrown:
    "Unable to get property 'dataSource' of undefined or null reference.
    It makes perfect sense, the call is async. 

    In the xaml world, the itemDataSource would be an observableCollection and the binding would have a notifyWhenSourceChanged event.  I have looked at promises, but have not figured out how ,where to apply the promise for the itemDataSource of the ListView control.

    Any guidance would be appreciated.
    thanks.

           


    Robotuner

    Thursday, April 3, 2014 4:51 PM

Answers

  • You need a reference to the ListView and the WinJS.Binding.List datasource.

    // Next line is typical in the templates, but you'd need a class name instead of ID (which might work, too).
    var lv = element.querySelector("YearList").winControl;

    // Next line assumes valid groups. At any rate, items needs to be a WinJS.Binding.List.
    var items = Data.getItemsFrom.Group("group name");

    lv.itemDataSource = items.dataSource;

    • Marked as answer by Robotuner Friday, April 4, 2014 4:41 PM
    Thursday, April 3, 2014 10:48 PM

All replies

  • There are various ways to do this. It doesn't look like you need to get the data asynchronously, using a promise (though you could do that). The easiest way is probably to set .itemDataSource for the ListView in the ready() function for the page (groupedItems.js).

    Also, is your ViewModel.js file loading first? You might move the script reference to be the first reference in default.html, and see if that solves the problem.

    Here's a link to template docs on binding data with Grid using asynchronous data:

    http://msdn.microsoft.com/en-us/library/windows/apps/hh758329.aspx

    Thursday, April 3, 2014 5:55 PM
  • Thanks for the quick response.  It's not entirely clear to me how I would set the .itemDataSource in the groupedItems.js ready() function.  I can find the div:

    var yearListDiv =  document.getElementById("YearList");

    but how to I get the data-win-control object and then navigate to the itemDataSource to set it?

    Thanks,


    Robotuner

    Thursday, April 3, 2014 6:32 PM
  • You need a reference to the ListView and the WinJS.Binding.List datasource.

    // Next line is typical in the templates, but you'd need a class name instead of ID (which might work, too).
    var lv = element.querySelector("YearList").winControl;

    // Next line assumes valid groups. At any rate, items needs to be a WinJS.Binding.List.
    var items = Data.getItemsFrom.Group("group name");

    lv.itemDataSource = items.dataSource;

    • Marked as answer by Robotuner Friday, April 4, 2014 4:41 PM
    Thursday, April 3, 2014 10:48 PM