소스 검색

Add back x-html

Caleb Porzio 3 년 전
부모
커밋
1b7de28e0f

+ 1 - 0
packages/alpinejs/src/directives/index.js

@@ -5,6 +5,7 @@ import './x-model'
 import './x-cloak'
 import './x-init'
 import './x-text'
+import './x-html'
 import './x-bind'
 import './x-data'
 import './x-show'

+ 15 - 0
packages/alpinejs/src/directives/x-html.js

@@ -0,0 +1,15 @@
+import { evaluateLater } from '../evaluator'
+import { directive } from '../directives'
+import { mutateDom } from '../mutation'
+
+directive('html', (el, { expression }, { effect, evaluateLater }) => {
+    let evaluate = evaluateLater(expression)
+
+    effect(() => {
+        evaluate(value => {
+            mutateDom(() => {
+                el.innerHTML = value
+            })
+        })
+    })
+})

+ 2 - 3
packages/alpinejs/src/directives/x-text.js

@@ -1,9 +1,8 @@
-import { evaluateLater } from '../evaluator'
 import { directive } from '../directives'
 import { mutateDom } from '../mutation'
 
-directive('text', (el, { expression }, { effect }) => {
-    let evaluate = evaluateLater(el, expression)
+directive('text', (el, { expression }, { effect, evaluateLater }) => {
+    let evaluate = evaluateLater(expression)
 
     effect(() => {
         evaluate(value => {

+ 29 - 0
packages/docs/src/en/directives/html.md

@@ -0,0 +1,29 @@
+---
+order: 7
+title: html
+---
+
+# `x-html`
+
+`x-html` sets the "innerHTML" an element to the result of a given expression.
+
+> ⚠️ Only use on trusted content and never on user-provided content. ⚠️
+> Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.
+
+Here's a basic example of using `x-html` to display a user's username.
+
+```html
+<div x-data="{ username: '<strong>calebporzio</strong>' }">
+    Username: <span x-html="username"></span>
+</div>
+```
+
+<!-- START_VERBATIM -->
+<div class="demo">
+    <div x-data="{ username: '<strong>calebporzio</strong>' }">
+        Username: <span x-html="username"></span>
+    </div>
+</div>
+<!-- END_VERBATIM -->
+
+Now the `<span>` tag's inner HTML will be set to "<strong>calebporzio</strong>".

+ 24 - 0
packages/docs/src/en/essentials/templating.md

@@ -342,3 +342,27 @@ Similar to `x-if`, `x-for` must be applied to a `<template>` tag. Internally, Al
 As you can see the new `status` variable is available in the scope of the iterated templates.
 
 [→ Read more about `x-for`](/directives/for)
+
+<a name="inner-html"></a>
+## Inner HTML
+
+Alpine makes it easy to control the HTML content of an element with the `x-html` directive.
+
+```html
+<div x-data="{ title: '<h1>Start Here</h1>' }">
+    <div x-html="title"></div>
+</div>
+```
+
+<!-- START_VERBATIM -->
+<div x-data="{ title: '<h1>Start Here</h1>' }" class="demo">
+    <div x-html="title"></div>
+</div>
+<!-- END_VERBATIM -->
+
+Now, Alpine will set the text content of the `<div>` with the element `<h1>Start Here</h1>`. When `title` changes, so will the contents of `<h1>`.
+
+> ⚠️ Only use on trusted content and never on user-provided content. ⚠️
+> Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.
+
+[→ Read more about `x-html`](/directives/html)

+ 0 - 45
packages/docs/src/en/upgrade-guide.md

@@ -35,7 +35,6 @@ Upgrading from Alpine V2 to V3 should be fairly painless. In many cases, NOTHING
 * [`x-spread` is now `x-bind`](#x-spread-is-now-x-bind)
 * [Use global lifecycle events instead of `Alpine.deferLoadingAlpine()`](#use-global-events-now)
 * [IE11 no longer supported](#no-ie-11)
-* [`x-html` has been removed](#no-x-html)
 
 <a name="el-no-longer-root"></a>
 ### `$el` is now always the current element
@@ -302,50 +301,6 @@ One of Alpine's stories for re-using functionality is abstracting Alpine directi
 
 Alpine will no longer officially support Internet Explorer 11. If you need support for IE11, we recommend still using Alpine V2.
 
-<a name="no-x-html"></a>
-### `x-html` has been removed
-
-`x-html` was a seldom used directive in Alpine V2. In an effort to keep the API slimmed down to only valued features, V3 is removing this directive.
-
-You can reproduce this exact functionality using `x-effect` like so:
-
-```html
-<!-- 🚫 Before -->
-<div x-data="{ someHtml: '<h1>...</h1>' }">
-    <div x-html="someHtml">
-</div>
-
-<!-- ✅ After -->
-<div x-data="{ someHtml: '<h1>...</h1>' }">
-    <div x-effect="$el.innerHTML = someHtml">
-</div>
-```
-
-Or, using V3's new custom directive API, it's trivial to polyfill this directive:
-
-```html
-<!-- 🚫 Before -->
-<div x-data="{ someHtml: '<h1>...</h1>' }">
-    <div x-html="someHTML">
-</div>
-
-<!-- ✅ After -->
-<!-- The above will now work with the following script added to the page: -->
-<script>
-    document.addEventListener('alpine:init', () => {
-        Alpine.directive('html', (el, { expression }, { evaluateLater, effect }) => {
-            let getHtml = evaluateLater(expression)
-
-            effect(() => {
-                getHtml(html => {
-                    el.innerHTML = html
-                })
-            })
-        })
-    })
-</script>
-```
-
 ## Deprecated APIs
 
 The following 2 APIs will still work in V3, but are considered deprecated and are likely to be removed at some point in the future.

+ 28 - 0
tests/cypress/integration/directives/x-html.spec.js

@@ -0,0 +1,28 @@
+import { haveText, notHaveText, html, test } from '../../utils'
+
+test('sets html on init',
+    html`
+        <div x-data="{ foo: '<h1>hey</h1>' }">
+            <span x-html="foo"></span>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('hey'))
+    }
+)
+
+test('sets html on update',
+    html`
+        <div x-data="{ foo: '' }">
+            <button x-on:click="foo = '<h1>hey</h1>'">Show "bar"</button>
+
+            <span x-html="foo"></span>
+        </div>
+    `,
+    ({ get }) => {
+        get('span').should(notHaveText('hey'))
+        get('button').click()
+        get('h1').should(haveText('hey'))
+    }
+
+)