How to show autocomplete for user mentions in TextArea

Sometimes we need to implement Twitter-like user mentions in text areas:

image

Here you will see how to integrate a third-party JavaScript extension, such as Mention.js.

First of all, you will need to create VAADIN directory in modules/web/web and copy all your JS/CSS assets there.

image

Then declare Java class for your extension in web module sources:

package com.company.demo.mentionjs;

import com.haulmont.cuba.web.widgets.WebJarResource;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.server.AbstractJavaScriptExtension;

import java.util.List;

@JavaScript({
        "vaadin://mentionjs/bootstrap-typeahead.js",
        "vaadin://mentionjs/mention.js",
        "vaadin://mentionjs/mentionjs-extension.js"
})
@StyleSheet("vaadin://mentionjs/bootstrap.min.css")
@WebJarResource({
        "jquery:jquery.min.js"
})
public class MentionJsExtension extends AbstractJavaScriptExtension {
    // extension requires Vaadin TextArea
    public MentionJsExtension(com.vaadin.ui.TextArea target) {
        super(target);
    }

    // API of our extension
    public void setUsers(List<String> logins) {
        getState().logins = logins;
    }

    // override getState methods to enable custom state POJO
    @Override
    protected MentionJsState getState() {
        return (MentionJsState) super.getState();
    }

    @Override
    protected MentionJsState getState(boolean markAsDirty) {
        return (MentionJsState) super.getState(markAsDirty);
    }
}

Here we define all the required CSS/JS resources using @JavaScript and @StyleSheet annotations. Also, we need custom POJO that will represent state of the extension (it will be passed to JS side):

public class MentionJsState extends JavaScriptExtensionState {
    public List<String> logins;
}

After that we can define JS side in mentionjs-extension.js:

// entry point of client side code
// function should be named as FQN of the corresponding Java class
window.com_company_demo_mentionjs_MentionJsExtension = function () {
    var self = this;
    var textArea = self.getElement(self.getParentId());

    // subscribe on state change from server
    self.onStateChange = function () {
        var logins = this.getState().logins;
        if (logins) {
            var users = [];
            for (var i = 0; i < logins.length; i++) {
                users.push({username: logins[i]});
            }
            // call mention JS code
            $(textArea).mention({
                delimiter: '@',
                users: users
            });
        }
    };
};

This code will be loaded and executed after JavaScript extension added to TextArea.

Now, we can enable extension in UI screen as follows:

public class DemoScreen extends Screen {
    @Inject
    protected TextArea<String> messageArea;

    @Subscribe
    protected void onInit(InitEvent event) {
        // get Vaadin UI component from CUBA class
        com.vaadin.ui.TextArea vTextArea = messageArea.unwrap(com.vaadin.ui.TextArea.class);

        // enable extension
        MentionJsExtension extension = new MentionJsExtension(vTextArea);
        extension.setUsers(Arrays.asList("Peter", "Lilith", "John", "Mathew", "Alex"));
    }
}

It will look like:
image

You can find the sample code here: https://github.com/cuba-labs/user-mentions

JavaScript extensions enable you to apply JS code to the existing UI components, e.g. inputs, labels, buttons, etc. Read more about JS integration here: https://vaadin.com/docs/v8/framework/gwt/gwt-javascript.html

6 Likes