canjs / can-component

Custom elements and widgets
https://canjs.com/doc/can-component.html
MIT License
8 stars 8 forks source link

If $element is on a ViewModel, add the component's element to the viewModel #295

Open justinbmeyer opened 6 years ago

justinbmeyer commented 6 years ago

If I have a component with $element as part of its definition as follows:

Component.extend({
  ViewModel: {
    $element: "any",
    width: {
      value({listenTo, resolve}){
        listenTo("$element",(ev, element)=>{ resolve(element.clientWidth) })
      }
    }
  }
});

I would like Component to automatically assign it's element to that ViewModel.

justinbmeyer commented 6 years ago

As a side proposal, I think it would be very cool to make the following work:

Component.extend({
  ViewModel: {
    $element: "any",
    get width(){
      return this.$element && this.$element.clientWidth
    }
  }
});

One way to do this would be to overwrite a LOT of the DOM's properties so they call ObservationRecorder.add(). I'm not sure the DOM can be overwritten in this way. Another alternative might be to add can.getKeyValue to symbols so the following would at least work:

Component.extend({
  ViewModel: {
    $element: "any",
    get width(){
      return get(this, "$element.clientWidth");
    }
  }
});

(where get is using canReflect.getKeyValue() under the hood).

mjstahl commented 6 years ago

My gut tells me this is a bad idea due to violating "the separation of concerns".

I understand there are times when getting direct access to a part of the DOM from VM is necessary, but it should be difficult and we should not encourage the practice.

And by "not encourage", I mean we should just say to people,

Use the DOM APIs if you want to infect your VM with DOM code.

justinbmeyer commented 6 years ago

To be clear, this isn't a "necessary evil". This is the RIGHT thing to do in many cases such as the one I talked about in Bitballs. We should encourage it.

We should encourage it when the DOM needs to be a source of state. This is common for things like an element's width, its scroll position, etc.

It should be discouraged when writing to the DOM, which should be done via stache or within the connected callback.

Finally, in the can-element future of CanJS, there is no distinction between viewModel and element. They are one and the same. This makes sense because the element can have state that needs to be managed.

Finally pt 2, I would like to see some "bad" examples where this gets abused, or somehow this creates worse code than what would have existed without it. Putting it another way, I am very skeptical this is going to lead to worse code than what exists now.

CODE-REaD commented 6 years ago

First, I confess I'm not clear on the proposal. Justin, I would be helped if you would illustrate your statement I would like Component to automatically assign it's element to that ViewModel with an example. IOW, why expose $element in this way?

(It would also help me if you didn't use the word element four times in your example; please try to use at least two different words).

Second, I find this conversation compelling as it involves the problem of hierarchy (i.e., what can be accessed from where) that I seem to keep hitting with CanJS. The more the boundaries can be clarified and explained the better off I (and I suspect others) will be.

Thanks to all.