I noticed today that the array.js field type does not allow duplicates and also does not prevent the user from "adding" a new element once maxItemCount is reached. It does prevent you from "saving" the entity if maxItemCount is exceeded but I wanted to prevent user from even adding a new item once maxItemCount is reached.

In my case, I wanted this field to act like a true array where I don't specify any options in field definitions so that espo allows user to enter custom options.

After some trial and error, I have come up with the following code.

You will see that one needs to override some functions. I'm using espoCRM 8.0.6 at the moment. I hope it helps someone. This is what I have so far. Curious as to why duplicates would not be allowed in a field type "array" anyway..


Code:
define('custom:views/fields/custom-array.js, ['views/fields/array', 'helpers/reg-exp-pattern'], function(Dep, RegExpPattern) {

    return Dep.extend({

        events: _.extend({
            //overide removeValue action
            'click [data-action="removeValue"]': function(e) {
                e.preventDefault();
                e.stopPropagation();
                let $listGroupItem = $(e.currentTarget).closest('div.list-group-item');
                this.removeValue($listGroupItem);
                this.focusOnElement();
            },
        }),

        setup: function() {
            Dep.prototype.setup.call(this);
            // some initialization
        },

        afterRender: function() {
            Dep.prototype.afterRender.call(this);
            // your customizations executed after the field is rendered
        },
        //overide addValueFromUi
        addValueFromUi(value) {
            value = value.trim();

            if (this.noEmptyString && value === '') {
                return;
            }

            if (this.params.pattern) {
                let helper = new RegExpPattern(this.getMetadata(), this.getLanguage());

                let result = helper.validate(this.params.pattern, value, this.name, this.entityType);

                if (result) {
                    setTimeout(() => this.showValidationMessage(result.message, 'input.select'), 10);

                    return;
                }
            }

            // Do not allow adding more than maxItemLength
            if (this.params.maxCount && this.selected.length === (this.params.maxCount)) {

                let msg =
                    this.translate('fieldExceedsMaxCount', 'messages')
                    .replace('{field}', this.getLabelText())
                    .replace('{maxCount}', this.params.maxCount.toString());

                this.showValidationMessage(msg, '.array-control-container');
                return;
            }

            this.addValue(value);

            this.$select.val('');

            this.controlAddItemButton();
        },
        //overide addValue
        addValue(value) {
            let html = this.getItemHtml(value);

            this.$list.append(html);
            this.selected.push(value);
            this.trigger('change');
        },
        //overide removeValue
        removeValue($listGroupItem) {

            let itemIndex = this.$list.children().index($listGroupItem);
            this.$list.children().eq(itemIndex).remove();

            this.selected.splice(itemIndex, 1);
            this.trigger('change');
        }

    });
});​
Attached Files