Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rendering events #365

Closed
luxzeitlos opened this issue Aug 25, 2018 · 5 comments
Closed

rendering events #365

luxzeitlos opened this issue Aug 25, 2018 · 5 comments

Comments

@luxzeitlos
Copy link

This is meant to be an alternative to modifiers (#353). Also this is a bit a vague idea I just had, and I would love some feedback. If some people like it I will try my best and try to write an RFC for this.

Basically why don't we just introduce rendering events like normal DOM events?

<button DidInsertElement={{action 'didInsertButton'}} WillDestroyElement={{action 'willDestroyButton'}}>Save</button>

This would be very straightforward to teach to everyone who understands closure actions!
The didInsertButton and willDestroyButton actions would receive a synthesized event property as single argument with a target property pointing to the DOM node.

For this example I've used uppercase event names to distinguish from normal events:

<button onclick={{action 'save'}} DidInsertElement={{action 'didInsertButton'}} WillDestroyElement={{action 'willDestroyButton'}}>Save</button>

However maybe we should use the @ sign, because in some ways its simmilar to the difference between arguments and attributes:

<button onclick={{action 'save'}} @didInsertElement={{action 'didInsertButton'}} @willDestroyElement={{action 'willDestroyButton'}}>Save</button>
@chadhietala
Copy link
Contributor

Thanks for putting this together. I'm realizing that I might need to add some more examples on what you could use modifiers for in my original RFC. That being said I have some questions and observations.

  • If you have a template only component does this mean you now need to create a backing class to get the life-cycle events? They do not fire unless a class exists.
  • Do you get access to the underlying element some how?
  • This is reserving HTML attributes names to map on to Ember lifecycle events which I think can be confusing and is technically a breaking change.
  • The usage of the @ sigil as key would expand the usage into HTML space. This may make it harder to explain exactly when @ is used.

@luxzeitlos
Copy link
Author

Thank you very much for your feedback!

you now need to create a backing class to get the life-cycle events? They do not fire unless a class exists.

same behaviour then with actions. Of course you need some place to implement the hooks. But you could even pass the action into the component:

<button DidInsertElement={{action @onInsert}}>...</button>

When passing it in:

<MyButton @onInsert={{action 'foo'}} />

and if you do DidInsertElement={{action 'foo'}} and there is no action foo we can throw the same compile time error then with normal actions.

Do you get access to the underlying element some how?

Absolutely, thats the idea:

actions would receive a synthesized event property as single argument with a target property pointing to the DOM node.

So you can do this:

<button DidInsertElement={{action 'foo}}>Save</button>

with this action:

actions: {
  foo(event) {
    event.target.onclick = () => console.log('clicked');
  }
}

This is reserving HTML attributes names to map on to Ember lifecycle events which I think can be confusing and is technically a breaking change

This is a good point. I'm not sure about this, since AFAIK actually not everything is allowed to be an HTML attribute by the HTML spec. But if we consider it a breaking change we can introduce it only to the new glimmer components and to classic components only with a optional feature. I think its save to assume that not many feature have HTML attributes with DidInsertElement as a name, so its save for them to enable this feature.

The usage of the @ sigil as key would expand the usage into HTML space. This may make it harder to explain exactly when @ is used.

Well, it would save us from the breaking change.

I'm also not sure about this. The current model for the @ sign when passing sign is that @ means argument to the component and without @ its an HTML-attribute. The idea here is to basically think about all tags as some kind of component provided by ember/glimmer vm. And then the @ sign is logical. Not @-things are HTML-attributes and the @-arguments are provided by the component (here ember itself).

Well, this is only a mental model, and not how it needs to be implemented.

@luxzeitlos
Copy link
Author

I've tried to adapt your examples from the RFC:

Performance Marking

<section id="about-us" DidInsertElement={{performance 'mark' 'about-page'}}>
  <h1>About Us</h1>
  {{!-- snip --}}
</section>

And performance is a helper:

import Ember from 'ember';

export function performance([type, marker]/*, hash*/) {
  return () => performance[type](marker);;
}

export default Ember.Helper.helper(performance);

jQuery Widget

  <Datepicker @changeMonth=true @changeYear=true />

Datepicker component:

<input type="date" DidInsertElement={{action 'didInsert'}} WillDestroyElement={{action 'willDestroy'}} />
  import Component from '@ember/component';

  export default Component.extend({
    _normalizeOptions(options) {
      return Object.assign(options, { minDate: 20, maxDate: '+1M +10D' });
    },
    actions: {
      didInsert(event) {
        $(event.target).datepicker(this._normalizeOptions(this.args)); // we dont have this.args yet, but this.attrs.
      },
      willDestroy(event) {
        $(event.target).datepicker('destroy');
      },
    }
  })

Page View Tracking

This is actually easiert with a Modifiert. Because we actually need something for the component:

<TrackImpressionSection @eventCategory="Post">
  <header>Chad liked a post</header>
  <img src="cat.jpg">
  {{!-- Snip --}}
</TrackImpressionSection>

and the component TrackImpressionSection:

<section DidInsertElement={{action 'didInsert'}} WillDestroyElement={{action 'willDestroy'}}>
  {{yield}}
</section>
import { inject as service } from '@ember/service';
import Component from '@ember/component';

export default Component.extend({
  ga: service('google-analytics'),
  init() {
    this._super(...arguments);
    this.eventCategory = undefined;
    this.interSectionObserver = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        this.ga.send('event', 'impression', this.eventCategory);
      });
    });
  },
  didInsert(event) {
    this.interSectionObserver.observe(event.target);
  },

  willDestroyElement(event) {
    this.interSectionObserver.unobserve(event.target);
  }
});

@sandstrom
Copy link
Contributor

I'm doing some issue gardening 🌱🌿 🌷 and came upon this issue. Since it's quite old I just wanted to ask if this is still relevant? If it isn't, maybe we can close this issue?

Element modifiers is out now, do they solve your problem? There is also https://github.com/pzuraq/ember-could-get-used-to-this which you could take a look at.

By closing some old issues we reduce the list of open issues to a more manageable set.

@wagenet
Copy link
Member

wagenet commented Jul 23, 2022

I'm closing this due to inactivity. This doesn't mean that the idea presented here is invalid, but that, unfortunately, nobody has taken the effort to spearhead it and bring it to completion. Please feel free to advocate for it if you believe that this is still worth pursuing. Thanks!

@wagenet wagenet closed this as completed Jul 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants