Search code examples
jsfiddlejsrenderjsviews

Complex object in a dropdown using JSViews


I am working on project with JSViews, Observables and TypeScript. I planned to handle several languages so I have an object that holds french and english version. A class with static methods that returns the collection of names and an array with all the static objects.

I wanted to display the objects in a dropdown with a converter to fetch the english name, I managed to fill the dropdown and react on change but I can't display the current item in the dropdown and I don't see what is missing.

Could you please help ? I made a javascript sample here : https://jsfiddle.net/ClaudeVernier/v093uqg0/

var data = new dataModel();
    for (var member in Harbors) {
  if (typeof Harbors[member] == "object" && Harbors[member].name) {
    data.harbors.push(Harbors[member]);
  }
}

var myTmpl = $.templates("#harborTmpl");
myTmpl.link("#container", data);
    $.observe(data, "*", myHandler);

Then, I'll need to figure how to change language on the click of a button, if you have idea on that... it would help :-)

Many thanks, Claude


Solution

  • Take a look at Data-linked <select> elements - including the section <select> with converters.

    Your code:

    <select id="cboHarbor" data-link="{{getName:activeHarbor}}">
    

    is incorrect. You need to data-link to activeHarbor. If activeHarbor was a string, you could data-link using:

    <select id="cboHarbor" data-link="activeHarbor">
    

    but since it is an object you need to have some kind of string-valued property for each harbor object, that you can then use for each option value. Then you will use converters to convert back and forth between the activeHarbor object and its string value property for the data-link binding behavior.

    For example you can use the name in the current language as string value, but it is a bit strange to use a value that changes based on current language. But you need a getHarbor converter to convert back to get from the name to the harbor object. That would look like this:

    <select id="cboHarbor" data-link="{getName:activeHarbor:getHarbor}">
      {^{for harbors}}
        <option data-link="{getName:}"></option>
      {{/for}}
    </select>
    

    Alternatively you can use the index of the harbor in the array, like this:

    <select id="cboHarbor" data-link="{getIndex:activeHarbor:getHarbor}">
      {^{for harbors}}
        <option value="{{:#index}}" data-link="{getName:}"></option>
      {{/for}}
    </select>
    

    with converters as follows:

    $.views.converters({
      getIndex: function (harbor) {
        return harbor.index;
      },
      getHarbor: function (index) {
        return data.harbors[index];
      },
      getName: function (harbor) {
        return harbor.name[data.languages[data.currentLanguage]];
      }
    });
    

    If you want to be able to dynamically change language and have the harbors drop-down switch to the new language, then you must make your getName converter depend on the current language, like this:

    $.views.converters.getName.depends = [data, "currentLanguage"];
    

    Here is an updated version of your jsfiddle complete with a language drop-down to switch language.

    UPDATE:

    Concerning the depends for getName, a modified jsFiddle has this:

    function getName(harbor) {
      return harbor.name[data.languages[data.currentLanguage]];
    }
    
    $.views.converters({
      getName: getName,
      ...
    });
    
    getName.depends = [data, "currentLanguage"];
    

    So you can simply use a getName function as your converter function, and then in the context in which you have access to the data instance (in a done() if it needs to be async), you then assign the depends:

    getName.depends = [data, "currentLanguage"];
    

    No need to use $.views.converters.getName.depends