اذهب إلى المحتوى

أمثلة عن إطار العمل Alpine.js


سمير عبود

بعد أن تعرفنا في المقال السابق على إطار العمل Alpine.js وأنشأنا نموذج مُصغر للإطار من الصفر بهدف التعلم وفهم آلية عمل هذا الإطار، سنقوم في هذا المقال بإنشاء بعض الأمثلة البسيطة باستخدام Alpine.js وسنُغطي مجموعة من المواضيع المهمة في الإطار كإنشاء المكونات، التعامل مع الأحداث، عرض البيانات وإنشاء رسومات متحركة.

هذا المثال لإنشاء نافذة منبثقة عبر Alpine.js، نقوم أولًا بإنشاء هيكلية HTML:

<!doctype html>
<html lang="ar" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>مثال لنافذة منبثقة</title>
</head>
<body>
<div class="modal-wrapper">
    <div class="modal">
        محتوى النافذة المنبثقة
    </div>
</div>
</body>
</html>

سنضيف بعض تنسيقات CSS لتظهر النافذة بشكل جيد، في عنصر head نضيف وسم style ونضع به التنسيقات التالية:

<style>
    body {
        margin: 0;
    }

    .modal-wrapper {
        position: absolute;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, .5);
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .modal {
        background-color: #fff;
        width: 30%;
        padding: 20px;
        box-shadow: 5px 5px 5px #333;
        border-radius: 5px;
    }
</style>

وسيكون مظهر النافذة بعد فتح صفحة HTML بالشكل التالي:

modal show

نريد التحكم الآن في إخفاء هذه النافذة، بحيث عند الضغط بزر الفأرة خارج حاوية النافذة البيضاء أو عند الضغط على زر الهروب escape تختفي النافذة، نقوم أولاً بتضمين إطار العمل Alpine.js عبر شبكة CDN بالشكل التالي:

<script src="https://unpkg.com/alpinejs" defer></script>

وكما تعلمنا سابقاً نستعمل أولاً الموجه x-data لنستطيع استعمال كافة ما تتيحه Alpine داخل المكون، بما أننا نريد التحكم في إظهار أو إخفاء النافذة، سنعرف خاصية بالاسم open، بحيث إذا كانت true فذلك يعني أن النافذة مفتوحة وإذا كانت false فإن النافذة مُغلقة، ونتحكم في ذلك عبر المُوجه x-show ونمرر له اسم الخاصية open:

<div
        class="modal-wrapper"
        x-data="{ open: true }"
        x-show="open"
>
    <div class="modal">
        محتوى النافذة المنبثقة
    </div>
</div>

سترى أن النافذة مفتوحة افتراضيًا عند فتح الصفحة، يُمكنك تجربة تغيير القيمة الابتدائية من true إلى false وسترى أنها تستجيب لذلك.

بما أننا نرغب عند النقر خارج الحاوية البيضاء بإخفاء النافذة فإننا سنستخدم المُوجه x-on للاستماع الى حدث النقر، لكني افضل استخدام الطريقة المختصرة @ ثم اسم الحدث أي click@ يُمكنك استخدام أي طريقة تريد سواء المختصرة click@ أو x-on:click ونحدد المُعدل outside للدلالة على أن الإستماع سيكون عند النقر خارج العُنصر المحدد:

<div
        class="modal-wrapper"
        x-data="{ open: true }"
        x-show="open"
>
    <div class="modal" @click.outside="open = false">
        محتوى النافذة المنبثقة
    </div>
</div>

الآن عند فتح الصفحة ستظهر النافذة وعند النقر خارج الحاوية البيضاء ستختفي النافذة المنبثقة، وهذا لأننا حددنا أنه عندما يتم النقر قم بتغيير قيمة open إلى false وبالتالي يتم تحديث شجرة DOM وتختفي النافذة المنبثقة.

بخصوص الضغط على زر الهروب escape لإخفاء النافذة المنبثقة سنستخدم حدث keyup للإستماع إلى حدث الضغط على الزر المطلوب ثم نمرر المعدل window لتحديد أن الإستماع سيحدث على مستوى الصفحة بشكل كامل:

<div
        class="modal-wrapper"
        x-data="{ open: true }"
        x-show="open"
        @keyup.escape.window="open = false"
>
    <div class="modal" @click.outside="open = false">
        محتوى النافذة المنبثقة
    </div>
</div>

لحد الآن لا توجد طريقة لإظهار النافذة المنبثقة سوى بتحديث الصفحة، سنتيح ذلك الآن، نقوم بتغيير قيمة open الافتراضية إلى false:

x-data="{ open: false }"

ثم نضيف خارج المكون زر لفتح النافذة المنبثقة:

<button>فتح النافذة المنبثقة</button>

بما أن الزر موجود خارج المكون فهو لا يعلم أي شيء عن بيانات ذلك المكون ولا يستطيع الوصول لها وبالتالي لا يستطيع التحكم فيها، سنُعرف بداخله الخاصية x-data حتى نتمكن من استعمال Alpine بداخله، ثم نستخدم الخاصية dispatch$ التي تُتيحها Alpine لإطلاق حدث خاص:

<button x-data @click="$dispatch('open-modal')">فتح النافذة المنبثقة</button>

يمكن تسمية الحدث بأي اسم لقد أسميته open-modal، ثم في المكون الأساسي يمكن الاستماع إلى الحدث الخاص الذي تم إطلاقه بنفس الطريقة التي نستخدمها في الإستماع إلى الأحداث العادية:

@open-modal.window="open = true"

بحيث تُصبح هيكلية HTML بالشكل التالي:

<div
        class="modal-wrapper"
        x-data="{ open: false }"
        x-show="open"
        @keyup.esc.window="open = false"
        @open-modal.window="open = true"
    >
    <div class="modal" @click.outside="open = false">
        محتوى النافذة المنبثقة
    </div>
</div>

<button x-data @click="$dispatch('open-modal')">فتح النافذة المنبثقة</button>

بهذا الشكل نكون قد أنشأنا مثال بسيط عن مكون نافذة منبثقة، وتحكمنا في إظهاره وإخفائه.

إنشاء مفسر لصيغة ماركداون Markdown Parser

إن لم تكن لديك معرفة مسبقة بصيغة ماركداون فأدعوك للإطلاع على المقالتين: ماركداون للمبرمجين و كيف تكتب بصيغة ماركداون ببساطة، لن نقوم بإنشاء مفسر من الصفر وإنما سنستخدم مكتبة marked وسيكون مثالنا في إنشاء حقل إدخال نكتب من خلاله بصيغة ماركداون ثم نضغط على زر ليقوم بتفسير تلك الصيغة ويعرض لنا ما يوافقها:

نبدأ بإنشاء هيكلية HTML:

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>مفسر صيغة ماركداون</title>
    <script src="https://unpkg.com/alpinejs" defer></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>

<div x-data="{
    body: '',
    markdown: ''
}">
    <div>
        <div>
            <textarea cols="50" rows="10"></textarea>
        </div>

        <button>عرض</button>
    </div>

    <div>
        المحتوى
    </div>
</div>

</body>
</html>

المحتوى بسيط فقط قمنا بتضمين إطار Alpine.js ثم حزمة marked التي ستساعدنا في عملية التفسير، عرفنا مكون يحتوي على خاصيتين body هو المحتوى الذي سيتم كتابته عبر حقل الإدخال، و markdown هو نتيجة التفسير والذي سيكون عبارة عن html.

نقوم بربط الخاصية body مع حقل الإدخال باستخدام المُوجه x-model بحيث عند الكتابة في حقل الإدخال تتغير القيمة:

<textarea cols="50" rows="10" x-model="body"></textarea>

نستمع الى حدث النقر ونقوم بتفسير القيمة المدخلة في حقل الإدخال ونسندها إلى الخاصية markdown:

<button @click="markdown = marked.parse(body)">عرض</button>

نقوم بعرض المحتوى باستخدام المُوجه x-html:

<div x-html="markdown"></div>

الآن إذا أدخلت محتوى بصيغة ماركداون في حقل الإدخال وضغطت على عرض سيتم عرضه بشكل مُفسر

جرب إدخال التالي في حقل الإدخال:

# عنوان أول
## عنوان ثاني
### عنوان ثالث
#### عنوان رابع

**نص ثخين**
*نص مائل*
~~نص مشطوب~~

> هذا مثال على اقتباس

مثال عن رابط: [أكاديمية حسوب](https://academy.hsoub.com/)

قائمة عناصر:
* عنصر
* عنصر آخر
* عنصر آخر

قائمة مرتبة:
1. عنصر أول
2. عنصر ثانٍ
3. عنصر ثالث.

markdown parser content

بعد الضغط على زر عرض ستحصل على النتيجة التالية:

markdown parser result

يُمكن في الكائن x-data إنشاء توابع وغير ذلك مما يمكن استعماله في كائنات جافاسكربت، سنقوم بإنشاء تابع يقوم بعملية التفسير ونستدعيه عند الاستماع إلى حدث الضغط حيث يصبح المكون بالشكل التالي:

<div x-data="{
    body: '',
    markdown: '',
    parseMarkdown() {
        this.markdown = marked.parse(this.body)
    }
}">
    <div>
        <div>
            <textarea cols="50" rows="10" x-model="body"></textarea>
        </div>

        <button @click="parseMarkdown">عرض</button>
    </div>

    <div x-html="markdown"></div>
</div>

إنشاء شريط تقدم دائري متحرك circular progress bar

سنقوم أولا بإنشاء هيكلية عناصر HTML واستخدام CSS لتنسيق شريط التقدم:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Animated circular progress bar</title>
    <script src="https://unpkg.com/alpinejs" defer></script>

    <style>
        body {
            margin: 0;
        }
        .box {
            position: absolute;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
            width: 100%;
        }
        .progress {
            position: relative;
            height: 250px;
            width: 250px;
            border-radius: 50%;
            background: conic-gradient(slategray 306deg, #ddd 0deg);
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .progress::before{
            content: "";
            position: absolute;
            height: 210px;
            width: 210px;
            border-radius: 50%;
            background-color: #fff;
        }

        .progress-inner{
            position: relative;
            font-size: 40px;
            font-weight: 600;
            color: slategray;
        }
        .text{
            font-size: 30px;
            font-weight: 500;
            color: #606060;
            margin-top: 10px;
        }
    </style>
</head>
<body>
<div class="box">
    <div class="progress">
        <div class="progress-inner">85%</div>
    </div>
    <span class="text">Alpine Js</span>
</div>
</body>
</html>

سيكون المظهر بالشكل التالي:

animated-circular-progress-bar.JPG

يُمكنك استخدام أي تنسيقات ترغب بها، ما يهم أننا سنستهدف الخاصية:

background: conic-gradient(slategray 306deg, #ddd 0deg);

القيمة 306deg بالضبط، حيث أننا سنحصل عليها من ضرب نسبة التقدم في 3.6، أي لما تكون النسبة 100% فإن القيمة تكون 360 درجة فهي عبارة عن قيس زاوية. وعندما تكون نسبة التقدم 50% تكون قيمة الزاوية 180 درجة وهي حاصل ضرب 50 في 3.6 لأننا سنبدأ التحريك من 0 إلى غاية القيمة النهائية.

لذلك سنجعل القيمة الابتدائية 0 بالشكل التالي:

background: conic-gradient(slategray 0deg, #ddd 0deg);

ثم نأتي لنبني المكون عبر Alpine.js ونبدأ بتعريف x-data في العُنصر progress، بحيث نحدد الخصائص التالية: currentValue وسنعتمد عليها في زيادة قيمة التقدم وتكون مُهيأة بالقيمة 0، endValue وهي القيمة الأعظمية للتقدم، speed خاصية زمنية سنعتمد عليها في تسريع التحريك.

<div class="box">
    <div class="progress"
        x-data="{
            currentValue: 0,
            endValue: 85,
            speed: 20, // ms
        }"
    >
        <div class="progress-inner" x-text="currentValue + '%'"></div>
    </div>
    <span class="text">Alpine Js</span>
</div>

ستلاحظ أننا حددنا x-text للعنصر progress-inner حتى تكون القيمة ديناميكية وتتحدث كلما تغيرت قيمة currentValue.

الآن سنقوم بتعريف تابع بالاسم init داخل x-data هذا التابع سيتم تنفيذه مباشرةً بعد تهيئة المكون يمكنك تجربة التالي:

<div class="progress"
    x-data="{
        currentValue: 0,
        endValue: 85,
        speed: 20, // ms

        init() {
            console.log('Initialized')
        }
    }"
>

ستجد أنه يتم طباعة العبارة Initialized في الطرفية console عند تحديث الصفحة، نحن لم نستدعي التابع، هذا لأن Alpine.js تقوم بذلك تلقائيًا، يُمكن أيضًا استخدام الموجه x-init لنفس الغرض.

سنقوم بداخل التابع init استخدام setInterval لتنفيذ دالة بعد كل زمن معين (هنا سنستخدم speed حيث أن الدالة سيتم تنفيذها بعد كل 20ms) بداخل الدالة سنقوم بزيادة قيمة currentValue بـ 1 ونتحقق إذا وصلنا للقيمة الأعظمية للتقدم حيث نقوم بإيقاف تنفيذ الدالة:

<div class="progress"
    x-data="{
        currentValue: 0,
        endValue: 85,
        speed: 20, // ms

        init() {
            let progress = setInterval(() => {
                this.currentValue++

                if (this.currentValue === this.endValue) {
                    clearInterval(progress)
                }
            }, this.speed)
        }
    }"
>

الآن إذا تصفحت ستجد أن القيمة تتغير من 0 إلى 85 كما هو مطلوب وعندما نصل إلى القيمة النهائية يتوقف التنفيذ. متبقي الآن ربط التنسيق الذي ذكرته في الأعلى بالقيمة الحالية، ولعمل هذا الشيء نحتاج إلى استخدام الموجه x-bind فهو يتيح ربط تعبير برمجي بخاصية من الخصائص في html، والخاصية التي سنتعامل معها هي style، هناك كتابة مختصرة لـ x-bind وهي رمز النقطتين : مثلما هو الحال مع x-on في الأحداث لذلك فبدل كتابة:

x-bind:style=""

يمكن كتابة:

:style=""

وبالتالي، لربط قيمة currentValue بخاصية style سنكتب:

:style="`background: conic-gradient(slategray ${currentValue * 3.6}deg, #ddd 0deg)`"

ليُصبح المكون بالشكل التالي:

<div class="box">
    <div class="progress"
        x-data="{
            currentValue: 0,
            endValue: 85,
            speed: 20, // ms

            init() {
                let progress = setInterval(() => {
                    this.currentValue++

                    if (this.currentValue === this.endValue) {
                        clearInterval(progress)
                    }
                }, this.speed)
            }
        }"

         :style="`background: conic-gradient(slategray ${currentValue * 3.6}deg, #ddd 0deg)`"
    >
        <div class="progress-inner" x-text="currentValue + '%'"></div>
    </div>
    <span class="text">Alpine Js</span>
</div>

بهذا الشكل سيُصبح كل من الشريط والنص المكتوب في الوسط يتحرك من القيمة 0 إلى القيمة النهائية 85، يمكنك تعديل القيمة النهائية 85 وقيمة السرعة speed لمعاينة النتائج:

animated-circular-progress-bar.gif

خاتمة

في ختام هذا المقال، نستنتج أن إطار العمل Alpine.js يعد أداة قوية وبسيطة في تطوير تجارب المستخدم الديناميكية على الويب. قدمت الأمثلة التي استعرضناها في هذا السياق نظرة عامة على قوة وسهولة استخدام Alpine.js في تحقيق العديد من الوظائف الديناميكية دون الحاجة إلى اللجوء إلى أطر عمل JavaScript الكبيرة والمعقدة.

تعتبر قدرة Alpine.js على توفير عمليات DOM الديناميكية والربط الفعّال بين العناصر والبيانات من خلال السماح بالتفاعل المباشر والتحكم السهل، مميزة بشكل خاص. التعابير البرمجية القصيرة والبنية البسيطة تجعل من السهل إدماجه في مشاريع الويب المختلفة دون تعقيدات زائدة.

من خلال الأمثلة البسيطة التي قمنا بإنشائها، يمكن للمطورين الآن البدء بفهم كيفية استخدام Alpine.js في مشاريعهم الشخصية أو العملية. عليهم استكشاف الميزات الإضافية واستخدام الإمكانيات الكاملة لهذا الإطار لتحسين تفاعل المستخدم وتبسيط التجارب على الويب.

اقرأ أيضًا

 


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...