If you have a mixin which is applied to legacy classes, where the mixin uses an observer to trigger an action on those legacy classes, you should:
Let’s imagine a hypothetical component which supplies a counter when you’re nearing the limits on a text input, by observing a value
property on a text editing component and calling an action . We’ll call it TextCountdown. It’s intended to be mixed into a class which extends Ember’s native Textarea
component.
// app/mixins/text-countdown.js
import { once } from '@ember/runloop';
import { observer } from '@ember/object';
import Mixin from '@ember/object/mixin';
import { isNone } from '@ember/utils';
export default Mixin.create({
// required arguments
maximum: undefined,
// optional arguments
showCountdownAt: 10,
countdownLimit: 0,
onValueChange: observer('value', function() {
once(this, 'valueDidChange');
}),
valueDidChange() {
const remaining = this.maximum - this.value.length;
const showCountdown = remaining <= this.showCountdownAt;
const limitReached = remaining <= this.countdownLimit;
this.updateCountdown(remaining, showCountdown, limitReached);
},
});
Note the implicit dependencies of this mixin, which could be exposed by documentation, but are invisible when looking at the consuming class:
value
property to observeupdateCountdown
method or action on the component the mixin will be applied toThe class you mix it into might look something like this:
// my-app/components/custom-textarea.js
import Textarea from '@ember/component/textarea';
import TextCountdown from 'my-app/mixins/text-countdown';
export default Textarea.extend(TextCountdown, {
// Set a required property for the mixin
maximum: 200,
// Override the values from the mixin
showCountdownAt: 20,
countdownLimit: -1,
});
Using the component in a template then looks something like this:
<!-- my-app/templates/components/custom-textarea.hbs -->
<CustomTextarea
@value={{this.value}}
@updateCountdown={{this.setNewCountdownValues}}
/>
Note that the CustomTextarea
here is a template-less component: all of its functionality is accomplished by extending two other classes in JavaScript.
The first thing we’re going to do is transform this to using native classes, using ember-native-class-codemod. (Note that you will need to have followed the codemod's setup instructions for this to work.)