locked
What's the recommended way to use requirejs with WinJS.UI.Page.define

    Question

  • I am still unsure how to facilitate requirejs with the WinJS PageControl. I understand that I need to call WinJS.UI.Page.define before the first WinJS.Navigation.navigate call, so that the navigator/WinJS knows there is a page control for the given URL.

    Now, when my page has dependencies I can declare them like this:

    //page.js
    
    require(["dep"], function(dep) {
      WinJS.UI.Pages.define ("page.html", {
      });
    });
    

    Of course, since require is async the page is only defined if when the "dep" has been loaded. WinJS will not know about the page.

    So the only option left is to call require inside the pages init/processed method? Should I return a promise from any of those methods?

    Thursday, April 3, 2014 8:24 PM

Answers

  • RequireJS is great for that, but I had considerable trouble getting it to work with WinJS.  When I did get it working I then had trouble getting it to work properly. 

    RequireJS is expected to be initialised with an include reference in the page html with a data-main tag, but this doesn't really work with the WinJS app lifecycle, so it took a bit of work to get it behaving the way I expected.

    Eventually I got it working, and working with async initialisation code which was a key part of the initialisation process in my sample app.

    So the basics of getting RequireJS working in WinJS is the following - I have probably made a million big JavaScript no-no's here, but as I said, still learning.

    default.html - add
        <script src="/js/require.js" ></script>
    
    before default.js

    default.js - initialise requireJS and include whatever dependencies you want to use in your startup code.

    app.addEventListener("activated", function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.closedByUser) {
                    // TODO: This application has been launched after a requested shutdown. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
                }
     
                if (app.sessionState.history) {
                    nav.history = app.sessionState.history;
                }
     
                //RequireJS Initialisation here
                require.config({
                    baseUrl: '/js'
                });
     
                //this creates a promise that calls complete() when the initAsync promises finish
                //all this code happens within the require context so we are guaranteed that the dependencies are resolved before the promise is resolved
                //the initAsync methods return promises that load data from internal json data files
                //persistence.loadState is synchronous code so doesn't return a promise
                var loadPromise = new WinJS.Promise(function (complete) {
                    require([
                        'persistence',
                        'game/staticdata/itemClassStore',
                        'game/staticdata/itemStore',
                        'game/gamestate/world',
                        'game/gamestate/characters',
                        'game/classes/character'
                    ], function (persistence, itemClassStore, itemStore, world, characters, character) {
     
                        itemClassStore.initAsync()
                            .then(function () { return itemStore.initAsync(); })
                            .then(
                                function () {
                                    var data = persistence.loadState();
                                    world.fromJson(data);
                                    complete();
                                }
                            );
     
     
                    });
                });
     
                //args.setPromise is a function on the activate event which allows you to wait for promises to complete before continuing
                args.setPromise(
                    loadPromise
                        .then(function () { return WinJS.UI.processAll(); })
                        .then(
                            function () {
                                if (nav.location) {
                                    nav.history.current.initialPlaceholder = true;
                                    return nav.navigate(nav.location, nav.state);
                                } else {
                                    return nav.navigate(Application.navigator.home);
                                }
                            }
                        )
                );
            }
        });

    xPage.js - resolve your dependencies in the page ready function with a call to require(), and perform your page initialisation as per normal. 
    /// <reference nbsp="" path="/js/require.js"></reference> 
    /// <reference nbsp="" path="/js/appbar.js"></reference>
    /// <reference nbsp="" path="/js/game/classes/character.js"></reference>
    /// <reference nbsp="" path="/js/game/gamestate/world.js"></reference>
    /// <reference nbsp="" path="/js/game/gamestate/characters.js"></reference>
     
    (function () {
        "use strict";
     
        WinJS.Namespace.define("CharacterSelect", {
            CharacterSelectViewModel: WinJS.Class.define(
                //the viewmodel constructor takes the injected requireJS dependency from the page ready code.
                function (world) {
                    this._charactersModule = world.characters;
                    this._itemsDataSource = new WinJS.Binding.List(this._charactersModule.characters);
                    this._inventoryDataSource = new WinJS.Binding.List(null);
                },
                {
                    _charactersModule: null,
                    _itemsDataSource: null,
                    _inventoryDataSource: null,
                    _selectedCharacterId: 0,
                    selectedCharacterId: {
                        get: function () {
                            return this._selectedCharacterId;
                        },
                        set: function (value) {
                            this._selectedCharacterId = value;
                            if (value == 0) {
                                this._inventoryDataSource = new WinJS.Binding.List(null);
     
                            } else {                            
                                this._inventoryDataSource = new WinJS.Binding.List(this._charactersModule.get(value).inventory.items);
                            }
                            this._getObservable().notify("selectedCharacterId", value);
                            this._getObservable().notify("inventoryDataSource", this._inventoryDataSource);
                        }
                    },
     
                    listDataSource: {
                        get: function () {
                            return this._itemsDataSource;
                        }
                    },
                    
                    inventoryDataSource: {
                        get: function () {
                            return this._inventoryDataSource;
                        }
                    },
     
                    addNew: function () {
                        WinJS.Navigation.navigate("/pages/newCharacter/newCharacter.html");
                    },
     
                    deleteCharacter: function (that) {
                        that._charactersModule.deleteCharacter(this.selectedCharacterId);
                        that._itemsDataSource = new WinJS.Binding.List(this._charactersModule.characters);
     
                        that._getObservable().notify("listDataSource", that._itemsDataSource);
                    }
     
                },
                {}),
     
        });
     
        WinJS.UI.Pages.define("/pages/characterSelect/characterSelect.html", {
     
            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                require(
                    [
                        'persistence',
                        "appbar",
                        "game/gamestate/world"
                    ],
                    function (persistence, appbar, world) {
                        //normal page initialisation code - all dependency modules are initialised at this point
                        //create view model (passing in resolved dependencies) and bind to relevant events
                        //(WinJS only has 1-way bindings and can't declaratively bind to button events that i can see)
                        var section = element.querySelector("section");
     
                        var viewModel = new CharacterSelect.CharacterSelectViewModel(world);
                        var observableviewModel = WinJS.Binding.as(viewModel);
                        WinJS.Binding.processAll(section, observableviewModel);
     
                        document.getElementById("cmdCreate").addEventListener("click", observableviewModel.addNew, false);
                        document.getElementById("cmdDeleteCharacter").addEventListener(
                            "click",
                            function(){
                                observableviewModel.deleteCharacter(observableviewModel);
                                persistence.saveState();
                            }
                            ,
                            false
                            );
     
                        document.getElementById("characters").winControl.onselectionchanged = function (ev) {
                            var selection = document.getElementById("characters").winControl.selection;
                            if (selection.getItems()._value.length > 0) {
                                viewModel.selectedCharacterId = selection.getItems()._value[0].data.id;
                                appbar.characterSelectSelected();
                            } else {
                                viewModel.selectedCharacterId = 0;
                                appbar.characterSelectDeSelected();
                            }
     
                        };
                        appbar.characterSelectInit();
     
     
                    }
                );
     
     
            },
     
            unload: function () {
                // TODO: Respond to navigations away from this page.
            },
     
            updateLayout: function (element, viewState, lastViewState) {
                /// <param domelement="true" name="element" nbsp="" />
     
                // TODO: Respond to changes in viewState.
            }
        });
    })();
    
    Tuesday, April 8, 2014 6:53 AM

All replies

  • I am still unsure how to facilitate requirejs with the WinJS PageControl. I understand that I need to call WinJS.UI.Page.define before the first WinJS.Navigation.navigate call, so that the navigator/WinJS knows there is a page control for the given URL.

    Now, when my page has dependencies I can declare them like this:

    //page.js
    
    require(["dep"], function(dep) {
      WinJS.UI.Pages.define ("page.html", {
      });
    });
    

    Of course, since require is async the page is only defined if when the "dep" has been loaded. WinJS will not know about the page.

    So the only option left is to call require inside the pages init/processed method? Should I return a promise from any of those methods?

    Thursday, April 3, 2014 11:22 PM
  • RequireJS is great for that, but I had considerable trouble getting it to work with WinJS.  When I did get it working I then had trouble getting it to work properly. 

    RequireJS is expected to be initialised with an include reference in the page html with a data-main tag, but this doesn't really work with the WinJS app lifecycle, so it took a bit of work to get it behaving the way I expected.

    Eventually I got it working, and working with async initialisation code which was a key part of the initialisation process in my sample app.

    So the basics of getting RequireJS working in WinJS is the following - I have probably made a million big JavaScript no-no's here, but as I said, still learning.

    default.html - add
        <script src="/js/require.js" ></script>
    
    before default.js

    default.js - initialise requireJS and include whatever dependencies you want to use in your startup code.

    app.addEventListener("activated", function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.closedByUser) {
                    // TODO: This application has been launched after a requested shutdown. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
                }
     
                if (app.sessionState.history) {
                    nav.history = app.sessionState.history;
                }
     
                //RequireJS Initialisation here
                require.config({
                    baseUrl: '/js'
                });
     
                //this creates a promise that calls complete() when the initAsync promises finish
                //all this code happens within the require context so we are guaranteed that the dependencies are resolved before the promise is resolved
                //the initAsync methods return promises that load data from internal json data files
                //persistence.loadState is synchronous code so doesn't return a promise
                var loadPromise = new WinJS.Promise(function (complete) {
                    require([
                        'persistence',
                        'game/staticdata/itemClassStore',
                        'game/staticdata/itemStore',
                        'game/gamestate/world',
                        'game/gamestate/characters',
                        'game/classes/character'
                    ], function (persistence, itemClassStore, itemStore, world, characters, character) {
     
                        itemClassStore.initAsync()
                            .then(function () { return itemStore.initAsync(); })
                            .then(
                                function () {
                                    var data = persistence.loadState();
                                    world.fromJson(data);
                                    complete();
                                }
                            );
     
     
                    });
                });
     
                //args.setPromise is a function on the activate event which allows you to wait for promises to complete before continuing
                args.setPromise(
                    loadPromise
                        .then(function () { return WinJS.UI.processAll(); })
                        .then(
                            function () {
                                if (nav.location) {
                                    nav.history.current.initialPlaceholder = true;
                                    return nav.navigate(nav.location, nav.state);
                                } else {
                                    return nav.navigate(Application.navigator.home);
                                }
                            }
                        )
                );
            }
        });

    xPage.js - resolve your dependencies in the page ready function with a call to require(), and perform your page initialisation as per normal. 
    /// <reference nbsp="" path="/js/require.js"></reference> 
    /// <reference nbsp="" path="/js/appbar.js"></reference>
    /// <reference nbsp="" path="/js/game/classes/character.js"></reference>
    /// <reference nbsp="" path="/js/game/gamestate/world.js"></reference>
    /// <reference nbsp="" path="/js/game/gamestate/characters.js"></reference>
     
    (function () {
        "use strict";
     
        WinJS.Namespace.define("CharacterSelect", {
            CharacterSelectViewModel: WinJS.Class.define(
                //the viewmodel constructor takes the injected requireJS dependency from the page ready code.
                function (world) {
                    this._charactersModule = world.characters;
                    this._itemsDataSource = new WinJS.Binding.List(this._charactersModule.characters);
                    this._inventoryDataSource = new WinJS.Binding.List(null);
                },
                {
                    _charactersModule: null,
                    _itemsDataSource: null,
                    _inventoryDataSource: null,
                    _selectedCharacterId: 0,
                    selectedCharacterId: {
                        get: function () {
                            return this._selectedCharacterId;
                        },
                        set: function (value) {
                            this._selectedCharacterId = value;
                            if (value == 0) {
                                this._inventoryDataSource = new WinJS.Binding.List(null);
     
                            } else {                            
                                this._inventoryDataSource = new WinJS.Binding.List(this._charactersModule.get(value).inventory.items);
                            }
                            this._getObservable().notify("selectedCharacterId", value);
                            this._getObservable().notify("inventoryDataSource", this._inventoryDataSource);
                        }
                    },
     
                    listDataSource: {
                        get: function () {
                            return this._itemsDataSource;
                        }
                    },
                    
                    inventoryDataSource: {
                        get: function () {
                            return this._inventoryDataSource;
                        }
                    },
     
                    addNew: function () {
                        WinJS.Navigation.navigate("/pages/newCharacter/newCharacter.html");
                    },
     
                    deleteCharacter: function (that) {
                        that._charactersModule.deleteCharacter(this.selectedCharacterId);
                        that._itemsDataSource = new WinJS.Binding.List(this._charactersModule.characters);
     
                        that._getObservable().notify("listDataSource", that._itemsDataSource);
                    }
     
                },
                {}),
     
        });
     
        WinJS.UI.Pages.define("/pages/characterSelect/characterSelect.html", {
     
            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                require(
                    [
                        'persistence',
                        "appbar",
                        "game/gamestate/world"
                    ],
                    function (persistence, appbar, world) {
                        //normal page initialisation code - all dependency modules are initialised at this point
                        //create view model (passing in resolved dependencies) and bind to relevant events
                        //(WinJS only has 1-way bindings and can't declaratively bind to button events that i can see)
                        var section = element.querySelector("section");
     
                        var viewModel = new CharacterSelect.CharacterSelectViewModel(world);
                        var observableviewModel = WinJS.Binding.as(viewModel);
                        WinJS.Binding.processAll(section, observableviewModel);
     
                        document.getElementById("cmdCreate").addEventListener("click", observableviewModel.addNew, false);
                        document.getElementById("cmdDeleteCharacter").addEventListener(
                            "click",
                            function(){
                                observableviewModel.deleteCharacter(observableviewModel);
                                persistence.saveState();
                            }
                            ,
                            false
                            );
     
                        document.getElementById("characters").winControl.onselectionchanged = function (ev) {
                            var selection = document.getElementById("characters").winControl.selection;
                            if (selection.getItems()._value.length > 0) {
                                viewModel.selectedCharacterId = selection.getItems()._value[0].data.id;
                                appbar.characterSelectSelected();
                            } else {
                                viewModel.selectedCharacterId = 0;
                                appbar.characterSelectDeSelected();
                            }
     
                        };
                        appbar.characterSelectInit();
     
     
                    }
                );
     
     
            },
     
            unload: function () {
                // TODO: Respond to navigations away from this page.
            },
     
            updateLayout: function (element, viewState, lastViewState) {
                /// <param domelement="true" name="element" nbsp="" />
     
                // TODO: Respond to changes in viewState.
            }
        });
    })();
    
    Tuesday, April 8, 2014 6:53 AM