How can I combine ember-bootstrap and ember-autoresize?

213 views Asked by At

I would like to add the attribute autoresize=true to a textarea that is drawn using ember-bootstrap in order to use ember-autoresize.

As you can see here, this attribute is not bound, so I cannot simply add it to my template.

I've tried to manipulate the yielded control like so:

{{#bs-form formLayout="vertical" model=email as |form|}}
    {{#form.element controlType="textarea" property="textEdit" as |el|}}
        {{el.control autoresize=true}}
    {{/form.element}}
{{/bs-form}}

But this only works for classNames, not attributes.

What is the simplest way of achieving what I'm trying to do?

2

There are 2 answers

6
Andrey Mikhaylov - lolmaus On

There are two ways to do it.

Both examples assume ember-bootstrap and ember-autoresize are installed.

1. Ad-hoc approach: no configuration needed, but less convenient to use

Use "Custom controls" described here.

Here's an example:

{{#bs-form formLayout="vertical" model=email as |form|}}
    {{#form.element controlType="textarea" property="textEdit" as |el|}}
        {{textarea autoresize=true id=el.id value=el.value class="form-control"}}
    {{/form.element}}
{{/bs-form}}

Demo: https://ember-twiddle.com/4860f5d660dceadc804495d2720f69f6?openFiles=templates.application.hbs%2C

2. Robust approach: configuration needed, but more convenient to use

Override the original textarea component.

Here's the path for the Classic project structure. For Pods or Module Unification, the path will be different.

app/components/bs-form/element/control/textarea.js

Inside that file, do what the autoresize textarea component does, but on top of Ember-Bootstrap's textarea component:

import BootstrapTextareaComponent from 'ember-bootstrap/components/bs-form/element/control/textarea';
import AutoResizeMixin from 'ember-autoresize/mixins/autoresize';
import { computed, get } from '@ember/object';
import { isEmpty, isNone } from '@ember/utils'; 

export default BootstrapTextareaComponent.extend(AutoResizeMixin, {
  autoresize: true,
  shouldResizeHeight: true,
  significantWhitespace: true,
  autoResizeText: computed('value', 'placeholder', {
    get() {
      var placeholder = get(this, 'placeholder');
      var value = get(this, 'value');
      var fillChar = '@';

      if (isEmpty(value)) {
        return isEmpty(placeholder) ? fillChar : placeholder + fillChar;
      }

      if (isNone(value)) {
        value = '';
      }

      return value + fillChar;
    }
  })
});

Then you can invoke the Bootstrap textarea component normally:

{{#bs-form model=this formLayout="vertical" as |form|}}
  {{form.element label="Inline" placeholder="Enter description..." property="appName" controlType="textarea"}}
{{/bs-form}}

Demo: https://ember-twiddle.com/693209c5fd4c2eeae765827f42dbd635?openFiles=templates.application.hbs%2C

The above code will enable autoresizing to all Ember-Bootstrap textareas. If you need granular control, you can also remove autoresize: true from the component definition. This will let you enable autoresizing individually by passing autoresize=true into the {{form.element}} component invocation.

2
Redsandro On

It looks like using ember-bootstrap and ember-autoresize together will not work, because even adding the ember-autoresize mixin does not autoresize the textarea, although the mixin is applied successfully as evidenced the class added by ember-autoresize.

Perhaps both plugins trying to manupulate the textarea causes conflicts. Perhaps the latter is not compatible with the former in combination with Ember 3.11.

Alternatively, You can hack a solution together by manipulating data-min-rows on input, like this codepen jquery example.

Quoted for brevity:

// Applied globally on all textareas with the "autoExpand" class
$(document)
    .one('focus.autoExpand', 'textarea.autoExpand', function(){
        var savedValue = this.value;
        this.value = '';
        this.baseScrollHeight = this.scrollHeight;
        this.value = savedValue;
    })
    .on('input.autoExpand', 'textarea.autoExpand', function(){
        var minRows = this.getAttribute('data-min-rows')|0, rows;
        this.rows = minRows;
        rows = Math.ceil((this.scrollHeight - this.baseScrollHeight) / 16);
        this.rows = minRows + rows;
    });

Bootstrap adds jQuery either way. Might as well use it.