Răsfoiți Sursa

Custom directive order (#3307)

* Add failing test

* Allow custom order for custom directives

* Update documentation
Simone Todaro 2 ani în urmă
părinte
comite
258a60a291

+ 17 - 0
packages/alpinejs/src/directives.js

@@ -17,6 +17,23 @@ let directiveHandlers = {}
 
 export function directive(name, callback) {
     directiveHandlers[name] = callback
+
+    return {
+        before(directive) {
+            if (!directiveHandlers[directive]) {
+                console.warn(
+                    "Cannot find directive `${directive}`. "
+                    + "`${name}` will use the default order of execution"
+                );
+                return;
+            }
+            const pos = directiveOrder.indexOf(directive)
+                ?? directiveOrder.indexOf('DEFAULT');
+            if (pos >= 0) {
+                directiveOrder.splice(pos, 0, name);
+            }
+        }
+    }
 }
 
 export function directives(el, attributes, originalAttributeOverride) {

+ 18 - 0
packages/docs/src/en/advanced/extending.md

@@ -224,6 +224,24 @@ Alpine.directive('...', (el, {}, { cleanup }) => {
 
 Now if the directive is removed from this element or the element is removed itself, the event listener will be removed as well.
 
+<a name="custom-order"></a>
+### Custom order
+
+By default, any new directive will run after the majority of the standard ones (with the exception of `x-teleport`). This is usually acceptable but some times you might need to run your custom directive before another specific one.
+This can be achieved by chaining the `.before() function to `Alpine.directive()` and specifing which directive needs to run after your custom one.
+ 
+```js
+Alpine.directive('foo', (el, { value, modifiers, expression }) => {
+    Alpine.addScopeToNode(el, {foo: 'bar'})
+}).before('bind')
+```
+```alpine
+<div x-data>
+    <span x-foo x-bind:foo="foo"></span>
+</div>
+```
+> Note, the directive name must be written without the `x-` prefix (or any other custom prefix you may use).
+
 <a name="custom-magics"></a>
 ## Custom magics
 

+ 15 - 1
tests/cypress/integration/custom-directives.spec.js

@@ -1,4 +1,4 @@
-import { haveText, html, test } from '../utils'
+import { haveText, haveAttribute, html, test } from '../utils'
 
 test('can register custom directive',
     [html`
@@ -49,3 +49,17 @@ test('directives are auto cleaned up',
         get('h1').should(haveText('7'))
     }
 )
+
+test('can register a custom directive before an existing one',
+    [html`
+        <div x-data>
+            <span x-foo x-bind:foo="foo"></span>
+        </div>
+    `,
+    `
+        Alpine.directive('foo', (el, { value, modifiers, expression }) => {
+            Alpine.addScopeToNode(el, {foo: 'bar'})
+        }).before('bind')
+    `],
+    ({ get }) => get('span').should(haveAttribute('foo', 'bar'))
+)