1

I struggle to find a good way to make a custom element communicate with its surroundings. So far, my google-karma is low and I haven't found anything which describes a good architecture for custom elements in Knockout.

As always, what's considered a good architecture depends ;-)

Update: I created a fiddle trying to explain what I have in mind.

In my case:

  • I want the custom element to be truly autonomous.
  • It should just work when added to a view, without errors.
  • It should not communicate directly with any services, or any other outside components at all.

Given a custom element with one save-button, which should be activated only after something has changed, we could pass a coordinator like this:

function CustomElement(params) {
    var coordinator = params.coordinator;
    var enabled = this.enabled = ko.observable(false);

    this.save = function() {
        coordinator.save();
    }

    coordinator.onchange(function(hasChanges) {
        enabled(hasChanges);
    });
}

Where the parent view-model defines the coordinator:

function ParentView() {
    this.coordinator = new CustomElementCoordinator();
}

And pass it via params:

<custom-element params="coordinator: coordinator"></custom-element>

Then I'm thinking that the coordinator should be defined in the same directory as the custom element:

custom-elements
    - custom-element
        - coordinator.js
        - model.js
        - template.html

What do you think? Am I onto something, or do I approach this incorrectly?

Thomas Eyde
  • 3,820
  • 2
  • 25
  • 32
  • But... but... why? To me it sounds like you want to implement the MVC controller (eg, found in Angular) into KO (MVVM); whereas in KO the view does this already (eg it communicates your model's values to the component through the view; what's more, it is able to directly modify your model so the proposed 'onchange' and 'save' are always implicitly executed in observables). So in my eyes, this just constitutes an unnecessary extra step... – webketje Mar 08 '15 at 20:08
  • @Tyblitz, how would you design to custom elements to communicate with each other, or any other component? As to why, I don't want element-a to be coupled to element-b. And if I want two instances of element-a on a view, I don't want events from one to interfere with the other. Let's say three of the elements are different filters, one element is a list, and the main viewmodel references the actual service which executes the query and returns the filtered result. Let's say that the list is also used in other views, but with a differen service and no filter. – Thomas Eyde Mar 09 '15 at 12:44
  • Basically I would use a centralized master VM as coordinator instead of building one for every component, which I think goes well with the [top-down nature of JS/DOM/KO](http://stackoverflow.com/questions/28369613/replace-container-element-when-using-knockout-component/28390636#28390636). Eg, check out [this fiddle](https://jsfiddle.net/kevinvanlierde/c4efpxrc/embedded/result/) which uses a list component and 2 filter components, all with the same template but modified arrays. I hope it kind of accurately portrays the situation you described – webketje Mar 09 '15 at 21:49
  • Interesting! Let's compare notes. [I created a fiddle where I added some extras to show more closely what I have in mind](https://jsfiddle.net/teyde/hdt5b2gn/). In general, I think it's bad practice to declare an observable in one view, then bind to it in another. Also using observables for inter-component communication feels wrong. In the fiddle, I try to remove coupling between components and formalize the communication contract in their own classes. – Thomas Eyde Mar 10 '15 at 12:37
  • That looks really, really neat. I do have some thoughts. Perhaps we should continue [in this topic](http://www.quicktopic.com/51/H/FcpvmLDFYF7uE) I started, not to clutter comments (if this becomes something, you can refer to it in OP). (Reply to your last comment is there too) – webketje Mar 10 '15 at 17:55

1 Answers1

0

In the rather large debate of cross-component communication, I subscribe to the opinion that simpler is better.

What is the oldest, most realiable way of communicating between DOM elements (which components will become, eventually)? Why, events, of course!

Your component should use Knockout to deal with internal state. However, adding an event API can work wonders on the compatibility side. If your entire app is not Knockout driven one day (and, believe me, this might very well happen), your component won't be completely useless.

It's as simple as:

$(window).on("myEvent.myComponent", this.handleMyEvent) //listen to the world
$("#myNode").trigger("finished.myComponent", ["param1", "param2"]) // talk to the world

If you want to grab a hold of your component's node element when it is injected, please take a look at the createViewModel style, as per this question: How to access custom element in a Knockout component?

Community
  • 1
  • 1
Guilherme Rodrigues
  • 2,818
  • 1
  • 17
  • 22
  • I'm really not a fan of string-based events. I've experimented with this and found that the string itself can introduce a hard coupling between custom elements. I see the risk that one custom element may stop working when we remove another one. – Thomas Eyde Mar 11 '15 at 16:05
  • Choose strings in the event names or function names :) – Guilherme Rodrigues Mar 11 '15 at 16:22