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

طريقة عمل infinite scroll بإستخدام livewire

أمير عبد الكريم

السؤال

لدي هذا المكون:

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;
class PostsList extends Component
{
    protected $listeners = ['toggleFollow' => '$refresh'];

    public function getPostsProperty()
    {
        $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
        return Post::whereIn('user_id', $ids)->latest()->get();
    }
    public function render()
    {
        return view('livewire.posts-list');
    }
}

ولدي posts-list.blade.php

<div class="w-[30rem] mx-auto lg:w-[95rem]">
        @forelse($this->posts as $post)
            <livewire:post :post="$post" :wire:key="'post_'.$post->id"/>
        @empty
            <div class="max-w-2xl gap-8 mx-auto dark:text-gray-100">
                {{__('Start Following Your Friends and Enjoy.')}}
            </div>

        @endforelse
</div>

كيف اقوم بإضافة ميزة التحميل التلقائي للمنشورات؟ حاولت بحث ع يوتيوب ولكن ما استفدت ولا شيء.

رابط هذا التعليق
شارك على الشبكات الإجتماعية

Recommended Posts

  • 1

الامر بسيط ويوجد عدة طرق ولكن الافضل هو .

اولا انشاء متغير يحمل المنشورات التى تم تحميلها الى الان حيث لا نريد فى كل مرة يصل المستخدم الى اخر الصفحة وناتى بالمنشروات من جديد , ونعطى قيمة افتراضية لهذا المتغير بمصفوفة فارغة .

public $postCotainer = [];

وايضا نقوم بانشاء متغيرين اخرين احدهما يحمل القيمة الافتراضية لعدد المنشورات التى سيتم تحميلها فى بداية الصفحة او حتى عند التمرير لاسفل الصفحة . والاخر يحمل عدد المنشورات الذى تم عرضها الى الان .

public $take = 5;
public $skip = 0;

والان نقوم بانشاء استماع الى حدث التمرير جديد وفى الدالة نقوم بزيادة عدد المنشورات التى عرضها .

protected $listeners = ['scrollPosts'=>'getMorePosts'];

public function getMorePosts() {
  $this->skip += $this->take;
}

هكذا كل مرة يتم التمرير لاسفل الصفحة نقوم بزيادة العدد الذى تم عرضه .

والان فى الدالة getPostsProperty نضع هذا الكود.

public function getPostsProperty()
{
  $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
  $this->postCotainer = array_merge($this->postCotainer,Post::whereIn('user_id', $ids)->latest()->skip($this->skip)->take($this->take)->get()->all());
  return $this->postCotainer;
}

لاحظ هنا اننا اولا استخدمنا دالة array_merge وذلك لدمج المنشورات التى تم تحميلها سابقا الى المنشورات الجديدة الذى سوف يتم تحميلها .

ولاحظ اننا فى الاستعلام الخاص بال sql اضفنا داليتن الاولى هى skip اى اننا لا نريد ان ناتى بعدد معين بالمنشورات وهى المنشورات التى تم تحميلها بالفعل ولذلك نمرر لها المتغير skip الذى يحمل عدد المنشروات الذى تم تحميلها الى الان. 

والدالة take التى تسمح لنا باحضار عدد معين فقط من المنشورات ولذلك ارسلنا لها المتغير take الذى يحمل عدد المنشورات التى نريد عرضها .

وبهذا نكون قد انتهينا من الكود الخلفى . ولكن ستجد مشكلة سوف تحدث فى الحدث الذى لديك toggleFollow فمثلا اذا قام المستخدم بالغاء متابعه او متابعه مستخدم جديد فان منشوراته التى تم تحميلها سابقا نريد ان نحذفها او عرضها . لذلك سنجعل عند استماع الى الحدث toggleFollow هو حذف جميع المنشورات التى تم تحميلها سابقا واعادة التحميل من جديد حتى لا تحدث تلك المشكلة.وهذا هو كود المكون كاملا بعد التعديل.

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;
class PostsList extends Component
{
    protected $listeners = ['toggleFollow' => 'resetPosts','scrollPosts'=>'getMorePosts'];
    public $take = 5;
    public $skip = 0;
    public $postCotainer = [];
    public function resetPosts() {
        $this->postCotainer = [];
        $this->take = 5;
        $this->skip = 0;
    }
    public function getPostsProperty()
    {
        $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
        $this->postCotainer = array_merge($this->postCotainer,Post::whereIn('user_id', $ids)->latest()->skip($this->skip)->take($this->take)->get()->all());
        return $this->postCotainer;
    }
    public function getMorePosts() {
        $this->skip += $this->take;
    }
    public function render()
    {
        return view('livewire.posts-list');
    }
}

اما فى الواجهة الامامية فسوف نضيف استماع الى حدث التمرير و التحقق من ان المستخدم قد قام بالتمرير الى اخر الصفحة . واذا كان قام بالتمرير الى اخر الصفحة نقوم بارسال الحدث scrollPosts الذى سوف يقوم باحضار المزيد من المنشورات وبذلك كلما قام بالتمرير ياتى بالمنشورات الجديد .

<div class="w-[30rem] mx-auto lg:w-[95rem]">
    @forelse($this->posts as $post)
        <livewire:post :post="$post" :wire:key="'post_'.$post->id" />
    @empty
        <div class="max-w-2xl gap-8 mx-auto dark:text-gray-100">
            {{ __('Start Following Your Friends and Enjoy.') }}
        </div>
    @endforelse
</div>
@script
    <script>
        window.onscroll = function(ev) {
            if ((window.innerHeight + Math.round(window.scrollY)) >= document.body.offsetHeight) {
                $wire.dispatch('scrollPosts');
            }
        };
    </script>
@endscript

وهكذا قد انتهينا من جميع الاكود ويمكنك استعمالها وستعمل معك . ويمكنك تغير القيمة الافتراضية للمتغير take على حسب عدد المنشورات التى تريدها حيث انا اعطيته قيمة ابتدائية ب 5

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0
بتاريخ 9 ساعة قال محمد_عاطف:

الامر بسيط ويوجد عدة طرق ولكن الافضل هو .

اولا انشاء متغير يحمل المنشورات التى تم تحميلها الى الان حيث لا نريد فى كل مرة يصل المستخدم الى اخر الصفحة وناتى بالمنشروات من جديد , ونعطى قيمة افتراضية لهذا المتغير بمصفوفة فارغة .

public $postCotainer = [];

وايضا نقوم بانشاء متغيرين اخرين احدهما يحمل القيمة الافتراضية لعدد المنشورات التى سيتم تحميلها فى بداية الصفحة او حتى عند التمرير لاسفل الصفحة . والاخر يحمل عدد المنشورات الذى تم عرضها الى الان .

public $take = 5;
public $skip = 0;

والان نقوم بانشاء استماع الى حدث التمرير جديد وفى الدالة نقوم بزيادة عدد المنشورات التى عرضها .

protected $listeners = ['scrollPosts'=>'getMorePosts'];

public function getMorePosts() {
  $this->skip += $this->take;
}

هكذا كل مرة يتم التمرير لاسفل الصفحة نقوم بزيادة العدد الذى تم عرضه .

والان فى الدالة getPostsProperty نضع هذا الكود.

public function getPostsProperty()
{
  $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
  $this->postCotainer = array_merge($this->postCotainer,Post::whereIn('user_id', $ids)->latest()->skip($this->skip)->take($this->take)->get()->all());
  return $this->postCotainer;
}

لاحظ هنا اننا اولا استخدمنا دالة array_merge وذلك لدمج المنشورات التى تم تحميلها سابقا الى المنشورات الجديدة الذى سوف يتم تحميلها .

ولاحظ اننا فى الاستعلام الخاص بال sql اضفنا داليتن الاولى هى skip اى اننا لا نريد ان ناتى بعدد معين بالمنشورات وهى المنشورات التى تم تحميلها بالفعل ولذلك نمرر لها المتغير skip الذى يحمل عدد المنشروات الذى تم تحميلها الى الان. 

والدالة take التى تسمح لنا باحضار عدد معين فقط من المنشورات ولذلك ارسلنا لها المتغير take الذى يحمل عدد المنشورات التى نريد عرضها .

وبهذا نكون قد انتهينا من الكود الخلفى . ولكن ستجد مشكلة سوف تحدث فى الحدث الذى لديك toggleFollow فمثلا اذا قام المستخدم بالغاء متابعه او متابعه مستخدم جديد فان منشوراته التى تم تحميلها سابقا نريد ان نحذفها او عرضها . لذلك سنجعل عند استماع الى الحدث toggleFollow هو حذف جميع المنشورات التى تم تحميلها سابقا واعادة التحميل من جديد حتى لا تحدث تلك المشكلة.وهذا هو كود المكون كاملا بعد التعديل.

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;
class PostsList extends Component
{
    protected $listeners = ['toggleFollow' => 'resetPosts','scrollPosts'=>'getMorePosts'];
    public $take = 5;
    public $skip = 0;
    public $postCotainer = [];
    public function resetPosts() {
        $this->postCotainer = [];
        $this->take = 5;
        $this->skip = 0;
    }
    public function getPostsProperty()
    {
        $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
        $this->postCotainer = array_merge($this->postCotainer,Post::whereIn('user_id', $ids)->latest()->skip($this->skip)->take($this->take)->get()->all());
        return $this->postCotainer;
    }
    public function getMorePosts() {
        $this->skip += $this->take;
    }
    public function render()
    {
        return view('livewire.posts-list');
    }
}

اما فى الواجهة الامامية فسوف نضيف استماع الى حدث التمرير و التحقق من ان المستخدم قد قام بالتمرير الى اخر الصفحة . واذا كان قام بالتمرير الى اخر الصفحة نقوم بارسال الحدث scrollPosts الذى سوف يقوم باحضار المزيد من المنشورات وبذلك كلما قام بالتمرير ياتى بالمنشورات الجديد .

<div class="w-[30rem] mx-auto lg:w-[95rem]">
    @forelse($this->posts as $post)
        <livewire:post :post="$post" :wire:key="'post_'.$post->id" />
    @empty
        <div class="max-w-2xl gap-8 mx-auto dark:text-gray-100">
            {{ __('Start Following Your Friends and Enjoy.') }}
        </div>
    @endforelse
</div>
@script
    <script>
        window.onscroll = function(ev) {
            if ((window.innerHeight + Math.round(window.scrollY)) >= document.body.offsetHeight) {
                $wire.dispatch('scrollPosts');
            }
        };
    </script>
@endscript

وهكذا قد انتهينا من جميع الاكود ويمكنك استعمالها وستعمل معك . ويمكنك تغير القيمة الافتراضية للمتغير take على حسب عدد المنشورات التى تريدها حيث انا اعطيته قيمة ابتدائية ب 5

ماشاء الله شكراً لك كثيراً الأمر طلع سهل وانا طورت الكود بحيث يقوم بإزالة اول منشور اذا كان عدد المنشورات اكثر من 10 لضمان تجربة افضل.

  public function getMorePosts()
    {
        $this->skip += $this->take;
	
        if (count($this->postContainer) > 10) {
            array_shift($this->postContainer);
        }
    }

ولاحظت انك استخدم التابع all() لضمان إسترجاع الإستعلام كمصفوفة. وليس كـ اوبجكت اليس كذلك؟

ولكن الموجه @script هو جديد بالنسبة ليا كنت اظنه @push('scripts') شكراً لك مرة اخرى.

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0

بإمكانك إستخدام نظام الصفحات (pagination) المتاح في Laravel مع Intersection Observer API لتحقيق ذلك.

سنقوم بإنشاء خاصية page للدلالة على الصفحة الحالية ترتفع قيمتها بـ 1 مع كل جلب لمجموعة بيانات جديدة

سنقوم بإنشاء خاصية حاوية (كما شرح المدرب محمد في الأعلى) الهدف منها تخزين السجلات

use Illuminate\Support\Collection;

public int $page = 1;
public Collection $posts;

سنقوم بإنشاء خاصية من النوع computed في Livewire لإنشاء paginator يقوم بجلب مجموعة السجلات الخاصة بالصفحة الحالية:

use Livewire\Attributes\Computed;

#[Computed]
public function paginator()
{
  $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
  return Post::whereIn('user_id', $ids)
    ->latest()
    ->paginate(10, ['*'], 'page', $this->page);
}

أعلاه يتم جلب 10 سجلات في كل مرة، يمكنك التحكم في ذلك من خلال الوسيط الأول للتابع paginate

سنُنشئ تابع مسؤول عن تحميل المزيد لإضافة سجلات الصفحة الحالية إلى الحاوية الأساسية ورفع قيمة page:

public function loadMore(): void
{
  $this->posts->push(
    ...$this->paginator->getCollection()
  );

  $this->page = $this->page + 1;
}

ثم في التابع mount نقوم بتهيئة الحاوية وإستدعاء التابع loadMore لجلب سجلات أول صفحة:

public function mount(): void
{
  $this->posts = collect();
  $this->loadMore();
}

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

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;

class PostsList extends Component
{
  protected $listeners = ['toggleFollow' => '$refresh'];

  public int $page = 1;
  public Collection $posts;

  public function mount(): void
  {
    $this->posts = collect();
    $this->loadMore();
  }

  public function loadMore(): void
  {
    $this->posts->push(
      ...$this->paginator->getCollection()
    );

    $this->page = $this->page + 1;
  }

  #[Computed]
  public function paginator()
  {
    $ids = auth()->user()->following()->wherePivot('confirmed', true)->get()->pluck('id');
    return Post::whereIn('user_id', $ids)
      ->latest()
      ->paginate(10, ['*'], 'page', $this->page);
  }

  public function render()
  {
    return view('livewire.posts-list');
  }
}

الآن ستحتاج فقط إلى إستخدام الموجه x-intersect المتاح في إطار العمل alpine.js لإستدعاء التابع loadMore من المكون، لست بحاجة إلى تثبيت الإطار فـ Livewire الإصدار الثالث يتيح إستعمال alpine.js دون حاجة لتثبيته:

@if ($this->paginator->hasMorePages())
  <div x-intersect="$wire.loadMore" class="h-12 -translate-y-44"></div>
@endif

المتصفحات القديمة لا تدعم  Intersection Observer API يمكنك إضافة زر لتحميل المزيد يظهر في هذه الحالة:

@if ($this->paginator->hasMorePages())
  <button wire:click="loadMore">Load more</button>
@endif

ملف العرض سيكون بالشكل التالي:

<div class="w-[30rem] mx-auto lg:w-[95rem]">
    @if($posts->count() > 0)
        @foreach($posts as $post)
            <livewire:post :post="$post" :wire:key="'post_'.$post->id"/>
        @endforeach

        @if ($this->paginator->hasMorePages())
            <div x-intersect="$wire.loadMore" class="h-12 -translate-y-44"></div>
        @endif

        @if ($this->paginator->hasMorePages())
            <button wire:click="loadMore">Load more</button>
        @endif
    @else
        <div class="max-w-2xl gap-8 mx-auto dark:text-gray-100">
            {{__('Start Following Your Friends and Enjoy.')}}
        </div>
    @endif
</div>
رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0
بتاريخ 11 ساعة قال أمير عبد الكريم:

ولاحظت انك استخدم التابع all() لضمان إسترجاع الإستعلام كمصفوفة. وليس كـ اوبجكت اليس كذلك؟

 

نعم صحيح استخدمت all للحصول على النتيجة كمصفوفة حتى استطيع دمجها مع المنشورات السابقة .

بتاريخ 12 ساعة قال أمير عبد الكريم:

لكن الموجه @script هو جديد بالنسبة ليا كنت اظنه @push('scripts')

نعم الموجه  push('scripts')@ هو خاص بالاصدار 2 من مكتبة livewire وبما انك تستعمل الاصدار 3 منها فهم قد قامو بتحديث اسم الموجه ليصبح script@ .

ولا شكر على واجب اخى أمير . وبالتوفيق لك ان شاء الله

رابط هذا التعليق
شارك على الشبكات الإجتماعية

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

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

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.

  • إعلانات

  • تابعنا على



×
×
  • أضف...