locked
Detail page accessing variable defined in master page

    Question

  • Hi all,

    I have a page with associated javascript which uses var page = WinJS.UI.Pages.define to instantiate variables.

    When it does so, it sets the value of a variable defined in the namespace "MyApp" to a particular value, say "123".

    On the page, I also have a <div class="abc" data-win-control="MyApp.MyBar">hi</div>, so when my page is loaded, it fetches MyBar which in itself is a small html page added to the main page.

    This MyBar ready function access the main page's value "123", however, when accessing it, it is still undefined, because the ready:handler of the child div executes before the ready:handler of the main page.

    How can I control this behavior?

    Thank you

    Thursday, April 4, 2013 12:45 PM

Answers

  • Hi Thomas,

    OK I think I am following your logic and what the problem is now.  The win-controls and associated code is parsed first.  Once the code and controls have been loaded, then 'ready' is called for the page.  You need the code to be parsed this way in order for the data-win-controls to function correctly with the page.  Think of the 'ready' event as 'all of my page has created the controls and fragments and the page is done processing.

    If you open base.js and look for this:

    WinJS.Namespace.define("WinJS.UI.Pages", {

    You will see the member functions for the Pages object.

    If I understand your problem description correctly you want to use the init member function to do you processing since this will execute before any control contained within the page is created.

    If I am still misunderstanding you, you can actually debug this yourself by putting some breakpoints in base.js to see when the data-win-control is created in relation to your page creation. 

     

    -Jeff


    Jeff Sanders (MSFT)

    Tuesday, April 9, 2013 12:27 PM
    Moderator

All replies

  • Hi Thomas,

    I can follow this without a concrete example however maybe this will help.  After the processAll call you can access anything defined in the namespace.  You have to wait until then because there is where all the control magic happens!

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, April 4, 2013 6:40 PM
    Moderator
  • Hi Jeff,

    I have a main page called ListProducts.html. It contains a ListView bound to a RESTful data source and has the following code upon iteminvoked:

            _itemInvoked: function (args) {
                //console.log("clicked itemindex :" + args.detail.itemIndex);
                args.detail.itemPromise.then(function (invokedItem) {
                    console.log("clicked productId :" + invokedItem.data.productId);
                    //MyApp.currentProduct = invokedItem.data;
                    WinJS.Navigation.navigate("/html/productdetail/1_ProductDetail.html", { selectedProduct: invokedItem.data });
                });
           }

    So far, so good. The output of console.log here always shows the exact same productId of the product I click in the Listview.

    Now for the weird stuff:

    The product detail page looks like this:

    ... (some lines omitted)

    <body>
        <div data-win-control="SdkSample.ScenarioInput" class="itemDetail fragment">
            <header aria-label="Header content" role="banner">
                <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to itemDetail</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
                <article>
                    <div class="item-content"></div>
                </article>
            </section>
           
            <div class="backForwards" data-win-control="MyApp.CartBar">
                hi
            </div>
           
            <div class="backForwards" data-win-control="NavigationSample.NavBar"></div>

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

    Here, you can see an item with data-win-control "MyApp.CartBar" which is defined in the following included JavaScript:

    (function () {
        "use strict";

        var settings = Windows.Storage.ApplicationData.current.roamingSettings;
        var myCart;
        var helloString = "hello there";
        var cartItems = [];

        var getMyCart = function () {
            //console.log("ShoppingCart.js called getMyCart()");
            if (!myCart) {
                myCart = settings.values['myCart'];
            }
            if (myCart == undefined) {
                console.log("myCart is undefined");
                myCart = cartItems;
            }
            return myCart;
        }

        var addToCart = function (productid) {
            console.log("adding to cart: " + productid);
            cartItems = getMyCart();
            cartItems.push(
                {
                    key: productid
                }
            );
        }

        var setMyCart = function (newValue) {
            if (myCart != newValue) {
                myCart = newValue;
                settings.values['myCart'] = myCart;
            }
        }

        var page = WinJS.UI.Pages.define("/html/cartFunctionsBar.html", {
            ready: function (element, options) {

                console.log("loading cartFunctionsBar");
                //WinJS.UI.processAll();
                console.log("MyApp.currentProductId: " + MyApp.currentProduct.productId);
                this.addToCartButton = element.querySelector(".addToCart");
              
                this.enableCartButtons();
            },

            enableCartButtons: function () {

              
                console.log("enable cart buttons . current product is : " + MyApp.currentProduct.productId);

                    for (var i = 0, itemsLength = MyApp.Settings.getMyCart().length; i < MyApp.Settings.getMyCart().length; i++) {
                       
                        if (MyApp.currentProduct.productId = MyApp.Settings.getMyCart()[i].key) {
                            this.addToCartButton.setAttribute("disabled", "disabled");
                            return;
                        }
                    }

                    WinJS.Utilities.query(".addToCart", this.element)
                        .listen("click", this.onAdd.bind(this))
                        .forEach(function (e) {
                            e.removeAttribute("disabled");
                        });
                   
              
            },
           
            onAdd: function () {
                console.log("Getting cart contents: " + MyApp.Settings.getMyCart().length + " -- currentProduct: " + MyApp.currentProduct.productId);
                MyApp.Settings.addToCart(MyApp.currentProduct.productId);
                this.addToCartButton.innerText = "Switch to grid";
                this.addToCartButton.setAttribute("disabled", "disabled");

                console.log("Added to cart contents: " + MyApp.Settings.getMyCart().length + " - " + MyApp.Settings.getMyCart()[0].key);
            }


        });

        WinJS.Namespace.define("MyApp", {
            CartBar: page
        });

        WinJS.Namespace.defineWithParent(MyApp, 'Settings', {
            getMyCart: getMyCart,
            setMyCart: setMyCart,
            addToCart: addToCart,
            hello : helloString
        });

    })();

    The productDetail Page JavaScript itself is:

    // For an introduction to the Page Control template, see the following documentation:
    // http://go.microsoft.com/fwlink/?LinkId=232511
    (function () {
        "use strict";
        var currentProduct;
        var page = WinJS.UI.Pages.define("/html/productdetail/1_ProductDetail.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) {
                console.log("loading productdetail >> " + options.selectedProduct.productId);

                MyApp.currentProduct = options.selectedProduct; // options && options.item ? options.item : Data.items.getAt(0);
                element.querySelector(".titlearea .pagetitle").textContent = MyApp.currentProduct.productId;

            },

            unload: function () {
                console.log("navigating away from productDetail");
                // TODO: Respond to navigations away from this page.
            },

            updateLayout: function (element, viewState, lastViewState) {
                /// <param name="element" domElement="true" />

                // TODO: Respond to changes in viewState.
            }


        });


        WinJS.Namespace.define("MyApp", {
            currentProduct: currentProduct
        });


    })();

    The problem now is that, even though the Product clicked always outputs the correct values in the ListView, either the CartBar "subelement" or the productDetail console.log statements output the ID of the Product I clicked before the current one.

    So for ex. I click ProductId 1 - click "Back" in the NavStack - click ProductId 2 - all good

    Then I click ProductId 1 - click "add to cart" - then go back in the NavStack - click ProductId 2 - in that case my output is:

    clicked productId :2
    loading cartFunctionsBar
    MyApp.currentProductId: 1
    enable cart buttons . current product is : 1
    loading productdetail >> 2
    navigating away from productDetail

    So my productDetail loads product 2 (fine), but the included "Bar" still thinks it is loading Id 1.

    And this sometimes happens vice versa.

    When adding "WinJS.UI.processAll()" to the Cart JavaScript upon ready, it did not help.

    Do you see an error here?

    Thanks

    Thomas

     

    Sunday, April 7, 2013 6:12 PM
  • Hi Thomas,

    I would put a break point here and see when it is hit:

               
    MyApp.currentProduct = options.selectedProduct; 

      


    Jeff Sanders (MSFT)

    Monday, April 8, 2013 2:54 PM
    Moderator
  • Hi Jeff,

    I have done so.

    I first load the first product detail -- hits the break point -- all ok -- both in the "cart" component as well as the detail page, the product is 184007. So continue.

    Then added product 184007 to the shopping cart -- then hit the back button on the navStack.

    Then I load another product detail. When it hits the break point, this is the output:

    clicked productId :184009
    loading cartFunctionsBar
    MyApp.currentProductId: 184007
    enable cart buttons . current product is : 184007
    loading productdetail >> 184009

    So for the cartBar JavaScript and subcomponent on the product detail page, the code executes first.

    Why does the included javascript of the "cart" component execute before the page javascript code it is hosted within and is there any way to control this behavior?

    Thanks

    Thomas

    Tuesday, April 9, 2013 8:55 AM
  • Hi Thomas,

    OK I think I am following your logic and what the problem is now.  The win-controls and associated code is parsed first.  Once the code and controls have been loaded, then 'ready' is called for the page.  You need the code to be parsed this way in order for the data-win-controls to function correctly with the page.  Think of the 'ready' event as 'all of my page has created the controls and fragments and the page is done processing.

    If you open base.js and look for this:

    WinJS.Namespace.define("WinJS.UI.Pages", {

    You will see the member functions for the Pages object.

    If I understand your problem description correctly you want to use the init member function to do you processing since this will execute before any control contained within the page is created.

    If I am still misunderstanding you, you can actually debug this yourself by putting some breakpoints in base.js to see when the data-win-control is created in relation to your page creation. 

     

    -Jeff


    Jeff Sanders (MSFT)

    Tuesday, April 9, 2013 12:27 PM
    Moderator
  • Hi Jeff,

    Thank you for that reply. Now I understand what you mean.

    I have moved the code to set the MyApp variable value over to the ListView itemInvoked Event as well as the definition of the variable in the MyApp namespace.

    Then the code behaves correctly. Thank you for the valuable tip on the order of loading: now I understand why my control code executed before the page ready.

    Best regards

    Thomas

    Tuesday, April 9, 2013 12:47 PM