Custom Enum (dropdown) Fields and Select2 Fields

This is a question and tutorial combined.  The question is, can I do something similar to Relate inputs like Enum (dropdown) inputs?

So after some scrounging around and figuring out of some custom features and extending features, I've come across the way that will allow you to create custom Enum Dropdown inputs that have select values based on filtered results for SugarCRM 7.~, with OR without a parent dependent dropdown input.  Now that I've found out how to do this, I am now wondering if there is a similar method to do this for select2's or Relate fields.  If someone's done this for Relate fields, that would be great because having a clickable link and the search mechanism makes it worth it.  I figure I can just wrap <a> link around the text value the custom enum type but knowing how to do it with relates would be cool.

This is how I've been able to accomplish custom enum dropdowns, the demonstration has a dropdown of related accounts to the Accounts module.  So in the Opportunities module, the linked Account of that opportunity will have a dropdown in the layout where you can choose a related account.  The second part will show you how to populate the second custom enum dropdown with the contacts from that related account.  Basically, you can repurpose this as you like because it doesn't really matter what it's doing, in so much that you can have any dropdown dependency and filter any result you like in any module.

1.  You MUST extend from 'EnumField' and this is placed in either: /custom/clients/base/fields/ or /custom/modules/<module>/clients/base/fields/  Writing this extra code seems cumbersome at first, if you could just overwrite in record.js, but it's better to do it this way.

You can start off with something really easy in there like:

// in: /custom/modules/<Opportunities>/clients/base/fields/custom-related-accounts

// named: custom-related-accounts.js

({

  extendsFrom: 'EnumField',

  initialize: function(opts) {

    this._super('initialize', [opts]);

    this.type = 'enum';

  }

})

2. Now you have an enum extended custom type, if you were to assign a dropdown type to the above, it would be just your bread and butter enum field and load as normal.  So the trick is to overwrite the loadEnumOptions function:

({

  extendsFrom: 'EnumField',

  initialize: function(opts) {

    this._super('initialize', [opts]);

    this.type = 'enum';

  },

   // all this stuff is standard, keep it, it's interesting to know how they populate their options

   // so you have them available to you, also to take a look, beautify /enum.js in their core code

   loadEnumOptions: function(fetch, callback) {

    var self = this,

        meta = app.metadata.getModule(this.module, 'fields'),

        fieldMeta = meta && meta[this.name] ? meta[this.name] : this.def,

        request;

     // console log these so you know what's being fetched

     console.log([meta, fieldMeta, this.def]);

     this.isFetchingOptions = true;

     // now all you need to do is populate self.items, and call callback(self);

     // Here is where I use the app.api call but for demonstration I'm just creating the object

    var account_id = this.model.get('account_id'); // fetch original linked account_id

     var items = {"":""};

     items[account_id] = "Parent Account";

     this.items = items;

     callback.call(this);

     // and that's it!

  }

})

3. Well what if I wanted to make it dependent on another dropdown?  What I've done is create something that's kind of dynamic, so fields with _parent_c and _child_c, when on `/_child_c/.test(this.name)` is === true then it knows to fetch the parent value and then call another api call to get related contacts.  But you could do this more simply by just doing an if statement:

if (this.name == "child_field") {

  var items = {"":""};

  var parent_val = this.model.get('parent_name');

  // get related contacts through API, pass parent value

  this.items = items;

  callback.call(this);

} else {

  var account_id = this.model.get('account_id');

  // get related account ids and populate this.items

  this.items = items;

  callback.call(this);

}

You would also have to set an on change event at initiation:

initialize: function(opts) {

  this._super('initialize', [opts]);

  this.type = 'enum';

  this._initEvents();

},

_initEvents: function() {

  // so if it's your child id, ensure that you're able to trigger a re-render

  if (this.name == 'your_child_id') {

    this.model.on('parent:module:change', function() {

      this._render();

    });

  } else if (this.name == 'parent_id') {

    // if it's your parent id, set the render and change event to trigger your re-render of the model

    this.on('render', this._triggerParentChange, this);

    this.on('change:' + this.name, this._triggerParentChange, this);

  }

},

_triggerParentChange: function() {

  // this will trigger the re-render of your child object, which will execute loadEnumOptions again

  this.model.trigger('parent:module:change');

}

So in hind sight, it's actually not that difficult.  HOWEVER.  How do you go about creating a custom filtered relate field in a similar kind of fashion where your extending the core functionality?  If done this way, it appears less "hackish".  Can someone advise?  And then I'll update this accordingly.

4.  Now there's one last step.  After you add the fields that you want affected by this new Enum type to the record layout within studio, then you have to update the record.php file with that type.  Once you find your fields you have to add the type like so:

16 =>

array (

'name' => 'vendor_5_parent_c',

'type' => 'customCustom-related-accounts',

'studio' => 'visible',

'label' => 'LBL_VENDOR_5_PARENT_C',

),

So I want the field "vendor_5_parent_c" to be affected by this, and if I wanted to have a child dropdown, I would add the same type to that field as well in record.php (/custom/modules/<Opportunities>/clients/base/views/record/record.php).  What's weird to me, is that it seems I have to append "custom" to the type name: "customCustom..." rather than just the name "custom-related-accounts".  Why did I put that in there?  Because that's how it's named in your cache javascript file.  If you search "custom-related-accounts" in either one of your /cache/javascript/base files, you'll see that that's what it's named.  So I used that and it works.

So I'll ask the question again.  Has someone created something similar with Select2 and Relate fields using the same approach in extending the front end behavior?

Thanks,

Parents
  • Doesn't seem to work in Detail View or Edit View, I can't see dynamically added values there. Here's my code:

    ({

    extendsFrom: 'EnumField',

    initialize: function (options) {

        this._super('initialize', [options]);
    },
    loadEnumOptions: function (fetch, callback) {

        // make sure only source gets modified
        if (this.name == 'source_c') {
            this.isFetchingOptions = true;

            var items = app.lang.getAppListStrings('source_dom')

            var dynamicOptions = { "dynamic": "Dynamic Option" };

            items = {...items, ...dynamicOptions}

            this.items = items;
        }
        else {
            this._super('loadEnumOptions', fetch, callback)
        }
    },

    })



Reply
  • Doesn't seem to work in Detail View or Edit View, I can't see dynamically added values there. Here's my code:

    ({

    extendsFrom: 'EnumField',

    initialize: function (options) {

        this._super('initialize', [options]);
    },
    loadEnumOptions: function (fetch, callback) {

        // make sure only source gets modified
        if (this.name == 'source_c') {
            this.isFetchingOptions = true;

            var items = app.lang.getAppListStrings('source_dom')

            var dynamicOptions = { "dynamic": "Dynamic Option" };

            items = {...items, ...dynamicOptions}

            this.items = items;
        }
        else {
            this._super('loadEnumOptions', fetch, callback)
        }
    },

    })



Children
No Data