การใช้เนื้อหา › ตัวอย่างสด (live preview)
เอกสาร/การใช้เนื้อหา/ตัวอย่างสด (live preview)

ตัวอย่างสด (live preview)

หน้านี้สอนอะไร

ให้ผู้แก้เห็นหน้าเว็บจริงข้างฟอร์ม อัปเดตตามที่พิมพ์ก่อนบันทึก

ควรอ่านก่อน: ฉบับร่าง เวอร์ชัน และการตั้งเวลา

ตัวอย่างสดข้างฟอร์มตัวอย่างสดข้างฟอร์ม

ผู้แก้ไขเห็นหน้าเว็บจริงอยู่ข้างฟอร์ม และหน้านั้นอัปเดตตามที่พิมพ์ทันทีโดยยังไม่ต้องบันทึก

{
  slug: 'posts',
  preview: ({ doc }) => (doc.slug ? `/posts/${doc.slug}` : null),
  fields: [/* … */],
}

preview คืนค่าหน้าที่แสดงเอกสารนั้น เป็น path บนเว็บหรือ URL แบบเต็ม หรือ null ถ้าเอกสารไม่มีหน้า global ใช้ option เดียวกันได้ จากนั้นหน้า admin จะมีปุ่ม Preview ซึ่งเปิดหน้าเว็บใน frame ข้างฟอร์ม

ทำงานอย่างไร

  1. หน้า admin โหลดหน้าจาก preview ครั้งเดียว
  2. ทุกครั้งที่ฟอร์มเปลี่ยน admin จะส่งข้อมูลในฟอร์มไปที่ server (POST /:collection/:id/preview) ซึ่งคืน เอกสารในรูปแบบเดียวกับการอ่านปกติ คือ populate relationship และไฟล์อัปโหลด และรัน hook afterRead แล้ว โดยไม่บันทึกอะไร
  3. admin ส่งเอกสารนั้นไปให้หน้าเว็บด้วย postMessage แล้วหน้าเว็บ render ใหม่

ดังนั้นหน้าเว็บต้อง render จากเอกสารที่ได้รับ helper ด้านล่างช่วยเชื่อมส่วนนี้ให้

Nuxt

useLivePreview ถูก auto-import ให้แล้ว ส่ง ref ที่เก็บเอกสารให้มัน:

<script setup lang="ts">
import { renderRichText } from '@easy-cms/richtext'
 
const { data: post } = await useFetch(`/api/posts/${useRoute().params.slug}`)
useLivePreview(post)
const html = computed(() => renderRichText(post.value?.body))
</script>

Next.js

โหลดข้อมูลใน Server Component แล้ว render ใน Client Component ด้วย useLivePreview:

'use client'
import { useLivePreview } from '@easy-cms/next/live-preview'
 
export function PostView({ post: initial }: { post: Post }) {
  const post = useLivePreview(initial)
  return <h1>{post.title}</h1>
}

Frontend อื่นๆ

import { subscribeLivePreview } from '@easy-cms/core/live-preview'
 
const stop = subscribeLivePreview((doc) => render(doc), {
  origin: 'https://cms.example.com', // the admin's origin; default: the page's own
})

นอก frame ของหน้า admin helper เหล่านี้ไม่ทำอะไร จึงปล่อยไว้ในโค้ด production ได้

Preview token

เมื่อหน้า admin เปิดตัวอย่าง จะเติม easy-cms-preview=<token> ต่อท้าย URL token นี้เปิดอ่าน ฉบับร่างปัจจุบัน ของเอกสารเดียว (หรือ global เดียว) ได้ 1 ชั่วโมงโดยไม่ต้องเข้าสู่ระบบ frontend ที่อยู่คนละ origin จึงแสดง ฉบับร่างที่ยังไม่เคยเผยแพร่ได้:

import { getPreviewToken } from '@easy-cms/core/live-preview'
 
const token = getPreviewToken() // from ?easy-cms-preview=, or null
const post = await fetch(
  `${api}/posts/${id}?depth=2${token ? `&preview=${token}` : ''}`,
).then((r) => r.json())

กรณีนี้ให้ใส่ id ของเอกสารไว้ใน URL ของ preview เช่น preview: ({ doc }) => \https://www.example.com/preview/posts/${doc.id}\``

ฝั่ง server cms.verifyPreviewToken(token) คืนสิ่งที่ token เปิดได้ ({ collection, id } หรือ { global }) หรือ null ส่วน cms.createPreviewToken({ collection, id }) ใช้สร้าง token เอง เช่น ปุ่ม "แชร์ลิงก์ตัวอย่าง"

ข้อควรรู้

  • ให้ render rich text ฝั่ง client ด้วย (renderRichText จาก @easy-cms/richtext ใช้ในเบราว์เซอร์ได้) เพราะเอกสารที่ส่งมามี rich text เป็น JSON
  • เมื่อใช้ adapter ของ Nuxt และ Next หน้าเว็บกับหน้า admin อยู่ origin เดียวกันและใช้ session เดียวกัน หน้าที่อ่าน ฉบับร่างให้ผู้ใช้ที่เข้าสู่ระบบ (อย่างที่ตัวอย่างทำ) จึงไม่ต้องใช้ token
  • preview token เปิดได้เฉพาะเอกสารที่สร้างให้ หมดอายุใน 1 ชั่วโมง และถ้าถูกแก้ไขจะใช้ไม่ได้ ให้ถือว่า URL ของ preview เป็นลิงก์แชร์ชั่วคราว
  • ดูตัวอย่างได้เฉพาะผู้ใช้ที่มีสิทธิ์แก้ไขเอกสารนั้น (หรือสิทธิ์สร้าง สำหรับเอกสารใหม่)

ขั้นต่อไป