⚠️ �� This addon has been deprecated in favor of the officialember-modifier
addon. See the upgrade guide.
This is the next iteration of ember-oo-modifiers with some breaking changes to the API. If you are looking for the previous documentation, select the version you are using in the tags dropdown at the top of the page. For a list of API differences, see here
This addon provides a class-based API for authoring element modifiers in Ember, similar to the class-based helper API.
This is currently compatible with:
ember install ember-class-based-modifier
This addon does not provide any modifiers out of the box; instead, this library allows you to write your own.
Much of this addon was based on ember-oo-modifiers, and, in turn, ember-functional-modifiers.
For example, let's say you want to implement your own {{scroll-position}}
modifier (similar to this).
This modifier can be attached to any element and accepts a single positional argument. When the element is inserted, and whenever the argument is updated, it will set the element's scrollTop
property to the value of its argument.
// app/modifiers/scroll-position.js
import Modifier from 'ember-class-based-modifier';
export default class ScrollPositionModifier extends Modifier {
get scrollPosition() {
// get the first positional argument passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~
//
return this.args.positional[0];
}
get isRelative() {
// get the named argument "relative" passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~~
//
return this.args.named.relative
}
didReceiveArguments() {
if(this.isRelative) {
this.element.scrollTop += this.scrollPosition;
} else {
this.element.scrollTop = this.scrollPosition;
}
}
}
Usage:
// app/components/scroll-container.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class ScrollContainerComponent extends Component {
@tracked scrollPosition = 0;
@action scrollToTop() {
this.scrollPosition = 0;
}
}
If the functionality you add in the modifier needs to be torn down when the modifier is removed, you can use the willRemove
hook.
For example, if you want to have your elements dance randomly on the page using setInterval
, but you wanted to make sure that was canceled when the modifier was removed, you could do this:
// app/modifiers/move-randomly.js
import { action } from '@ember/object';
import Modifier from 'ember-class-based-modifier';
const { random, round } = Math;
const DEFAULT_DELAY = 1000;
export default class MoveRandomlyModifier extends Modifier {
setIntervalId = null;
get delay() {
// get the named argument "delay" passed to the modifier
//
// {{move-randomly delay=@someNumber}}
// ~~~~~~~~~~~
//
return this.args.named.delay || DEFAULT_DELAY;
}
@action moveElement() {
let top = round(random() * 500);
let left = round(random() * 500);
this.element.style.transform = `translate(${left}px, ${top}px)`;
}
didReceiveArguments() {
if (this.setIntervalId !== null) {
clearInterval(this.setIntervalId);
}
this.setIntervalId = setInterval(this.moveElement, this.delay);
}
willRemove() {
clearInterval(this.setIntervalId);
this.setIntervalId = null;
}
}
Usage:
You can also use services into your modifier, just like any other class in Ember.
For example, suppose you wanted to track click events with ember-metrics
:
// app/modifiers/track-click.js
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Modifier from 'ember-class-based-modifier';
export default class TrackClickModifier extends Modifier {
@service metrics;
get eventName() {
// get the first positional argument passed to the modifier
//
// {{track-click "like-button-click" page="some page" title="some title"}}
// ~~~~~~~~~~~~~~~~~~~
//
return this.args.positional[0];
}
get options() {
// get the named arguments passed to the modifier
//
// {{track-click "like-button-click" page="some page" title="some title"}}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
return this.args.named;
}
@action onClick() {
this.metrics.trackEvent(this.eventName, this.options);
}
didInstall() {
this.element.addEventListener('click', this.onClick, true);
}
willRemove() {
this.element.removeEventListener('click', this.onClick, true);
}
}
Usage:
If you would like to use Ember.Object
based APIs, such as this.get
, this.set
, this.setProperties
, etc, you can import the "classic" base class instead, located at the import path ember-class-based-modifier/classic
.
The examples above can be rewritten using the classic API:
// app/modifiers/scroll-position.js
import { computed } from '@ember/object';
import Modifier from 'ember-class-based-modifier/classic';
export default Modifier.extend({
scrollPosition: computed('args.positional.[]', function() {
// get the first positional argument passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~
//
return this.args.positional[0];
}),
isRelative: computed('args.named.relative', function() {
// get the named argument "relative" passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~~
//
return this.args.named.relative;
}),
didReceiveArguments() {
if(this.isRelative) {
this.element.scrollTop += this.scrollPosition;
} else {
this.element.scrollTop = this.scrollPosition;
}
}
});
// app/modifiers/move-randomly.js
import { action, computed } from '@ember/object';
import Modifier from 'ember-class-based-modifier/classic';
const { random, round } = Math;
const DEFAULT_DELAY = 1000;
export default Modifier.extend({
init() {
this._super(...arguments);
this.set('setIntervalId', null);
},
delay: computed('args.named.delay', function() {
// get the named argument "delay" passed to the modifier
//
// {{move-randomly delay=@someNumber}}
// ~~~~~~~~~~~
//
return this.args.named.delay || DEFAULT_DELAY;
}),
moveElement: action(function() {
let top = round(random() * 500);
let left = round(random() * 500);
this.element.style.transform = `translate(${left}px, ${top}px)`;
}),
didReceiveArguments() {
let setIntervalId = this.get('setIntervalId');
if (setIntervalId !== null) {
clearInterval(setIntervalId);
}
setIntervalId = setInterval(this.moveElement, this.get('delay'));
this.set('setIntervalId', setIntervalId);
},
willRemove() {
clearInterval(this.setIntervalId);
this.setIntervalId = null;
}
});
// app/modifiers/track-click.js
import { action, computed } from '@ember/object';
import { inject as service } from '@ember/service';
import Modifier from 'ember-class-based-modifier/classic';
export default Modifier.extend({
metrics: service(),
eventName: computed('args.positional.[]', function() {
// get the first positional argument passed to the modifier
//
// {{track-click "like-button-click" page="some page" title="some title"}}
// ~~~~~~~~~~~~~~~~~~~
//
return this.args.positional[0];
}),
options: computed('args.named', function() {
// get the named arguments passed to the modifier
//
// {{track-click "like-button-click" page="some page" title="some title"}}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
return this.args.named;
}),
onClick: action(function() {
this.metrics.trackEvent(this.get('eventName'), this.get('options'));
}),
didInstall() {
this.element.addEventListener('click', this.onClick, true);
}
willRemove() {
this.element.removeEventListener('click', this.onClick, true);
}
});
Whenever possible, it is recommended that you use the default "modern" API instead of the classic API.
Using the "modern" native class API, you can use .ts
instead of .js
and it'll just work, as long as you do runtime checks to narrow the types of your args when you access them.
// app/modifiers/scroll-position.ts
import Modifier from 'ember-class-based-modifier';
export default class ScrollPositionModifier extends Modifier {
// ...
}
But to avoid writing runtime checks, you can extend Modifier
with your own args, similar to the way you would define your args for a Glimmer Component.
// app/modifiers/scroll-position.ts
import Modifier from 'ember-class-based-modifier';
interface ScrollPositionModifierArgs {
positional: [number],
named: {
relative: boolean
}
}
export default class ScrollPositionModifier extends Modifier<ScrollPositionModifierArgs> {
get scrollPosition(): number {
// get the first positional argument passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~
//
return this.args.positional[0];
}
get isRelative(): boolean {
// get the named argument "relative" passed to the modifier
//
// {{scoll-position @someNumber relative=@someBoolean}}
// ~~~~~~~~~~~~
//
return this.args.named.relative
}
didReceiveArguments() {
if(this.isRelative) {
this.element.scrollTop += this.scrollPosition;
} else {
this.element.scrollTop = this.scrollPosition;
}
}
}
See this pull request comment for a full discussion about using TypeScript with your Modifiers.
element
args
:
{ positional: Array, named: Object }
args.positional
is an array of positional arguments, and
args.named
is an object containing the named arguments.
isDestroying
true
if the modifier is in the process of being destroyed, or has already been destroyed.
isDestroyed
true
if the modifier has already been destroyed.
constructor(owner, args)
(or
init()
in classic API)
super(...arguments)
(or
this._super(...arguments)
in classic API) before performing other initialization. The
element
is not yet available at this point (i.e. its value is
null
during construction).
didReceiveArguments()
didUpdateArguments()
didReceiveArguments
.
didInstall()
didReceiveArguments
.
willRemove()
willDestroy()
willRemove
. The
element
is no longer available at this point (i.e. its value is
null
during teardown).
Install | Update | Remove | this.element |
this.args |
|
---|---|---|---|---|---|
constructor() |
(1) |
|
|
|
after super() |
didUpdateArguments() |
|
(1) |
|
|
|
didReceiveArguments() |
(2) | (2) |
|
|
|
didInstall() |
(3) |
|
|
|
|
willRemove() |
|
|
(1) |
|
|
willDestroy() |
|
|
(2) |
|
|
ember-class-based-modifier
.Modifier.modifier()
function.ember-class-based-modifier/classic
.this.args
.didInsertElement
to didInstall
and willDestroyElement
to willRemove
. This is to emphasize that when the modifier is installed or removed, the underlying element may not be freshly inserted or about to go away. Therefore, it is important to perform clean-up work in the willRemove
to reverse any modifications you made to the element.didReceiveArguments
fires before didInstall
, and didUpdateArguments
fires before didReceiveArguments
, mirroring the classic component life-cycle hooks ordering.willDestroy
, isDestroying
and isDestroyed
with the same semantics as Ember objects and Glimmer components.See the Contributing guide for details.
This project is licensed under the MIT License.
ember-native-class-codemod A codemod for transforming your ember app code to native JavaScript class syntaxwith decorators! Usage First, install the dependencies that the codemod relies on. These area
@AspectJ指的是将方面声明为使用Java 5注释注释的常规Java类的样式。 通过在基于XML模式的配置文件中包含以下元素来启用@AspectJ支持。 <aop:aspectj-autoproxy/> 您还需要在应用程序的类路径上使用以下AspectJ库。 这些库位于AspectJ安装的“lib”目录中,否则您可以从Internet下载它们。 aspectjrt.jar aspectjwe
到目前为止,您已经了解了我们如何使用XML配置文件配置Spring bean。 如果您对XML配置感到满意,那么实际上不需要了解如何继续使用基于Java的配置,因为您将使用任一可用配置获得相同的结果。 基于Java的配置选项使您可以在没有XML的情况下编写大部分Spring配置,但是在本章中介绍的几个基于Java的注释的帮助下。 @Configuration&@Bean Annotations 使
最近Clear应用很火,有一部分原因是其超炫的列表交互效果,用户可以用手指手势来直接对列表进行编辑。这份代码实现了Clear应用中大部分的列表交互效果,包括,手指划动列表行来更新列表,手指划动列表行来删除列表行,下拉新增列表行,pinch移动列表来插入列表行等等。 [Code4App.com]
问题内容: 我希望能够自动连接单例bean() 由另一个单例方法()创建 使用xml就是。我该如何使用注释? 问题答案: 尝试使用Java
Tiny Drive is a premium TinyMCE plugin for cloud-based asset management and storage solution. Tiny Drive is a solution for creating rich text by attaching and embedding images, videos, and other files