Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Localized dropdown with countries

    Usecase: I want a dropdown with localized countryname, flag and phone prefix in a form.

    I was hoping for some thoughts about best practice here. For android/ios it's simple, there are tons of libraries on github etc.

    This is what i've come up with:

    1. datasource. This means i have to localize it, which i guess i could, but if i understand that correctly, won't the datasource fmt-tags be parsed for every request?

    2. put resources in a bundle so that it gets compiled into js, and then write code that created the data for the dropdown manually. I will explicitly have to create each item in the list, since each country name resource is a method in the gwt resource bundle

    3. Use Locale and create the list from there. Here i'd also have to manually call each country i want in the list. Also, here there are no phone prefixes.


    Anybody done this? How did you do it?

    Thoughts appreciated.

    #2
    Hi Mathias,

    I have a table in my DB for it.
    Code:
    CREATE TABLE t_language (
        id                  INTEGER
        created_by          INTEGER
        created_at          DATE DEFAULT SYSDATE
        modified_by         INTEGER
        modified_at         DATE DEFAULT SYSDATE
        available           VARCHAR2(1 CHAR)
        iso_639_1_alpha_2   VARCHAR2(2 CHAR)
        english_name        VARCHAR2(100 CHAR)
        french_name         VARCHAR2(100 CHAR)
        german_name         VARCHAR2(100 CHAR)
    );
    Code:
    public class SelectItemLanguage extends SelectItem {
        private I18n i18n = GWT.create(I18n.class);
        final private DataSource languageDS = DataSource.get(DatasourceEnum.T_LANGUAGE.getValue());
    
        public SelectItemLanguage(String name) {
            super(name);
            setTitle(i18n.language());
    
            String localLanguage;
            switch (Helper.getCookieOrBrowserLanguage()) {
            case "fr":
                localLanguage = DatasourceFieldEnum.T_LANGUAGE__FRENCH_NAME.getValue();
                break;
            case "de":
                localLanguage = DatasourceFieldEnum.T_LANGUAGE__GERMAN_NAME.getValue();
                break;
            default:
                localLanguage = DatasourceFieldEnum.T_LANGUAGE__ENGLISH_NAME.getValue();
                break;
            }
    
            setOptionDataSource(languageDS);
            setValueField(DatasourceFieldEnum.T_LANGUAGE__ISO_639_1_ALPHA_2.getValue());
            setDisplayField(localLanguage);
            setSortField(localLanguage);
            setOptionCriteria(new AdvancedCriteria(new Criterion(DatasourceFieldEnum.T_LANGUAGE__AVAILABLE.getValue(), OperatorId.EQUALS, true)));
    
            ListGridField displayField = new ListGridField(localLanguage);
            ListGridField id = new ListGridFieldHidden(DatasourceFieldEnum.T_LANGUAGE__ID.getValue());
            ListGridField iso6391alpha2 = new ListGridFieldHidden(DatasourceFieldEnum.T_LANGUAGE__ISO_639_1_ALPHA_2.getValue());
    
            setPickListFields(displayField, id, iso6391alpha2);
    
            setPickListProperties(new ListGrid() {
                {
                    setDataFetchMode(FetchMode.BASIC);
                }
            });
        }
    }
    You'd have to add columns for other languages you want to support.

    Best regards
    Blama

    Comment


      #3
      Hi,

      sorry that one was about languages.
      My country table is this one:
      Code:
      CREATE TABLE t_country
        (
          id                  INTEGER CONSTRAINT NNC_country_id NOT NULL ,
          created_by          INTEGER CONSTRAINT NNC_country_creaby NOT NULL ,
          created_at          DATE DEFAULT SYSDATE CONSTRAINT NNC_country_creaat NOT NULL ,
          modified_by         INTEGER CONSTRAINT NNC_country_modby NOT NULL ,
          modified_at         DATE DEFAULT SYSDATE CONSTRAINT NNC_country_modat NOT NULL ,
          available           VARCHAR2 (1 CHAR) CONSTRAINT NNC_country_available NOT NULL ,
          shortname_de        VARCHAR2 (40 CHAR) CONSTRAINT NNC_country_shortname_de NOT NULL ,
          shortname_de_ccplus VARCHAR2 (52 CHAR) AS ( shortname_de
          || ' (+'
          || callingcode
          || ')' ) VIRTUAL ,
          fullname_de         VARCHAR2 (60 CHAR) CONSTRAINT NNC_country_fullname_de NOT NULL ,
          post_de             VARCHAR2 (60 CHAR) CONSTRAINT NNC_country_post_de NOT NULL ,
          shortname_en        VARCHAR2 (40 CHAR) CONSTRAINT NNC_country_shortname_en NOT NULL ,
          shortname_en_ccplus VARCHAR2 (52 CHAR) AS ( shortname_en
          || ' (+'
          || callingcode
          || ')' ) VIRTUAL ,
          fullname_en               VARCHAR2 (60 CHAR) CONSTRAINT NNC_country_fullname_en NOT NULL ,
          fullname_orig             VARCHAR2 (80 CHAR) CONSTRAINT NNC_country_fullname_orig NOT NULL ,
          iso_3166_1_alpha_2        VARCHAR2 (2 CHAR) CONSTRAINT NNC_country_iso_3166_1_alpha_2 NOT NULL ,
          iso_3166_1_alpha_2_ccplus VARCHAR2 (12 CHAR) AS ( UPPER(ISO_3166_1_ALPHA_2)
          || ': +'
          || CALLINGCODE ) VIRTUAL ,
          iso_3166_1_alpha_3 VARCHAR2 (3 CHAR) CONSTRAINT NNC_country_iso_3166_1_alpha_3 NOT NULL ,
          lp_codes           VARCHAR2 (3 CHAR) CONSTRAINT NNC_country_lp_codes NOT NULL ,
          callingcode        VARCHAR2 (7 CHAR) CONSTRAINT NNC_country_callingcode NOT NULL
        ) ;
      Java code:
      Code:
      public class ComboBoxItemCountry extends ComboBoxItem {
          private I18n i18n = GWT.create(I18n.class);
      
          final private DataSource countryDS = DataSource.get(DatasourceEnum.T_COUNTRY.getValue());
      
          public ComboBoxItemCountry(String name, boolean showTelephone) {
              super(name);
      
              String locale = LocaleInfo.getCurrentLocale().getLocaleName();
              SC.logInfo("ComboBoxItemCountry started with Locale: " + (locale != null ? locale : ""), "LMS");
              if (locale.length() > 2)
                  locale = locale.substring(0, 2).toLowerCase();
              String shortnameLGFName = null;
              String optionOperationId = null;
      
              if ("en".equals(locale)) {
                  if (showTelephone) {
                      shortnameLGFName = DatasourceFieldEnum.T_COUNTRY__SHORTNAME_EN_CCPLUS.getValue();
                      optionOperationId = OperationIdEnum.T_COUNTRY__FETCHDROPDOWNDATATELEPHONE_EN.getValue();
                  } else {
                      shortnameLGFName = DatasourceFieldEnum.T_COUNTRY__SHORTNAME_EN.getValue();
                      optionOperationId = OperationIdEnum.T_COUNTRY__FETCHDROPDOWNDATACOUNTRY_EN.getValue();
                  }
              } else {
                  if (showTelephone) {
                      shortnameLGFName = DatasourceFieldEnum.T_COUNTRY__SHORTNAME_DE_CCPLUS.getValue();
                      optionOperationId = OperationIdEnum.T_COUNTRY__FETCHDROPDOWNDATATELEPHONE_DE.getValue();
                  } else {
                      shortnameLGFName = DatasourceFieldEnum.T_COUNTRY__SHORTNAME_DE.getValue();
                      optionOperationId = OperationIdEnum.T_COUNTRY__FETCHDROPDOWNDATACOUNTRY_DE.getValue();
                  }
              }
      
              setOptionDataSource(countryDS);
              setOptionCriteria(new AdvancedCriteria(new Criterion(DatasourceFieldEnum.T_COUNTRY__AVAILABLE.getValue(), OperatorId.EQUALS, true)));
              setValueField(countryDS.getPrimaryKeyFieldName());
              setFetchMissingValues(true);
              SettingsCache sc = SettingsCache.getInstance();
              setOptionOperationId(optionOperationId);
              setDisplayField(shortnameLGFName);
              setSortField(shortnameLGFName);
              setDefaultValue(sc.getLongSetting(SettingEnum.DEFAULTCOUNTRYID));
      
              setTextMatchStyle(TextMatchStyle.SUBSTRING);
              setBrowserSpellCheck(false);
      
              // See http://forums.smartclient.com/showthread.php?t=32856
              setAddUnknownValues(false);
              setPickListProperties(new ListGrid() {
                  {
                      setDataFetchMode(FetchMode.BASIC);
                  }
              });
      
              ListGridField shortnameLGF = new ListGridField(shortnameLGFName);
              // must be without escapeHTML in ds.xml.
              ListGridFieldCountryflag countrycodeLGF = new ListGridFieldCountryflag(DatasourceFieldEnum.T_COUNTRY__ISO_3166_1_ALPHA_2.getValue(),
                      i18n.flag(), 25);
      
              setPickListFields(countrycodeLGF, shortnameLGF);
              setPickListHeaderHeight(0);
              setPickListWidth(200);
              setPickListHeight(200);
          }
      }
      Again you would have to add extra columns for additional languages you support in your GUI.
      The setOptionOperationId stuff is only there to reduce the amount of data transferred (has only a outputs="..." in .ds.xml)

      Best regards
      Blama

      Comment


        #4
        Hey, thanks for the inspiration!

        I've thought about DB, but the drawback is that i'd have to database queries all the time for something that's pretty static. I thought it'd might be better if i just had the names in a i18n resource so that the map for the SelectItem can be created in the browser. What do you think?

        BTW, what is that 'settingscache' you're using? Never seen it.

        Also, not sure what you mean by "reduce the amount of data transferred (has only a outputs="..." in .ds.xml)", care to elaborate?

        Comment


          #5
          Hi,

          SettingsCache is my singleton I use to fetch various settings on load, so that they are available synchronously in the application.

          outputs: See the Developer Console RPC Tab for the transmitted data. With outputs I cut the columns for those tables I need to ask often to only the columns I really need in order to save transmitted bytes - for country that is the PK, the flag column, the name column and not createdBy, createdAt, ....

          For the main question: You could also save the data in a client side object like my SettingsCache and singleton only transmit it on load.
          Or you could cacheAllData. I did my approach before I read about it. Basically it should do the same.

          I prefer the database solution over a static one, as you can then change the data on the fly without need to recompile/redeploy. This is even more true in a multi-tenant setting (don't know if this applies to you).

          Best regards
          Blama

          Comment


            #6
            mathias if you need something similar to Blama's SettingsCache, take a look at http://owner.aeonbits.org/

            Comment


              #7
              Blama right, i also have something similar for settings that i get in onload().

              Regarding the country dropdown - right now i have ended up having a ConstantsWithLookup class that holds my i18n strings, and an Enum that holds the lookup keys. I then have a "CountryConstants" static class that iterates through my enum, looks up the names in the lookup class and produces the LinkedHashMap objects (names and icons) that i need for the SelectItem.
              Works fine, but I might change my my mind and move it to DB later :)

              claudio hey thanks for feedback! That library looks really nifty, haven't seen it before. I lean heaviliy on Spring on the server, so i'm using ReloadableResourceBundleMessageSources and PropertyFactoryBeans everywhere, but that library looks smoother.

              Huge thanks for your thoughts and feedback.

              Comment

              Working...
              X