-
-
Notifications
You must be signed in to change notification settings - Fork 160
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
Feature idea: supply a decorator API? #144
Comments
@chriskrycho I've been trying to write a trackEvent decorator, and @pzuraq generously provided an outline on how to define this:
This seems like a great candidate for decorators. If ember computed decorators are actively maintained, why not? Only question I have is how best to handle accessing the |
Decorators also have access to the target, so you could add the metrics service injection if it doesn't exist on the target class already. |
So something like this seems okay: // utils/track-event.js
import Ember from 'ember';
const { service } = Ember.inject;
export default function trackEvent(eventCategory, eventAction) {
return (target, name, desc) => {
const descriptor = desc;
const originalValue = descriptor.value;
descriptor.value = function(...args) {
if (!this.get('metrics')) {
this.set('metrics', service());
}
this.get('metrics').trackEvent(eventCategory, eventAction);
originalValue.call(this, ...args);
};
return descriptor;
};
} |
That's alright, but it may be better to do it at class definition time rather than when the action gets called for the first time: import Ember from 'ember';
const { service } = Ember.inject;
export default function trackEvent(eventCategory, eventAction) {
return (target, name, desc) => {
const descriptor = desc;
const originalValue = descriptor.value;
if (!('metrics' in target)) {
target.metrics = service('metrics');
}
descriptor.value = function(...args) {
this.get('metrics').trackEvent(eventCategory, eventAction);
originalValue.call(this, ...args);
};
return descriptor;
};
} This way all instances of the class would get the same injection, as well as subclasses. It may also be a good idea to make this a private key ( |
This would be an optional |
I wonder if #167 is a better solution? I'm finding I'm mostly using the decorator for actions. Anyway, @kellyselden, I think that probably makes sense - Separately, I couldn't get @pzuraq's approach to work (I don't think you can inject services that way, isn't function trackEvent(eventCategory, incAction, incLabel, eventValue) {
return (target, name, desc) => {
const descriptor = desc;
const originalValue = descriptor.value;
descriptor.value = function(...args) {
originalValue.call(this, ...args);
if (!this.get('metrics')) {
this.set('metrics', service());
}
let eventAction = incAction;
let eventLabel = incLabel;
// allow getting prop names for values
if (eventAction) {
const actionIdentifier = this.get(eventAction);
if (actionIdentifier !== undefined) {
eventAction = actionIdentifier;
}
}
if (eventLabel) {
const labelIdentifier = this.get(eventLabel);
if (labelIdentifier !== undefined) {
eventLabel = labelIdentifier;
}
}
this.get('metrics').trackEvent(
'GoogleAnalytics',
{ eventCategory, eventAction, eventLabel, eventValue },
);
};
return descriptor;
};
} |
Looking at the compiled output of the |
This is an older thread, but modifiers provide a suuuuper simple way to do something like this, right in the DOM (for anyone who comes upon this thread!) {{! create button }}
<button {{track-event "Created User" properties=(hash performedBy=this.session.currentUser.username)}}>
Submit
</button>
{{! input, utilizing "focus" }}
<SearchInput {{track-event "Search" "focus" properties=(hash resource="Vehicles")}}> import Modifier from 'ember-modifier';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class TrackEventModifier extends Modifier {
@service metrics;
get eventName() {
// the name of the event to track, first positional argument
//
// {{track-event eventName}}
// ~~~~~~~~
//
return this.args.positional[0];
}
get domEvent() {
// get the optional DOM event name for use on other
//
// {{track-event eventName "focus"}}
// ~~~~~~~
//
return this.args.positional[1] || 'mouseup';
}
get properties() {
// the `properties` named argument passed to the modifier.
// Allows passing custom event properties to the analytics tools
//
// {{track-event eventName properties=(hash prop1=prop1)}}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
return this.args.named.properties;
}
@action
onEventTriggered() {
this.metrics.trackEvent({
event: this.eventName,
...(this.properties || {})
});
}
didInstall() {
this.element.addEventListener(this.domEvent, this.onEventTriggered, true);
}
willRemove() {
this.element.removeEventListener(
this.domEvent,
this.onEventTriggered,
true
);
}
} |
This is obviously a bit out before we could land it, but as I was looking at the current (really solid) API it occurred to me that class method decorators (after ES6-style classes land… 🤞) could make for a really nice API:
That's obviously a very rough idea, and obviously it's quite a ways before we could land it, but I figured I'd throw it out as something to start thinking through – including even whether it's a good idea – ahead of time.
The text was updated successfully, but these errors were encountered: