Change data in JavaScript connector

Hi! I have Editor screen with TextArea:

 <vbox id="commentBoxSample" stylename="rcg-gt-block" spacing="true" width="100%" >
                    <textArea id="comment" 
                                     property="comment" 
                                     width="100%" 
                                     height="90px" 
                                     datasource="productDs"
                                     visible="false"/>
        </vbox>

Here is controller:

public class ProductEdit extends AbstractEditor<Product> {
        @Inject
        protected VBoxLayout commentBoxSample;
        @Inject
        private Datasource<Product> productDs;
        @Inject
        private DataManager dataManager;
        @Inject
        private Metadata metadata;

        private List<User> users = new ArrayList<>();

        @Override
        protected void postInit() {
            super.postInit();
            LoadContext<User> userLoadContext = LoadContext.create(User.class);
            userLoadContext.setView("_local")
                    .setQuery(LoadContext.createQuery("select e from sec$User e"));
            users = ProductEdit.this.dataManager.loadList(userLoadContext); // loads 40000+ users
            configureMention();
        }

        protected void configureMention() {
            List<MentionUser> collect = users.stream()
                    .map(user -> {
                    MentionUser mentionUser = new MentionUser(user.getLogin(), user.getFirstName(), "http://placekitten.com/25/25");
                       return mentionUser;
                    })
                    .collect(Collectors.toList());
            List<MentionUser> subList = collect.subList(0, 5);
            Component mentionArea = componentsFactory.createComponent(VBoxLayout.class);
            mentionArea.setId("mentionArea");
            Layout vaadinTextArea = (Layout) WebComponentsHelper.unwrap(mentionArea);
            MentionServerComponent mention = new MentionServerComponent();
            mention.setLoginValues(subList);
            mention.setValueChangeListener(new MentionValueChangeListenerImpl());
            mention.setContentChangeListener(new MentionValueChangeListenerImpl());
            vaadinTextArea.addComponent(mention);
            commentBoxSample.add(mentionArea);
        }
        class MentionValueChangeListenerImpl implements MentionServerComponent.ValueChangeListener, MentionServerComponent.ContentChangeListener {
            @Override
            public List<MentionUser> valueChanged(String newValue) {
                return users.stream()
                            .filter(user -> user.getLogin().toLowerCase().contains(newValue.toLowerCase()))
                            .map(user -> {
                        MentionUser mentionUser = new MentionUser(user.getLogin(), user.getFirstName(), "http://placekitten.com/25/25");
                            return mentionUser;
                            })
                            .collect(Collectors.toList());
            }

            @Override
            public void contentChanged(String content) {
                ProductEdit.this.comment.setValue(content);
            }
        }
    }

Here is JavaScript connector:

> @JavaScript({"jquery.min.js", "bootstrap-typeahead.js", "mention.js", "mention-connector.js"})
> @StyleSheet({"bootstrap.min.css"})
> public class MentionServerComponent extends AbstractJavaScriptComponent {
>     private ValueChangeListener valueChangeListener;
>     public interface ValueChangeListener {
>         List<MentionUser> valueChanged(String userValue);
>     }
> 
>     private ContentChangeListener contentChangeListener;
>     public interface ContentChangeListener {
>         void contentChanged(String content);
>     }
> 
>     public MentionServerComponent() {
>         addFunction("valueChanged", arguments -> {
>             String userMentionValue = arguments.get(0).asString();
>             List<MentionUser> usernameValues = valueChangeListener.valueChanged(userMentionValue);
> 
>             if (usernameValues != null) {
>                 setLoginValues(usernameValues);
>             }
>         });
> 
>         addFunction("writeContent", arguments -> {
>             String content = arguments.get(0).asString();
>             contentChangeListener.contentChanged(content);
>         });
>     }
> 
>     public void setLogins(String[] logins) {
>         getState().logins = logins;
>     }
> 
>     public String[] getLogins() {
>         return getState().logins;
>     }
> 
>     @Override
>     protected MentionState getState() {
>         return (MentionState) super.getState();
>     }
> 
>     @Override
>     public MentionState getState(boolean markAsDirty) {
>         return (MentionState) super.getState(markAsDirty);
>     }
> 
>     public ValueChangeListener getValueChangeListener() {
>         return valueChangeListener;
>     }
> 
>     public void setValueChangeListener(ValueChangeListener listener) {
>         this.valueChangeListener = listener;
>     }
> 
>     public void setLoginValues(List<MentionUser> logins) {
>         getState(false).users = logins.toArray(new MentionUser[0]);
>     }
> 
>     public ContentChangeListener getContentChangeListener() {
>         return contentChangeListener;
>     }
> 
>     public void setContentChangeListener(ContentChangeListener contentChangeListener) {
>         this.contentChangeListener = contentChangeListener;
>     }
> 
> }

Here is state:

   public class MentionState extends JavaScriptComponentState {
    public String[] logins;
    public MentionUser[] users;
    }

And, JavaScript code:

> com_company_jsdemo_web_toolkit_ui_mention_MentionServerComponent = function () {
>     var connector = this;
>     var element = connector.getElement();
>     $(element).html("" +
>         "<div>" +
>         "<textarea id='mention'></textarea>" +
>         "</div>");
>     $(element).css("padding", "5px 10px");
> 
>     var mentionArea = $("#mention", element);
>     mentionArea.css("width", "600px");
> 
>     mentionArea.bind('input propertychange', function () {
>         if (mentionArea) {
>             var resultVal = mentionArea.val();
>             connector.writeContent(resultVal);
> 
>             if(mentionArea.val().charAt(0) === '@'){
>                 resultVal = mentionArea.val().substring(1);
>                 connector.valueChanged(resultVal);
>             }
>         }
>     });
> 
>     connector.onStateChange = function () {
>         var state = connector.getState();
>         var logins = state.users;
>         alert(logins.length);
>         if (logins) {
>             var userList = [];
>             for (var i = 0; i < logins.length; i++) {
>                 userList.push({username: logins[i].username, name: logins[i].name, image: logins[i].image});
>             }
> 
>             $(mentionArea).mention({
>                 delimiter: '@',
>                 users: userList,
>                 typeaheadOpts: {
>                     items: 10
>                 }
>             });
>         }
>     }
> 
> }

I used JavaScript library from Mention.JS

But, JS side does not see new values of MentionState when it is changed by mentionArea.bind() event listener. The content of MentionState.users changes and new values comes into connector.onStateChange function, but ignores them and it works with old data values. I saw similar example on This topic. But there is nothing about dynamically changing state data. Please, help me)

Hello!

Take a look at MentionServerComponent#setLoginValues() method: it invokes getState with false argument, so the connector won’t be updated.

Try to invoke without argument:

public void setLoginValues(List<MentionUser> logins) {
    getState().users = logins.toArray(new MentionUser[0]);
}

Thank you! It helped to change state. But now it does not show new data.
For example, I have user “Marcus”, but when I type “marcus” it show nothing.
image

And when I remove some characters it shows old starting data which came on init of this label:
image

The code is same as before, except for your last remark. Help, please!

Hello!

I cannot see any issues in your code. Could you share a small demo project where the issue is reproduced?

The problem was in library. There is no functionality to refresh data with new values. You can only set full data once on init. Thanks!