javascriptbootstrap-4meteor-autoform

Where does the 'form-control' class get set when using autoForm, Bootstrap 4?


I am engineering a custom autocomplete type for the afQuickfield in my Meteor project. The issue I am having is that when I set the type="autocomplete" on the afQuickfield, the class="form-control" attribute is missing. For all other field types, it is included.

I grepped form-control across the entire codebases for meteor-autoform as well as the scss and js directories of the bootstrap library. I only see occurances in the changelog and class definitions in scss/css.

I could just include class="form-control" in the afQuickfield definition, but that's a monkey patch and I'm not a fan of cutting corners.

Can anyone help me understand how form-control gets assigned in autoForm fields?

Here's my custom autocomplete code (it's a WIP, so no judging!). I'm just hoping to start with a text element with the proper atts so I can build from there.

// autocomplete.js
AutoForm.addInputType('autocomplete', {
  template: 'afAutocomplete',
  valueOut: function () {
    return this.val()
  },
  valueConverters: {
    stringArray: AutoForm.valueConverters.stringToStringArray,
    number: AutoForm.valueConverters.stringToNumber,
    numberArray: AutoForm.valueConverters.stringToNumberArray,
    boolean: AutoForm.valueConverters.stringToBoolean,
    booleanArray: AutoForm.valueConverters.stringToBooleanArray,
    date: AutoForm.valueConverters.stringToDate,
    dateArray: AutoForm.valueConverters.stringToDateArray
  },
  contextAdjust: function (context) {
    context.atts.autocomplete = 'off'
    return context
  }
})

// autocomplete.html
<template name="afAutocomplete">
  <input type="text" value="{{this.value}}" {{this.atts}} />
</template>

Here's the code from the type="text" field for comparison.

// text.js
AutoForm.addInputType('text', {
  template: 'afInputText',
  valueOut: function () {
    return this.val()
  },
  valueConverters: {
    stringArray: AutoForm.valueConverters.stringToStringArray,
    number: AutoForm.valueConverters.stringToNumber,
    numberArray: AutoForm.valueConverters.stringToNumberArray,
    boolean: AutoForm.valueConverters.stringToBoolean,
    booleanArray: AutoForm.valueConverters.stringToBooleanArray,
    date: AutoForm.valueConverters.stringToDate,
    dateArray: AutoForm.valueConverters.stringToDateArray
  },
  contextAdjust: function (context) {
    if (typeof context.atts.maxlength === 'undefined' && typeof context.max === 'number') {
      context.atts.maxlength = context.max
    }
    return context
  }
})

// text.html
<template name="afInputText">
  <input type="text" value="{{this.value}}" {{this.atts}}/>
</template>

As you can see the two are practically identical, yet the autocomplete version is without the form-control class in the actual HTML output.

Any ideas?


Solution

  • Actually this seems to be a follow-up bug from AutoForm, because it should already throw the following error, when including afInputText:

    There are multiple templates named 'afInputText'. Each template needs a unique name

    What about the missing form-control?

    This is correct in itself, because there is no class attribute on the input within the afAutoComplete template.

    AutoForm built-in input types have no class attributes, because they will be overridden by the themes.

    For example the text input in the Bootstrap 4 theme looks currently like this:

    <template name="afInputText_bootstrap4">
      <input type="text" value="{{this.value}}" {{attsPlusFormControlClass}}/>
    </template>
    

    which itself is defined as helper.

    So how to safely define the template?

    Since there may be one day, where you will move from Bootstrap 4 to Bootstrap 5 you can actually create your component + logic as defined above:

    // autocomplete.js
    AutoForm.addInputType('autocomplete', {
      template: 'afAutocomplete',
      valueOut: function () {
        return this.val()
      },
      valueConverters: {
        stringArray: AutoForm.valueConverters.stringToStringArray,
        number: AutoForm.valueConverters.stringToNumber,
        numberArray: AutoForm.valueConverters.stringToNumberArray,
        boolean: AutoForm.valueConverters.stringToBoolean,
        booleanArray: AutoForm.valueConverters.stringToBooleanArray,
        date: AutoForm.valueConverters.stringToDate,
        dateArray: AutoForm.valueConverters.stringToDateArray
      },
      contextAdjust: function (context) {
        context.atts.autocomplete = 'off'
        return context
      }
    })
    
    // autocomplete.html
    <template name="afAutocomplete">
      <input type="text" value="{{this.value}}" {{this.atts}} />
    </template>
    

    and additionally create a separate template, that overrides the styling:

    <template name="afAutocomplete_bootstrap4">
        <input type="text" class="form-control" value="{{this.value}}" {{attsPlusFormControlClass}} />
    </template>
    

    Important here is the combination of afAutocomplete and _bootstrap4 which is used by AutoForm to determine the current template, as long as either the default template is set to bootstrap4 or the current form uses bootstrap4 as theme.