none
Can you bind a Drop Down to a Query or dynamic data in the HTML client? RRS feed

  • Question

  • In the Silverlight client we had the Auto Complete Box that you could bind to either a Query or a Choice List based property.

    In the current HTML client we have the Drop Down control that you can bind to a Choice List based property only from what I can see.

    Questions:

    1/ Can we bind a Drop Down control to a Query?

    2/ Can we dynamically populate a Choice List in Javascript at run-time (this was not possible in the Silverlight client)?

    If neither of the above are possible, what is the recommended way to have Drop Downs that are based on a query or dynamic data?

    Thanks in advance


    Regards, Xander

    Tuesday, May 21, 2013 10:41 PM

Answers

  • Hi,

    Yes, it's a timing issue and you're closed. Here's what I would use in the screen.created.

    myapp.activeDataWorkspace.Main
      .Statuses
      .load()
      .then(function onComplete(data) {
        var choice = screen.findContentItem("Status"),
            values = [];
        data.results.forEach(function (status){
          values.push({
            value: status.Id,
            stringValue: status.Title
          });
        });
        choice.choiceList = values;
        choice.dispatchChange("choiceList");
      }, function onError(error) {
        msls.showMessageBox(error, {
        title: "Error loading Statuses from the backend"
      });
    });

    The key call that fixes the issue is choice.dispatchChange("choiceList");. The rest of the changes are just my compulsion (sorry, could not resist =).

    Best regards,
    Huy


    Wednesday, July 17, 2013 3:42 PM
  • The following solution overcomes this problem in the short term: Javascript to create the content in the dropdown

    You can therefore execute a query against the backend, get the list of values/entities and then inject them into the dropdown as per the linked example.


    Regards, Xander

    • Marked as answer by novascape Tuesday, June 25, 2013 5:38 AM
    • Edited by novascape Tuesday, June 25, 2013 5:41 AM added description
    Tuesday, June 25, 2013 5:38 AM
  • Hi,

    Unfortunately the answer is No for both.

    It should be possible to write your own Custom Control and populate a <select> element with dynamic data or query result.

    Best regards,
    Huy

    • Marked as answer by novascape Friday, May 24, 2013 3:58 AM
    Friday, May 24, 2013 1:38 AM

All replies

  • Hi,

    Unfortunately the answer is No for both.

    It should be possible to write your own Custom Control and populate a <select> element with dynamic data or query result.

    Best regards,
    Huy

    • Marked as answer by novascape Friday, May 24, 2013 3:58 AM
    Friday, May 24, 2013 1:38 AM
  • Thanks Huy. May I respectfully request that both these features are looked at for a future version? Number 2 is one of the most often asked questions about the Silverlight client and would be great for both Silverlight and HTML clients and number 1 is really a must-have in the HTML client.


    Regards, Xander

    Friday, May 24, 2013 4:01 AM
  • Thanks for the feedback Xander, I will pass this back to the team.

    For #1: Unlike Silverlight, HTML <select> element is much more light-weight. Also, for HTML Client we started of with the Visual Collection doing continuous loading, so it was much harder to bind a query to just a Dropdown. The Modal Picker (with a List View control doing continuous loading inside) was a compromise for this scenario. Plus we don't have support for search operation in HTML Client yet, and it's also a requirement to fulfill #1.

    #2 is an easier problem to solve.

    Best regards,
    Huy

    Friday, May 24, 2013 4:17 PM
  • ... for HTML Client we started of with the Visual Collection doing continuous loading, so it was much harder to bind a query to just a Dropdown. The Modal Picker (with a List View control doing continuous loading inside) was a compromise for this scenario ...

    I cover that here:

    Programmatically Constraining a List In a Popup


    The Visual Studio LightSwitch Marketplace

    http://LightSwitchHelpWebsite.com


    Friday, May 24, 2013 4:40 PM

  • Questions:

    1/ Can we bind a Drop Down control to a Query?

    2/ Can we dynamically populate a Choice List in Javascript at run-time (this was not possible in the Silverlight client)?

    If neither of the above are possible, what is the recommended way to have Drop Downs that are based on a query or dynamic data?

    Hi,

    Unfortunately the answer is No for both.

    It should be possible to write your own Custom Control and populate a <select> element with dynamic data or query result.

    Best regards,
    Huy

    For screen controls that are bound to choice-lists and rendered as drop-downs, there are methods that contentItem exposes that are of particular interest for this question:

    for .choicelist, an array is returned which is manipulable with standard JS array functions and displayed.  The .choicesSource would appear to go directly to question #2, however, I have not personally gotten it to work in this regard, perhaps the LS team can shed light on its use?

    Saturday, May 25, 2013 5:09 AM
  • This is probably obvious, but I would like to add that the use case for being able to inject values into the choice list at runtime is where there is only a *short* list of values (e.g. Customer Classification, Order Status, Etc). In these cases you don't need the full blown incremental search functionality provided by the ACB (as provided in the Silverlight client) but you also cannot hardcode the values at design time as they need to be configurable by the user.

    Most business applications have the above requirement. In the Silverlight client we obviously managed by using the ACB, but we need this ability in the HTML client as well. And not as a pop-up.



    Regards, Xander

    Monday, May 27, 2013 11:15 PM
  • The following solution overcomes this problem in the short term: Javascript to create the content in the dropdown

    You can therefore execute a query against the backend, get the list of values/entities and then inject them into the dropdown as per the linked example.


    Regards, Xander

    • Marked as answer by novascape Tuesday, June 25, 2013 5:38 AM
    • Edited by novascape Tuesday, June 25, 2013 5:41 AM added description
    Tuesday, June 25, 2013 5:38 AM
  • A per the link posted above, the following code in the screen activated function does indeed work to dynamically add items to a dropdown (provided you have added the dummy choice list entry in the designer):

    var choice = screen.findContentItem("Status");
    var values = [];
    values[0] = { "value": 1, "stringValue": "New" };
    values[1] = { "value": 2, "stringValue": "In Progress" };
    values[2] = { "value": 3, "stringValue": "Completed" };
    values[3] = { "value": 4, "stringValue": "Cancelled" };
    choice.choiceList = values;

    However, if you replace the above with a query then it doesn't, due to what I believe is a timing issue, as a result of the delay caused waiting for the promise to complete successfully. Btw the code below works correctly, I have tested that the Statuses do come back correctly from the backend and the values[] array do get populated correctly :

    myapp.activeDataWorkspace.Main
      .Statuses
      .filter(null)
      .execute()
      .then(function onComplete(data) {
        var choice = screen.findContentItem("Status");
        var statuses = data.results;
        var values = [];
        for (var i = 0; i < statuses.length; i++) {
          values[i] = { "value": statuses[i].Id, "stringValue": statuses[i].Title };
        }
        choice.choiceList = values;
      }, function onError(error) {
        msls.showMessageBox(error, {
        title: "Error loading Statuses from the backend"
      });
    });

    Any thoughts on how one might overcome the second example above not working?



    Regards, Xander


    • Edited by novascape Wednesday, July 17, 2013 3:23 AM typo
    Wednesday, July 17, 2013 1:49 AM
  • I wonder whether there is a way to call .trigger('create') on the screen.findContentItem("Status") content item? If one could do that in the onComplete() function then JQM might render the dropdown again, reflecting the updated choiceList.

    Regards, Xander

    Wednesday, July 17, 2013 11:07 AM
  • Ok, found the answer about getting the element from the contentItem in this thread: HTML DOM - please generate ID's in the elements and basically the answer is no, you cannot. You have to use the postRender() method of the contentItem in question to get access to the element and then store a reference to that element on the screen object, like so:

    myapp.Home.Status_postRender = function (element, contentItem) {
        contentItem.screen._$Status = $(element);
    };

    That seems to provide access in the onComplete() method above so that you can call:

    screen._$Status.trigger("create");
    once the .choiceList has been set. However, that still doesn't work, so that theory has gone out the window too.


    Regards, Xander

    Wednesday, July 17, 2013 11:52 AM
  • Hi,

    Yes, it's a timing issue and you're closed. Here's what I would use in the screen.created.

    myapp.activeDataWorkspace.Main
      .Statuses
      .load()
      .then(function onComplete(data) {
        var choice = screen.findContentItem("Status"),
            values = [];
        data.results.forEach(function (status){
          values.push({
            value: status.Id,
            stringValue: status.Title
          });
        });
        choice.choiceList = values;
        choice.dispatchChange("choiceList");
      }, function onError(error) {
        msls.showMessageBox(error, {
        title: "Error loading Statuses from the backend"
      });
    });

    The key call that fixes the issue is choice.dispatchChange("choiceList");. The rest of the changes are just my compulsion (sorry, could not resist =).

    Best regards,
    Huy


    Wednesday, July 17, 2013 3:42 PM
  • Can you provide any more information on:

    choice.dispatchChange("choiceList");

    What does it do?

    How does it do it?

    Thanks!


    Unleash the Power - Get the LightSwitch HTML Client book

    http://LightSwitchHelpWebsite.com

    Wednesday, July 17, 2013 3:54 PM
  • Hi,

    As the name suggested, dispatchChange raises a notification to any listeners/subscribers that a property with the given name was changed. So in this case the content item lets the LightSwitch Dropdown control know that the choice list was updated and the control should refresh itself.

    It does it through the change tracking / notification layer implemented throughout HTML Runtime.

    Best regards,
    Huy

    Wednesday, July 17, 2013 4:01 PM
  • Thanks for the clarification on how to use .choiceList, Huy.

    Has anyone been able to figure out the related .choicesSource member property usage?


    Wednesday, July 17, 2013 7:52 PM
  • Thank you Huy, that is very helpful in solving the problem, introducing us to the new dispatchChange() method (which I'm sure will come in handy) and also showing us how to write better Javascript.

    Unless I'm missing something obvious, I've noticed that using the .choiceList property to set the choice list values works for integer choice lists, but not for string choice lists. Do you know whether there is anything special or different that we need to do to get this working with string based choice lists?

    Thanks in advance


    Regards, Xander

    Wednesday, July 17, 2013 11:24 PM
  • Actually, cancel my point/question about this not working for string type choice lists. It indeed does in the same way as integer choice lists.

    My issue was that I had an entity type screen property which I renamed and then added a string type choice list property with the same name as the previous entity type property.

    I think there may be an issue with LS2013 and renaming screen properties since for some reason the newly created string property was still seen by LS as the previous entity type property (EDIT: it may have been me getting confused between the screen data item name vs. the contentItem name, so I don't think there is actually an issue on LS2013 with this).

    Once I created a totally differently named string choice list property it behaved as expected.

    Thanks again for the help.

     

    Regards, Xander


    • Edited by novascape Thursday, July 18, 2013 12:40 AM clarified
    Wednesday, July 17, 2013 11:44 PM
  • @Allen: contentItem.choicesSource is used by the Details Modal Picker only. Let's say we have an Add/Edit screen for Northwind's Order.

    If you select the Customer Details Modal Picker, you can see a setting for Choices.

    • By default it has only one value - "Auto". It means that at runtime, the Modal Picker control will dynamically create a Visual Collection that will load every Customers on the server.
    • Let's say you want the Modal Picker to only load Customers from France, you can add a new Screen Property of type Customers query, customize the query. Now you can pick that property as the 'Choices' for the Modal Picker. At runtime, the content item will look at its binding and setup contentItem.choicesSource to be the screen.CustomersFromFrance visual collection. And the Modal Picker will use that visual collection instead.

    For performance reason, the Modal Picker control only initializes it Visual Collection once, so it's not a good idea to try and change contentItem.choicesSource. But by allowing you to set a custom query in design time, I think it should be flexible enough to customize the Modal Picker dynamically. Also settings the Choices source at Design time allows better validation and error prevention.

    Best regards,
    Huy


    Thursday, July 18, 2013 12:28 AM
  • I found a little gotcha with the above solution to be aware of when loading an existing entity that has existing values set for those choice list properties. If you don't add the following line in bold then the dropdown will display the current integer value as the selected value instead of the stringValue:

        contentItem.choiceList = values;
        contentItem.dispatchChange("choiceList");
        contentItem.dispatchChange("value");       // required to correctly display existing selected value
    

    Huy, please let us know if there is a better way to overcome that or whether the above is indeed the correct way.

    Thanks in advance


    Regards, Xander

    Friday, July 19, 2013 9:49 AM
  • Hi Xander,

    I think that makes sense in your custom timing. In a normal Drop down control, the timing would be:

    1. Control created.
    2. Control look at contentItem.choiceList and populate the dropdown.
    3. Control look at contentItem.value and set the dropdown value.

    In your custom scenario,

    1. Control created.
    2. Dummy contentItem.choiceList with 1 item.
    3. contentItem.value coming from entity does not match, so fall to error handling mode.
    4. Custom code populate contentItem.choiceList. Notify control to populate dropdown.
    5. Need to fake a value change so that the control can set the dropdown value.

    Best regards,
    Huy

    Friday, July 19, 2013 2:41 PM
  • Does anyone know why creating a dynamic choiceList (using code such as above) works in the screen Created method but does not work in the postRender method for the control?  I tested with just adding a few static values.  The same code, adapted to find the contentItem, works great in the created method but doesn't work in the postRender method where there is already a reference to the contentItem???
    • Edited by Hessc Monday, September 12, 2016 7:47 PM
    Monday, September 12, 2016 7:45 PM
  • Hi Hessc,

    The following type of approach can be used in the postRender:

    myapp.AddEditScreen.DropDown_postRender = function (element, contentItem) {
        var view = contentItem._view;
        var updateChoiceList = function () {
            var values = contentItem.choiceList;
            values.splice(0, values.length); // Used instead of = [] in order to preserve the original array instance
            values.push({ value: "", stringValue: "" });
            values.push({ value: 0, stringValue: "Option 1" });
            values.push({ value: 1, stringValue: "Option 2" });
            contentItem.choiceList = values;
            if (view.underlyingControl) {
                view.underlyingControl._refreshView();
            }
            contentItem.isEnabled = (contentItem.choiceList.length > 1);
        };
        if (view && view.isRendered) {
            updateChoiceList();
        } else {
            contentItem.dataBind("_view.isRendered", function (isRendered) {
                if (isRendered) {
                    updateChoiceList();
                }
            });
        }
    };

    If the above doesn't do the trick please let me know - it's a cobbled together example (as we have this type of functionality wrapped up in some helper functions).

    HTH,

    Chris

    • Edited by ChrisCookDev Tuesday, September 13, 2016 6:00 PM
    Monday, September 12, 2016 10:14 PM
  • Thanks Chris!  I need to check out the handy ._view property!!  I will give it a try and post back with results.  +1
    Tuesday, September 13, 2016 3:00 PM