آموزش جامع Alpine.js
فریمورک سبک و قدرتمند جاوااسکریپت برای ساخت رابطهای کاربری ریکتیو — بدون کامپایلر، بدون باندلر، فقط یک فایل کوچک.
🏭 معرفی Alpine.js
Alpine.js یک فریمورک جاوااسکریپت بسیار سبک و قدرتمند است که توسط Caleb Porzio ساخته شده است. این فریمورک به شما امکان میدهد رفتارهای ریکتیو (واکنشپذیر) را مستقیماً در HTML خود بنویسید — دقیقاً مثل Vue یا React، اما با حجم بسیار کمتر (فقط حدود ۱۵ کیلوبایت gzipped).
Alpine.js ایدهآل است برای پروژههایی که به تعاملات کوچک نیاز دارند — مثل منوهای کشویی، مودالها، تبها، فرمهای اعتبارسنجی — بدون اینکه به یک SPA کامل نیاز باشد.
مقایسه با Vue و React
| ویژگی | Alpine.js | Vue.js | React |
|---|---|---|---|
| حجم (gzipped) | ~۱۵KB | ~۳۳KB | ~۴۵KB+ |
| نیاز به بیلد | ندارد | اختیاری | دارد |
| منحنی یادگیری | خیلی کم | متوسط | زیاد |
| مناسب برای SPA | خیر | بله | بله |
| نصب با CDN | بله (یک اسکریپت) | بله | محدود |
| مناسب برای صفحات چندتایی | عالی | متوسط | ضعیف |
چه زمانی از Alpine.js استفاده کنیم؟
- وقتی سایت شما سرور-رندر است (Laravel, Django, Rails, PHP) و فقط به تعاملات کوچک نیاز دارید
- وقتی میخواهید Alpine را به HTML موجود اضافه کنید بدون بازنویسی
- وقتی نمیخواهید با webpack, npm یا build pipeline درگیر شوید
- برای افزودن dropdowns، modals، tabs، accordions و فرمهای پویا
نصب Alpine.js
سه روش برای نصب Alpine.js وجود دارد:
<!-- روش ۱: CDN (پیشنهادی برای شروع) --> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- روش ۲: فایل محلی (همین صفحه) --> <script defer src="alpine.min.js"></script> <!-- روش ۳: npm --> // npm install alpinejs import Alpine from 'alpinejs' window.Alpine = Alpine Alpine.start()
defer استفاده کنید تا Alpine بعد از لود DOM اجرا شود. بدون defer، Alpine ممکن است عناصر HTML را نبیند.
دموی اولیه — شمارنده ساده
<!-- تنها چند خط HTML کافی است! --> <div x-data="{ count: 0 }"> <p x-text="count"></p> <button @click="count--">کاهش</button> <button @click="count++">افزایش</button> </div>
📃 x-data و x-text
x-data قلب Alpine.js است. این دایرکتیو یک «حوزه داده» (scope) ریکتیو تعریف میکند. هر عنصر HTML که x-data داشته باشد، به یک کامپوننت Alpine تبدیل میشود و تمام فرزندانش به دادههای آن دسترسی دارند.
ساختار x-data
<!-- داده ساده --> <div x-data="{ name: 'علی', age: 25 }"> ... </div> <!-- با متدها --> <div x-data="{ count: 0, increment() { this.count++ }, decrement() { this.count-- }, reset() { this.count = 0 } }"> ... </div> <!-- بدون داده (فقط رفتار) --> <div x-data> ... </div>
x-text — نمایش داده
x-text محتوای متنی یک عنصر را به یک مقدار ریکتیو متصل میکند. هر بار که داده تغییر کند، متن خودکار بهروز میشود.
<p x-text="'سلام ' + name"></p> <span x-text="price * quantity + ' تومان'"></span> <div x-text="items.length + ' آیتم'"></div>
x-html — محتوای HTML
<div x-html="htmlContent"></div> <!-- خروجی HTML رندر میشود، نه متن خام -->
x-html فقط با دادههای مطمئن استفاده کنید. ورودی کاربر را هرگز بدون sanitize در x-html نگذارید (XSS).
دموی شمارنده پیشرفته با متدها
وضعیت:
<div x-data="{ count: 0, step: 1, increment() { this.count += this.step }, decrement() { this.count -= this.step }, reset() { this.count = 0 } }"> <p x-text="count"></p> <select x-model="step"> <option value="1">۱</option> <option value="5">۵</option> </select> <button @click="decrement()">کاهش</button> <button @click="increment()">افزایش</button> </div>
Alpine.data() تعریف کنید تا کد HTML تمیزتر بماند و بتوان از آنها بارها استفاده کرد.
x-init — اجرا هنگام راهاندازی
x-init یک expression است که دقیقاً یکبار، وقتی کامپوننت Alpine مقداردهی اولیه میشود اجرا میگردد. برای fetch داده، تنظیم state اولیه، یا راهاندازی third-party کتابخانهها ایدهآل است.
<!-- روش ۱: inline در x-data --> <div x-data="{ users: [], init() { fetch('/api/users') .then(r => r.json()) .then(data => this.users = data); } }">...</div> <!-- روش ۲: x-init جداگانه --> <div x-data="{ count: 0 }" x-init="count = parseInt(localStorage.getItem('count') || 0)"> <span x-text="count"></span> </div> <!-- روش ۳: async init --> <div x-data="{ post: null, loading: true, async init() { this.post = await fetch('/api/post/1').then(r => r.json()); this.loading = false; } }"> <div x-show="loading">در حال بارگذاری...</div> <div x-show="!loading" x-text="post?.title"></div> </div>
Alpine.data() — تعریف کامپوننت قابل استفاده مجدد
با Alpine.data() میتوانید کامپوننتهای Alpine را بیرون از HTML تعریف کنید. این روش کد را تمیزتر نگه میدارد و امکان استفاده مجدد در چند جا را فراهم میکند.
<!-- تعریف کامپوننت در script --> <script> document.addEventListener('alpine:init', () => { Alpine.data('searchBox', () => ({ query: '', results: [], loading: false, async search() { if (!this.query) { this.results = []; return; } this.loading = true; // شبیهسازی API call await new Promise(r => setTimeout(r, 300)); this.results = ['نتیجه ۱', 'نتیجه ۲']; this.loading = false; } })); }); </script> <!-- استفاده ساده در HTML --> <div x-data="searchBox"> <input x-model="query" @input.debounce.300ms="search()"> <div x-show="loading">در حال جستجو...</div> <template x-for="r in results"> <div x-text="r"></div> </template> </div>
init() به عنوان یک متد داخل x-data تعریف میشود و Alpine آن را خودکار فراخوانی میکند. x-init یک attribute جداگانه است و میتوان هر دو را با هم استفاده کرد — ابتدا init() سپس x-init اجرا میشود.
🔗 x-bind و x-model
x-bind به شما امکان میدهد مقادیر attribute های HTML را به دادههای Alpine متصل کنید. میتوان از shorthand : هم استفاده کرد.
x-model یک binding دوطرفه ایجاد میکند — تغییر داده، input را آپدیت میکند و بالعکس.
x-bind — اتصال یکطرفه
<!-- شکل کامل --> <img x-bind:src="imageUrl" x-bind:alt="imageAlt"> <!-- shorthand با : --> <img :src="imageUrl" :alt="imageAlt"> <!-- اتصال class پویا --> <div :class="{ 'active': isActive, 'error': hasError }"></div> <!-- اتصال style --> <div :style="{ backgroundColor: color, fontSize: size + 'px' }"></div> <!-- disabled پویا --> <button :disabled="!isValid">ارسال</button>
x-model — اتصال دوطرفه
<input x-model="name"> <!-- text input --> <textarea x-model="message"></textarea> <input type="checkbox" x-model="agreed"> <select x-model="selected">...</select> <!-- modifier: بهروز رسانی با تاخیر (پس از blur) --> <input x-model.lazy="name"> <!-- modifier: تبدیل به number --> <input type="number" x-model.number="age"> <!-- modifier: حذف فاصلههای اضافه --> <input x-model.trim="username">
دمو: فرم نمایهساز زنده
:style میپذیرد یک object با camelCase propertyها. مثلاً :style="{ backgroundColor: color }" معادل style="background-color: ..." است.
👁 x-show و x-if
دو دایرکتیو برای نمایش شرطی عناصر وجود دارد: x-show و x-if. هر کدام رویکرد متفاوتی دارند.
تفاوت x-show و x-if
| ویژگی | x-show | x-if |
|---|---|---|
| روش کار | display:none میگذارد | از DOM حذف/اضافه میکند |
| عنصر در DOM | همیشه هست | فقط وقتی true است |
| پشتیبانی از transition | بله (x-transition) | بله (template) |
| سرعت toggle | سریعتر | کندتر (re-render) |
| مناسب برای | toggle مکرر | نمایش یکبار یا هرگز |
<!-- x-show --> <div x-show="isVisible"> این عنصر پنهان/نمایان میشود </div> <!-- x-if (باید داخل template باشد) --> <template x-if="isLoggedIn"> <div>خوش آمدید!</div> </template> <!-- x-show با transition --> <div x-show="open" x-transition> محتوا با انیمیشن ظاهر میشود </div>
دمو: کنترل نمایش با x-show و x-if
📅 x-for — رندر لیست
x-for برای رندر پویای لیستها به کار میرود. این دایرکتیو باید روی تگ <template> استفاده شود.
<!-- لیست ساده --> <template x-for="item in items" :key="item.id"> <li x-text="item.name"></li> </template> <!-- با index --> <template x-for="(item, index) in items" :key="index"> <li x-text="(index+1) + '. ' + item"></li> </template> <!-- حلقه عددی --> <template x-for="i in 5"> <span x-text="i"></span> </template>
Alpine از
:key برای تشخیص هویت هر آیتم استفاده میکند تا DOM را بهینه بهروز کند. همیشه از یک مقدار یکتا (مثل id) به عنوان key استفاده کنید.
دمو: لیست وظایف (Todo List)
حلقه تودرتو (Nested x-for)
<!-- x-for تودرتو برای دادههای سلسلهمراتبی --> <div x-data="{ categories: [ { name: 'فرانتاند', items: ['HTML', 'CSS', 'JavaScript', 'Alpine.js'] }, { name: 'بکاند', items: ['PHP', 'Python', 'Node.js'] }, { name: 'دیتابیس', items: ['MySQL', 'MongoDB', 'Redis'] } ] }"> <template x-for="cat in categories" :key="cat.name"> <div> <h4 x-text="cat.name"></h4> <ul> <template x-for="item in cat.items" :key="item"> <li x-text="item"></li> </template> </ul> </div> </template> </div>
فیلتر و مرتبسازی لیست (Computed)
با استفاده از get (computed property) میتوانید لیست فیلترشده یا مرتبشده را مستقیماً در template استفاده کنید:
<div x-data="{ search: '', sort: 'name', products: [ { id: 1, name: 'لپتاپ', price: 25000000, stock: 3 }, { id: 2, name: 'موبایل', price: 12000000, stock: 0 }, { id: 3, name: 'تبلت', price: 8500000, stock: 5 } ], get filtered() { return this.products .filter(p => p.name.includes(this.search)) .sort((a, b) => this.sort === 'price' ? a.price - b.price : a.name.localeCompare(b.name)); } }"> <input x-model="search" placeholder="جستجو..."> <select x-model="sort"> <option value="name">نام</option> <option value="price">قیمت</option> </select> <template x-for="p in filtered" :key="p.id"> <div> <span x-text="p.name"></span> <span x-text="p.price.toLocaleString() + ' تومان'"></span> <span x-text="p.stock === 0 ? 'ناموجود' : 'موجود'"></span> </div> </template> </div>
دمو: فروشگاه با جستجو و مرتبسازی
id) برای :key استفاده کنید. از index به عنوان key زمانی استفاده کنید که ترتیب لیست تغییر نمیکند، چون Alpine از key برای بهینهسازی DOM diffing استفاده میکند.
⚡ x-on — رویدادها
x-on (یا shorthand @) برای گوش دادن به رویدادهای DOM استفاده میشود. Alpine از تمام رویدادهای استاندارد HTML پشتیبانی میکند.
رویدادهای رایج
<!-- کلیک --> <button @click="doSomething()"></button> <button x-on:click="doSomething()"></button> <!-- معادل --> <!-- کیبورد --> <input @keyup.enter="submit()"> <!-- Enter key --> <input @keyup.escape="close()"> <!-- Escape key --> <input @keydown.ctrl.s="save()"> <!-- Ctrl+S --> <!-- فرم --> <form @submit.prevent="handleSubmit()"></form> <!-- موس --> <div @mouseover="hovered = true" @mouseleave="hovered = false"></div> <!-- Window events --> <div @keydown.window="handleKey($event)"></div> <!-- فقط یکبار اجرا شود --> <button @click.once="doOnce()"></button> <!-- اجرای debounced --> <input @input.debounce.300ms="search()">
دمو: انواع رویدادها
modifier های رویداد
| Modifier | توضیح | مثال |
|---|---|---|
.prevent | جلوگیری از رفتار پیشفرض | @submit.prevent |
.stop | توقف event bubbling | @click.stop |
.once | فقط یکبار اجرا | @click.once |
.window | روی window گوش دهد | @keydown.window |
.document | روی document گوش دهد | @click.document |
.debounce | با تاخیر اجرا | @input.debounce.500ms |
.throttle | محدودیت تعداد اجرا | @scroll.throttle.100ms |
.self | فقط اگر روی خود element بود | @click.self |
.outside | کلیک خارج از element | @click.outside |
🎪 x-transition — ترنزیشنها
Alpine.js یک سیستم ترنزیشن داخلی دارد که با x-transition فعال میشود. میتوانید انیمیشنهای نمایش و پنهانسازی عناصر را به راحتی تعریف کنید.
روشهای مختلف x-transition
<!-- سادهترین حالت: transition پیشفرض --> <div x-show="open" x-transition>محتوا</div> <!-- با duration --> <div x-show="open" x-transition.duration.500ms>محتوا</div> <!-- با origin برای scale --> <div x-show="open" x-transition.origin.top.right>منو</div> <!-- کنترل کامل با modifier ها --> <div x-show="open" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 transform scale-90" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-300" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-90"> محتوا </div>
دموی ترنزیشنها
🌟 $store و ویژگیهای پیشرفته
Alpine.js ابزارهای قدرتمندی برای موارد پیشرفتهتر فراهم میکند: Alpine.store() برای state مشترک، $refs برای دسترسی مستقیم به DOM، $nextTick برای اجرای کد بعد از آپدیت DOM، و چندین خاصیت جادویی (magic properties).
Alpine.store() — حالت سراسری
Alpine.store() یک store (فروشگاه داده) سراسری ایجاد میکند که تمام کامپوننتها میتوانند به آن دسترسی داشته باشند — مثل Vuex سادهشده.
<!-- تعریف store (در script tag) --> <script> document.addEventListener('alpine:init', () => { Alpine.store('counter', { count: 0, increment() { this.count++ }, decrement() { this.count-- } }); Alpine.store('theme', { dark: false, toggle() { this.dark = !this.dark } }); }); </script> <!-- استفاده در هر کامپوننتی --> <div x-data> <span x-text="$store.counter.count"></span> <button @click="$store.counter.increment()">+</button> </div>
$refs — دسترسی به DOM
<div x-data="{ focus() { this.$refs.myInput.focus() } }"> <input x-ref="myInput" placeholder="..."> <button @click="focus()">فوکوس</button> </div>
خاصیتهای جادویی (Magic Properties)
| خاصیت | توضیح |
|---|---|
$el | عنصر DOM ریشه کامپوننت |
$refs | دسترسی به عناصر با x-ref |
$store | دسترسی به Alpine.store |
$data | شیء دادههای کامپوننت |
$watch | مشاهده تغییرات یک property |
$nextTick | اجرا بعد از آپدیت DOM |
$dispatch | انتشار custom event |
$event | آبجکت event در handler |
$root | عنصر ریشه نزدیکترین x-data |
$id | ID یکتا برای accessibility |
$watch — مشاهده تغییرات
<div x-data="{ name: '', init() { this.$watch('name', value => { console.log('نام تغییر کرد:', value); }) } }"> <input x-model="name"> </div>
$nextTick — بعد از رندر
<div x-data="{ items: [], async addItem() { this.items.push('آیتم جدید'); await this.$nextTick(); // حالا DOM آپدیت شده this.$refs.list.scrollTop = this.$refs.list.scrollHeight; } }">...</div>
$dispatch — انتشار رویداد
<!-- فرزند: ارسال رویداد --> <button @click="$dispatch('notify', { message: 'عملیات موفق' })"> ارسال اعلان </button> <!-- والد: دریافت رویداد --> <div @notify.window="alert($event.detail.message)"> ... </div>
Alpine.data() — کامپوننتهای قابل استفاده مجدد
<script> document.addEventListener('alpine:init', () => { Alpine.data('dropdown', () => ({ open: false, toggle() { this.open = !this.open }, close() { this.open = false } })); }); </script> <!-- استفاده بارها بدون تکرار کد --> <div x-data="dropdown"> <button @click="toggle()">منو</button> <div x-show="open" @click.outside="close()"> ... </div> </div>
دمو: ترکیب ویژگیهای پیشرفته
دو کامپوننت مستقل که یک store را به اشتراک میگذارند:
هر دو مقدار یکی هستند — state مشترک!
- از
x-dataبرای state محلی کامپوننت استفاده کنید - از
Alpine.store()برای state مشترک بین کامپوننتها استفاده کنید - از
Alpine.data()برای کامپوننتهای قابل استفاده مجدد استفاده کنید - همیشه
deferرا روی تگ script فراموش نکنید - از
:keyدرx-forاستفاده کنید
منابع بیشتر
⚡ x-init، x-cloak و x-effect
این سه دایرکتیو چرخه حیات و رفتار ریکتیو کامپوننت را کنترل میکنند و برای ساخت تجربههای روان و بدون پرش (FOUC) ضروری هستند.
x-cloak — جلوگیری از Flash of Uninitialized Content
وقتی صفحه بار میشود، قبل از اینکه Alpine اجرا شود، ممکن است کاربر برای لحظهای {{ message }} یا عناصر پنهاننشده را ببیند. x-cloak این مشکل را حل میکند.
<!-- ۱: یک بار این style را در <head> بگذارید --> <style> [x-cloak] { display: none !important; } </style> <!-- ۲: روی کامپوننت اضافه کنید --> <div x-data="{ name: 'علی' }" x-cloak> <!-- تا Alpine آماده نشود، این div پنهان است --> <p x-text="'سلام ' + name"></p> </div> <!-- بدون x-cloak، کاربر لحظهای میبیند: x-text="'سلام ' + name" --> <!-- با x-cloak، مستقیم نتیجه را میبیند: سلام علی -->
وقتی Alpine را از CDN لود میکنید (نه inline)، همیشه
x-cloak روی کامپوننتهای مهم صفحه قرار دهید تا تجربه بهتری داشته باشید.
x-effect — اثر ریکتیو
x-effect یک expression است که هر بار یکی از وابستگیهایش تغییر کند، دوباره اجرا میشود — بدون نیاز به صریحاً تعریف کردن $watch.
<!-- هر بار که count تغییر کند، title مرورگر آپدیت میشود --> <div x-data="{ count: 0 }" x-effect="document.title = 'تعداد: ' + count"> <button @click="count++">افزایش (<span x-text="count"></span>)</button> </div> <!-- مثال: ذخیره در localStorage هر بار که داده تغییر کند --> <div x-data="{ notes: JSON.parse(localStorage.getItem('notes') || '[]'), newNote: '' }" x-effect="localStorage.setItem('notes', JSON.stringify(notes))"> <input x-model="newNote" @keyup.enter="notes.push(newNote); newNote=''"> <!-- هر بار notes تغییر کند، localStorage آپدیت میشود --> </div>
مقایسه x-effect با $watch
| ویژگی | x-effect | $watch |
|---|---|---|
| محل تعریف | attribute روی element | داخل init() یا x-data |
| تشخیص وابستگی | خودکار | دستی (نام property) |
| دسترسی به مقدار قبلی | ندارد | دارد (oldValue) |
| چند property | هر تعداد (خودکار) | باید جداگانه $watch بزنید |
| اجرای اولیه | بله (بلافاصله) | خیر (فقط بعد از تغییر) |
دمو: x-cloak و x-effect در عمل
x-effect هر بار که text تغییر میکند، متن را در localStorage ذخیره میکند. صفحه را Refresh کنید — متن باقی میماند!
🌟 الگوهای واقعی — Modal، Tabs، Accordion، Dropdown
Alpine.js در ساخت کامپوننتهای UI رایج بسیار میدرخشد. در این بخش چهار الگوی پرکاربرد را با کد کامل میبینید که در پروژههای واقعی استفاده میشوند.
۱. Dropdown با @click.outside
یکی از رایجترین موارد: منوی کشویی که با کلیک خارج از خودش بسته میشود.
<div x-data="{ open: false }" style="position:relative"> <button @click="open = !open"> منو <span x-text="open ? '▲' : '▼'"></span> </button> <div x-show="open" x-transition.origin.top.right @click.outside="open = false" style="position:absolute; top:100%; right:0; background:#fff; border:1px solid #eee; border-radius:8px; box-shadow:0 8px 25px rgba(0,0,0,0.12); min-width:200px; z-index:50"> <a href="#" @click="open=false">👤 پروفایل</a> <a href="#" @click="open=false">⚙ تنظیمات</a> <a href="#" @click="open=false">🚪 خروج</a> </div> </div>
روی خارج از dropdown کلیک کنید تا بسته شود.
۲. Modal / Dialog
<div x-data="{ showModal: false }"> <button @click="showModal = true">باز کردن مودال</button> <!-- Backdrop --> <div x-show="showModal" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" @click.self="showModal = false" @keydown.escape.window="showModal = false" style="position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex; align-items:center;justify-content:center;z-index:999"> <!-- Modal box (کلیک روی این باعث بستن نمیشود: @click.self بالا) --> <div style="background:#fff;border-radius:12px;padding:32px;max-width:480px;width:90%"> <h3>عنوان مودال</h3> <p>محتوای مودال اینجا قرار میگیرد.</p> <button @click="showModal = false">بستن</button> </div> </div> </div>
📚 راهنمای Alpine.js
Alpine.js یک فریمورک سبک برای ساخت رابطهای کاربری ریکتیو است. این مودال با کلیک روی خارج یا زدن Escape بسته میشود.
@click.stop روی box باعث میشود کلیک داخل مودال آن را نبندد.
۳. Tabs
<div x-data="{ activeTab: 'intro' }"> <!-- Tab buttons --> <div role="tablist"> <button @click="activeTab = 'intro'" :class="{ 'active': activeTab === 'intro' }">معرفی</button> <button @click="activeTab = 'install'" :class="{ 'active': activeTab === 'install' }">نصب</button> <button @click="activeTab = 'usage'" :class="{ 'active': activeTab === 'usage' }">استفاده</button> </div> <!-- Tab panels --> <div x-show="activeTab === 'intro'">محتوای معرفی</div> <div x-show="activeTab === 'install'">محتوای نصب</div> <div x-show="activeTab === 'usage'">محتوای استفاده</div> </div>
Alpine.js یک فریمورک جاوااسکریپت سبک (۱۵KB gzip) است که امکان افزودن رفتار ریکتیو به HTML را بدون build step میدهد. ساخته Caleb Porzio است.
- نیاز به هیچ build pipeline ندارد
- کاملاً درون HTML نوشته میشود
- منحنی یادگیری بسیار کم دارد
- با Laravel، Django، Rails کاملاً سازگار است
- جایگزین عالی jQuery برای تعاملات ساده
<div x-data="{ open: false }"> <button @click="open = !open">Toggle</button> <div x-show="open" x-transition> سلام Alpine! 👋 </div> </div>
۴. Accordion
<div x-data="{ active: null, items: [ { id: 1, q: 'Alpine.js چیست؟', a: 'یک فریمورک سبک ریکتیو...' }, { id: 2, q: 'آیا نیاز به npm دارد؟', a: 'خیر، میتوانید از CDN استفاده کنید...' } ] }"> <template x-for="item in items" :key="item.id"> <div> <button @click="active = active === item.id ? null : item.id"> <span x-text="item.q"></span> <span x-text="active === item.id ? '−' : '+'"></span> </button> <div x-show="active === item.id" x-transition> <p x-text="item.a"></p> </div> </div> </template> </div>
active به عنوان ID آیتم باز، همیشه فقط یک آیتم باز است. اگر بخواهید چند آیتم همزمان باز باشند، active را به یک Set تبدیل کنید و با active.has(id) چک کنید.
🔌 پلاگینهای رسمی Alpine.js
Alpine.js دارای یک اکوسیستم پلاگین رسمی است که قابلیتهای اضافی را بدون افزایش حجم اصلی فراهم میکند. هر پلاگین را جداگانه install میکنید و فقط چیزی که نیاز دارید را اضافه میکنید.
جدول پلاگینهای رسمی
| پلاگین | CDN | کاربرد | Directive / Magic |
|---|---|---|---|
| Intersect | @alpinejs/intersect |
تشخیص ورود/خروج element به viewport | x-intersect |
| Persist | @alpinejs/persist |
ذخیره state در localStorage خودکار | $persist |
| Focus | @alpinejs/focus |
مدیریت Focus Trap (برای Modal/Drawer) | x-trap |
| Collapse | @alpinejs/collapse |
انیمیشن بستن/باز شدن ارتفاع (Accordion) | x-collapse |
| Mask | @alpinejs/mask |
Input masking (تلفن، کارت بانکی، تاریخ) | x-mask |
| Morph | @alpinejs/morph |
انتقال روان DOM بین دو حالت | Alpine.morph() |
| Sort | @alpinejs/sort |
Drag and Drop مرتبسازی با Alpine | x-sort |
پلاگین Persist — ذخیره خودکار state
با $persist، مقدار یک property بهصورت خودکار در localStorage ذخیره و بازیابی میشود:
<!-- نصب پلاگین --> <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- استفاده: فقط $persist() اضافه کنید --> <div x-data="{ count: $persist(0), theme: $persist('light') }"> <button @click="count++"> کلیکها: <span x-text="count"></span> </button> <!-- بعد از refresh هم مقدار count حفظ میشود! --> </div>
پلاگین Intersect — لود lazy
<!-- وقتی element به viewport میرسد، تصویر لود میشود --> <div x-data="{ loaded: false }" x-intersect="loaded = true"> <template x-if="!loaded"> <div style="background:#eee;height:200px;border-radius:8px">در حال بارگذاری...</div> </template> <img x-show="loaded" src="image.jpg" alt="..."> </div> <!-- اجرا فقط یکبار (enter) --> <div x-intersect:enter.once="animate()">...</div>
پلاگین Mask — Input Formatting
<!-- شماره تلفن --> <input x-mask="0999-999-9999" placeholder="09xx-xxx-xxxx"> <!-- شماره کارت بانکی --> <input x-mask="9999-9999-9999-9999" placeholder="xxxx-xxxx-xxxx-xxxx"> <!-- تاریخ --> <input x-mask="9999/99/99" placeholder="YYYY/MM/DD"> <!-- Mask داینامیک --> <input x-mask:dynamic="$input.startsWith('0') ? '09999999999' : '9999-999-9999'">
پلاگین Collapse — Accordion انیمیشن ارتفاع
<!-- x-collapse انیمیشن تغییر ارتفاع را به شکل طبیعی انجام میدهد --> <div x-data="{ open: false }"> <button @click="open = !open">سوال ▼</button> <div x-show="open" x-collapse> <p>پاسخ با انیمیشن slide-down نمایش داده میشود</p> </div> </div>
پلاگینها باید قبل از فایل اصلی Alpine لود شوند. Alpine در هنگام start کردن پلاگینها را register میکند، پس اگر بعد از Alpine لود شوند کار نمیکنند.
🚀 زمین بازی کد (Playground)
directiveهای Alpine را همینجا ویرایش کن و نتیجه را زنده ببین. یک مثال را از منوی بالا انتخاب کن،
x-data، x-model، x-for و… را تغییر بده و دکمهٔ ▶ اجرا را بزن (یا فقط تایپ کن تا خودکار اجرا شود).
نسخهٔ Alpine v3.13.5 همین صفحه بهصورت آفلاین داخل تمرین بارگذاری میشود.
console.log() استفاده کن.