none
Modal Picker Alternative (HTML Client) RRS feed

Answers

  • Hi Matt,

    You can accomplish this scenario with the Visual Collection and Show Screen method. Let's go through your screens.

    New Order

    The Modal Picker is not very extensible in this release, so you cannot replace the button click action. It's pretty easy to write a Custom Control that insert a jQuery Mobile Button with an arrow down icon (which is what the Modal Picker is), add data binding code and handle the click event. I left that as an exercise :), but let's just use a Summary Control and a Button. This Button simply launch the Select A Customer screen, passing the Order as the parameter.

    Select A Customer

    This screen can be start of using Add Edit Screen template on the Order entity (so that the Order is a required parameter). Then you can

    • Switch this screen to Browse mode (so that it only shows the OK button).
    • Add Data Item | Add Customers query to this screen, parameterize it and add it as a List.

    Now we write and event handler that will update Order.Customer when Customers selected item is changed.

    myapp.SelectOrderCustomerScreen.created = function (screen) {
        var customersVC = screen.Customers,
            order = screen.Order;
        if (order) {
            // When the Customers Visual Collection selected
            // item is changed, update the order's Customer 
            // and commit the changes, navigating back.
            customersVC.addChangeListener("selectedItem", function () {
                order.Customer = customersVC.selectedItem;
                myapp.commitChanges();
            });
        }
    };

    For the Add Customer button, we write a custom method that will launch the stock Add Customer screen, with a little bit more logic on beforeShown and afterClosed

    myapp.SelectOrderCustomerScreen.AddAndSelectCustomer_execute = function (screen) {
        myapp.showAddEditCustomer(null, {
            beforeShown: function (addScreen) {
                // Create a Customer here in the nested scope.
                var c = new myapp.Customer();
                addScreen.Customer = c;
            },
            afterClosed: function (addScreen, action) {
                // If the user accepted the new customer, 
                // update the selected item,
                // which will invoke the handler above. 
                // Otherwise, the user canceled
                // the new customer, do nothing.
                if (action === msls.NavigateBackAction.commit) {
                    screen.Customers.selectedItem = 
                        addScreen.Customer;
                }
            }
        });
    };

    New Customer

    Nothing special about this screen, just the one added using the default template.

    Sample:

    Here's my sample project if you're interested. http://sdrv.ms/ZXPzPD

    There are Customers from 'USA', 'UK' and 'France'. I leave 'Australia' for you to test your scenario ;)

    Best regards,
    Huy


    Thursday, May 2, 2013 2:57 AM

All replies

  • Hi Matt,

    You can accomplish this scenario with the Visual Collection and Show Screen method. Let's go through your screens.

    New Order

    The Modal Picker is not very extensible in this release, so you cannot replace the button click action. It's pretty easy to write a Custom Control that insert a jQuery Mobile Button with an arrow down icon (which is what the Modal Picker is), add data binding code and handle the click event. I left that as an exercise :), but let's just use a Summary Control and a Button. This Button simply launch the Select A Customer screen, passing the Order as the parameter.

    Select A Customer

    This screen can be start of using Add Edit Screen template on the Order entity (so that the Order is a required parameter). Then you can

    • Switch this screen to Browse mode (so that it only shows the OK button).
    • Add Data Item | Add Customers query to this screen, parameterize it and add it as a List.

    Now we write and event handler that will update Order.Customer when Customers selected item is changed.

    myapp.SelectOrderCustomerScreen.created = function (screen) {
        var customersVC = screen.Customers,
            order = screen.Order;
        if (order) {
            // When the Customers Visual Collection selected
            // item is changed, update the order's Customer 
            // and commit the changes, navigating back.
            customersVC.addChangeListener("selectedItem", function () {
                order.Customer = customersVC.selectedItem;
                myapp.commitChanges();
            });
        }
    };

    For the Add Customer button, we write a custom method that will launch the stock Add Customer screen, with a little bit more logic on beforeShown and afterClosed

    myapp.SelectOrderCustomerScreen.AddAndSelectCustomer_execute = function (screen) {
        myapp.showAddEditCustomer(null, {
            beforeShown: function (addScreen) {
                // Create a Customer here in the nested scope.
                var c = new myapp.Customer();
                addScreen.Customer = c;
            },
            afterClosed: function (addScreen, action) {
                // If the user accepted the new customer, 
                // update the selected item,
                // which will invoke the handler above. 
                // Otherwise, the user canceled
                // the new customer, do nothing.
                if (action === msls.NavigateBackAction.commit) {
                    screen.Customers.selectedItem = 
                        addScreen.Customer;
                }
            }
        });
    };

    New Customer

    Nothing special about this screen, just the one added using the default template.

    Sample:

    Here's my sample project if you're interested. http://sdrv.ms/ZXPzPD

    There are Customers from 'USA', 'UK' and 'France'. I leave 'Australia' for you to test your scenario ;)

    Best regards,
    Huy


    Thursday, May 2, 2013 2:57 AM
  • Huy,

    I just ran that sample, and it is great! This is how it should be. For anyone else interested, run the sample project, browse orders, add an order, browse for a customer, add a new customer, and you will see the new customer get added back.

    Thanks for taking the time to answer this thoroughly – I am sure that it will be useful to others.

    Thanks also for letting me add the first customer for Australia...I feel like a true patriot now.

    Thursday, May 2, 2013 3:30 AM
  • Hi,

    This is exactly what I am looking for; however, when I try to add it to my project the intelisense isnt working.  For example, I added the button - then added the execute code - when I type "myapp." show screen isn't available.  I tried typing it in anyway, but when I run the app it says that method isn't available when I click the button. 

    I am specifically talking about this function for the button:

    myapp.AddEditInventory.ShowEquipmentPicker_execute =

    function(screen) {

        myapp.showBrowseEquipmentPickerScreen();

    };

    Friday, May 31, 2013 4:34 PM
  • Hi Robert,

    Intellisense for JavaScript is flaky, and we do try to have a fix for that in the next release. To look for your myapp.showXYZ, you can open this file Application\Application\Application1.HTMLClient\GeneratedArtifacts\viewModel.js and they should all be listed. For example.

        msls._addToNamespace("msls.application", {
    
            //// These are screen definitions        
    
            BrowseCustomers: $defineScreen(BrowseCustomers, [
                {
                    name: "Customers", kind: "collection", elementType: lightSwitchApplication.Customer,
                    createQuery: function () {
                        return this.dataWorkspace.huynnwData.Customers;
                    }
                }
            ], [
            ]),
    
            BrowseOrders: $defineScreen(BrowseOrders, [
                {
                    name: "Orders", kind: "collection", elementType: lightSwitchApplication.Order,
                    createQuery: function () {
                        return this.dataWorkspace.huynnwData.Orders.expand("Customer").expand("Employee").expand("Shipper");
                    }
                }
            ], [
            ]),
    
            AddEditCustomer: $defineScreen(AddEditCustomer, [
                { name: "Customer", kind: "local", type: lightSwitchApplication.Customer }
            ], [
            ]),
    
            AddEditOrder: $defineScreen(AddEditOrder, [
                { name: "Order", kind: "local", type: lightSwitchApplication.Order }
            ], [
            ]),
    
            //// These are show screen definitions
    
            showBrowseCustomers: $defineShowScreen(function showBrowseCustomers(options) {
                /// <summary>
                /// Asynchronously navigates forward to the BrowseCustomers screen.
                /// </summary>
                /// <param name="options" optional="true">
                /// An object that provides one or more of the following options:<br/>- beforeShown: a function that is called after boundary behavior has been applied but before the screen is shown.<br/>+ Signature: beforeShown(screen)<br/>- afterClosed: a function that is called after boundary behavior has been applied and the screen has been closed.<br/>+ Signature: afterClosed(screen, action : msls.NavigateBackAction)
                /// </param>
                /// <returns type="WinJS.Promise" />
                var parameters = Array.prototype.slice.call(arguments, 0, 0);
                return lightSwitchApplication.showScreen("BrowseCustomers", parameters, options);
            }),
    
            showBrowseOrders: $defineShowScreen(function showBrowseOrders(options) {
                /// <summary>
                /// Asynchronously navigates forward to the BrowseOrders screen.
                /// </summary>
                /// <param name="options" optional="true">
                /// An object that provides one or more of the following options:<br/>- beforeShown: a function that is called after boundary behavior has been applied but before the screen is shown.<br/>+ Signature: beforeShown(screen)<br/>- afterClosed: a function that is called after boundary behavior has been applied and the screen has been closed.<br/>+ Signature: afterClosed(screen, action : msls.NavigateBackAction)
                /// </param>
                /// <returns type="WinJS.Promise" />
                var parameters = Array.prototype.slice.call(arguments, 0, 0);
                return lightSwitchApplication.showScreen("BrowseOrders", parameters, options);
            }),
    
            showAddEditCustomer: $defineShowScreen(function showAddEditCustomer(Customer, options) {
                /// <summary>
                /// Asynchronously navigates forward to the AddEditCustomer screen.
                /// </summary>
                /// <param name="options" optional="true">
                /// An object that provides one or more of the following options:<br/>- beforeShown: a function that is called after boundary behavior has been applied but before the screen is shown.<br/>+ Signature: beforeShown(screen)<br/>- afterClosed: a function that is called after boundary behavior has been applied and the screen has been closed.<br/>+ Signature: afterClosed(screen, action : msls.NavigateBackAction)
                /// </param>
                /// <returns type="WinJS.Promise" />
                var parameters = Array.prototype.slice.call(arguments, 0, 1);
                return lightSwitchApplication.showScreen("AddEditCustomer", parameters, options);
            }),
    
            showAddEditOrder: $defineShowScreen(function showAddEditOrder(Order, options) {
                /// <summary>
                /// Asynchronously navigates forward to the AddEditOrder screen.
                /// </summary>
                /// <param name="options" optional="true">
                /// An object that provides one or more of the following options:<br/>- beforeShown: a function that is called after boundary behavior has been applied but before the screen is shown.<br/>+ Signature: beforeShown(screen)<br/>- afterClosed: a function that is called after boundary behavior has been applied and the screen has been closed.<br/>+ Signature: afterClosed(screen, action : msls.NavigateBackAction)
                /// </param>
                /// <returns type="WinJS.Promise" />
                var parameters = Array.prototype.slice.call(arguments, 0, 1);
                return lightSwitchApplication.showScreen("AddEditOrder", parameters, options);
            })
    
        });
    The //// comments are added by me.

    Best regards,
    Huy

    Tuesday, June 4, 2013 10:14 PM
  • Hi Huy,

    I second Matt in thanking you for the thorough explanation. I learnt quite a bit from it.

    One question though, for Huy, or anyone else who knows. What is the first parameter in the showAddEditCustomer() method used for? You're currently passing null, but I'm left wondering what else could be passed instead.

    Thanks,


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Wednesday, November 13, 2013 1:29 PM
    Moderator
  • Hi Yann,

    A showScreen method signature will be in this format: showScreenABC(param1, param2, ..., paramN, options), where param1..paramN is the parameters you define on the screen, and options is the one hosting beforeShown, afterClosed callback.

    In this case, showAddEditCustomer first parameter can be a Customer, you can see my post above for the generated code.

    If we are editing an existing Customer, then we will pass in that existing Customer to showAddEditCustomer.

    However, in this case, we are adding a new Customer. It is very common to write code to:

    • create the customer.
    • showAddEditCustomer with the new customer passed in

    However, that approach does not take advantage of HTML Client nested change-set. When Add Edit Customer screen is shown, it creates a nested change-set for any changes made on itself, so if you cancel, only the changes made on that screen is reverted. So with the approach above, when user cancels, the new customer still exists, only the changes made on the screen are reverted.

    The better approach is what I did above, and what the runtime did when you wire up a screen to Add new method

    • showAddEditCustomer with a null customer.
    • create a new customer and assign to the screen parameter in beforeShown

    Now when the user cancel the screen, the new customer will also be discarded.

    Best regards,
    Huy

    Wednesday, November 13, 2013 5:31 PM