SugarCRM 7  How can I re-render a multi enum?

I want to change the dropdown options on a multi-enum (descriptors) based on another dropdown (product).

Dependencies and visibility grids don't help here because they don't work on multienum (as far as I know).

I have gotten as far as changing the options property on the filed but it won't render the new list on change.


 
   extendsFrom: 'RecordView',     initialize: function(options){
      this._super('initialize', [options]);
      this.model.on('change:case_product_c', this.toggleDescriptors, this);
   },
   render: function()
   {
      this._super('render');
   },
   toggleDescriptors : function(){
     var case_descriptor_c = this.model.fields['case_descriptor_c'],
     descriptor_dropdown = 'case_'+this.model.get('case_product_c').replace(' ','_') +'_descriptor_dd';
     case_descriptor_c.options = descriptor_dropdown;
     console.log(case_descriptor_c);
   },
                         
The above code works to set the dropdown correctly when the page first loads (the dropdpwn contains the right items based on the selected product of the record being loaded), but when I change the product it changes the case_descriptor_c.options (I have verified this in the log) but not the actual values displayed (when I click in the box I get the old list again)...

I believe I have to render the field again, but I don't know how...
Any hints?

thanks,
FrancescaS
  • Hi FrancescaS,

    I don't know if this is the best solution but when i try and create dynamic dropdown option based on other fields in the view i would do something like this
    ({
    extendsFrom:'RecordView',
    originalDDOption: null,
    initialize: function(opts){
        this._super('initialize',[opts];
        this._setDDOption();
    },
    _setDDOption(){
      var newDDOption = null
       if(!this.originalDDOption){
         newDDOption = this.originalDDOption = _.clone(app.metadata._dev_data.app_list_strings['(list_dom_here)']);
      } else {
         newDDOption = _.clone(this.originalDDOption);
      } 
      //process your dropdown option list here
      app.metadata._dev_data.app_list_strings['(list_dom_here)'] = newDDOption;
      //i always keep a copy of my original dropdown option so that i could revert the option if needed
    }
    })
    Hope this helped in some ways
  • Thanks EvilPeri I think this works, as does mine, when running in the initialize phase because it is before the page is rendered.

    My problem is with the on change, the page is already rendered and I can't get the select2 list to re-render with the new setting.

    Thanks for sharing your solution,
    FrancescaS
  • i have this customization before that there are two dropdown fields and when a value from one dropdown field is changes the display on the other is changed as well, the second dropdown field values are dynamic (these are either a list of account, contact or lead fields) so what i did was something like this

    ({
    extendsFrom: 'EnumField',
    initialize: function(opts){
      this._super('initialize',[opts]);
      this._initEvents();
    }
    _initEvents: function(){
        this._moduleDropdownFieldChange();
        this._fieldDropdownFieldChange();
    }
    //this is the control dropdown where values are Accounts,Contacts,Leads
    _moduleDropdownFieldChange: function() {
       if(this.name == 'templatemodule;){
          this.on('render', this.triggerTemplateFieldChange, this);
          this.model.on('change:'+ this.name, this.triggerTemplateFieldChange,this);
       }
    }
    //child dropdown with dynamic values
    _fieldDropdownFieldChange: function(){
       if(this.name  == 'fieldlist'){
             this.model.on('parent:module:change', function(){
               this.items = {};
               this._render();
             }, this);
       }
    }
    triggerTemplateFieldChange: function(){
       var ddOptions = {'':''}
        //do process here to construct the dropdown options
        app.metadata._dev_data.app_list_strings['fieldlist_list'] = ddOptions
        this.model.trigger('parent:module:change');
    }
    })
    So what this does is when the parent field in this case the templatemodule dropdown field changes we call the triggerTemplateFieldChange which constructs the dropdown option then we trigger a custom event on parent:module:change which in turn sets the value of the child dropdown to empty then re-render it to apply the new dropdown option 
    Hope this helps


  • Thanks, I need to think this through, it looks like you are extending the field type to a new type... I've not messed with field types before so I'll need time to see if this will work for me.

    Again, thank you for all your help, it looks like an option for what I want to do.
    FrancescaS
  • No problem. Also i've placed this in custom/modules/<myModule>/clients/base/fields/enum/enum.js im not using a new type both fields are dropdown and im just extending the enum.js to add in custom logic. 

    hope this helps :)
  • Beautiful! Worked like a charm!
    Thank you so much for sharing your solution!

           
    ({
      extendsFrom: 'EnumField',
      initialize: function(opts){
        this._super('initialize',[opts]);
        this._initEvents();
      },
      _initEvents: function(){
          this._productDropdownFieldChange();
          this._descriptorDropdownFieldChange();
      },
      _productDropdownFieldChange: function() {
         //when product is changed on TS interface
         if(this.name == 'case_product_c'){
            this.on('render', this.triggerDescriptorFieldChange, this);
            this.model.on('change:'+ this.name, this.triggerDescriptorFieldChange,this);
         }
      },
      _descriptorDropdownFieldChange: function(){
         if(this.name  == 'case_descriptor_c'){
               this.model.on('parent:module:change', function(){
                 this.items = {};
                 this._render();
               }, this);
         }
      },
      triggerDescriptorFieldChange: function(){
         var ddOptions = {'':''};
         ddOptions = app.lang.getAppListStrings('case_'+this.model.get('case_product_c').replace('/ /g','_') +'_descriptor_dd');
          //do process here to construct the dropdown options
          app.metadata._dev_data.app_list_strings['case_descriptor_dd'] = ddOptions
          this.model.trigger('parent:module:change');
      },
    })
                                     


    FrancescaS

  • Hi Francesca, Hi Evil,
    the following line
    app.metadata._dev_data.app_list_strings['case_descriptor_dd'] = ddOptions
    override multiselect selectable data, but not really clean the selected data in case_descriptor_dd field.
    it means that when you set "A" in the DrobDown master field, choose "abc" and "def" in the MultiSelect slave field, than switch to "B" the master field, the MultiSelect still have "abc" and "def" selected (though not visible, since they do not belongs to the new ddOptions list... and the following form submit (or other master field switching) cause unwanted behaviours.

    how to do to really clean MultiSelect selected data?

    thank you,
    lorenzo




  • Ah! Good catch Lorenzo!
    I'll have to review that.

    I think adding a set statement to empty the dropdown before re-rendering it may be the way to go...

    Grazie!
    Francesca
  • Hi!

    Sorry to rescue an old thread, but I was trying to implement this in 7.6 and the following line:
    app.metadata._dev_data.app_list_strings['case_descriptor_dd']
    didn't work for me. If I console.log() app.metadata, _dev_data isn't even defined.
    Then I tried this.options because I saw it in some other threads but that didn't work either because this.options is not just a list, it's a more complex object and I couldn't find a way to override the options list.

    I did find something that worked for me:
    this.items = *custom_list*; successfully overrides the enum list and it also solves the problem pointed out by lorenzo. If the old dropdown value is not in the new list, it defaults to one that is.

    Hope this is helpful.

  • Very timely comment Adinu, I just upgraded to 7.6 and mine broke :(

    I get an error: Cannot read property 'app_list_strings' of undefined for that exact line:
    app.metadata._dev_data.app_list_strings['case_descriptor_dd'] 

    And the user gets a notice that the field case_product_c cannot be rendered which is not a particularly correct error considering that it rendered that field just fine, it was the descriptor field that could not be rendered.

    this.items, in my case, doesn't work because I am not trying to replace the items for the case_product_c field but rather the items for the case_descriptor_c field.
    'this', in my case, is the product field which triggers the change in the descriptor field.

    Trying to find a solution... I'll update when I get one.

    thanks,
    FrancescaS