Components communication


#1

Hi I need an advise about how to organize general approach for components that I use in an application.

I have some pre-conditions that I should mention:

  1. Right now the application is old-fashioned multi-page Java application that uses JSP. What we are doing right now is migrating some pieces that we can to canjs. It means that components that I create should have an ability to be used in canjs pages (stache) and in just plain HTML pages with plain JS (or jQuery).

  2. I’m using canjs 2.2.9 I know that its pretty old, but unfortunately this is something that I can’t change in the nearest future.

I created small component (still WIP, but can represent my question):
https://dl.dropboxusercontent.com/u/25491580/can-test/index.html
(Sorry it’s not JSBin because there are different files, folders so it was easier to share in that way, but I can probably post it on Github if its easier than checking sources in devtools)

Basically this is textarea that should count amount of characters, change height and react when character limit exceeds (twitter / facebook like).

I would like to find a way how to react on event that I dispatch here:

isLimitExceeded: {
  type: 'boolean',
  set: function (newValue) {
    if (this.isLimitExceeded !== newValue) {
      can.event.dispatch.call(this, 'super-input:limitStatusChange');
    }
    return newValue;
  }
},

(actually I can implement something different, but event looks straightforward way for organizing communication to me)

The only way that I found is to do something like this:

can.event.on.call(can.viewModel(element.find('super-input')), 'super-input:limitStatusChange', function() {
    // do some logic
});

For parent component I have to do this in parent component inserted event in order to ensure that we have super-input inside:

can.Component.extend({
  tag: 'comment-adder',
  template: can.view('/t_static/can-components/comments/comment/comment-adder/comment-adder.template.stache'),
  viewModel: commentAdderViewModel,
  leakScope: false,
  events: {
    inserted: function (element) {
      can.event.on.call(can.viewModel(element.find('super-input')), 'super-input:limitStatusChange', function () {
        // do some logic
      });
    }
  }
});

And outside…well I guess I just need to ensure that this DOM element exist and do something similar.

This approach looks suspicious to me :slight_smile: Is it correct to do something like that ?
I know that there is better viewBindings available in canjs > 2.3.x but it won’t help if I would like to listen to this event from some other part of page that isn’t can.js component.

Besides if I need to set / get component data from JS that isn’t part of can application I also use similar approach:

can.viewModel($('super-input')).attr('message', 'new message');

Is it ok to do this, because this construciton can.viewModel(element) seems like I use something internal, I shouldn’t build my solution on top of such calls ?


#2

Hi @Lighttree,

Sorry you didn’t get a response sooner!

You’re right, I think there’s a better way to do this. Instead of using events, I would bind to the properties you’re interested in and let the observables take care of the rest.

isLimitExceeded could just be a getter:

isLimitExceeded: {
  type: 'boolean',
  get: function() {
    return this.attr('messageLength') > this.attr('limit');
  }
}

Then you can bind to that property in the parent component, something like: <super-input isLimitExceeded="{isLimitExceeded}" />

Then, in the parent component, instead of setting the event listener up in inserted, your logic can go… wherever it needs to depending on what you want to do. isLimitExceeded will be an observable so anything in your viewModel can access it (this.attr('isLimitExceeded')) and those properties will be refreshed whenever isLimitExceeded changes.

Let me know if that makes sense. If you’re able to provide a JS Bin (or something like it) with your code, I’d be happy to make changes to better explain the ideas. :slight_smile:

Yup, totally ok if you need to access stuff outside of CanJS.


#3

Wow. In the very beginning I was thinking that you are referring to {^to-parent} binding in 2.3.x and I wasn’t able to use it. But I’ve tried this syntax that you provided…and this:
is-limit-exceeded="{isLimitExceeded}" actually shared this property from super-input to parent component.
But…why !? :smiley: I can’t find any place where such behavior described for 2.2.9.


#4

Ok, I think I found the place in 2.3.x docs, where this syntax marked as “deprecated”, but didn’t found in 2.2.9 :slight_smile: