# سحب بحث أذكى مع HTMX

# المقدمة

في پست سابق أريتكم كيف تصنع [سحب البحث باستخدام Alpine.js وHTMX](/blog/textsearchingpt11) ثم أريتكم كيف يمكننا تشغيل "Cross-Site Request Forgery protection" باستخدام `AntiforgeryRequestToken` في ASP.NET Core مع JavaScript [باستخدام HTMX لتطبيق مخزن ثقب دونات](/blog/simpledonutholecachingwithhtmx). واحدة من القضايا outstanding كانت كيفية تحميل الصفحات

[TOC]

<!--category-- HTMX, Alpine.js -->
<datetime class="hidden">2024-09-16T22:30</datetime>

# المشكلة

المشكلة كانت أنني كنت أستخدم HTMX AJAX للقيام بتحميل الصفحة التي طلبتها بعد أن قمت باختيار النتيجة من صفحة السحب

```javascript
  selectResult(result) {
            htmx.ajax('get', result.url, {
                target: '#contentcontainer',  // The container to update
                swap: 'innerHTML',            // Replace the content inside the target
            }).then(function() {
                history.pushState(null, '', result.url);
                window.scrollTo({
                    top: 0,
                    behavior: 'smooth'
                });
            });
```

المشكلة كانت أنه بينما سيقوم هذا بتحميل الصفحة اليمنى و تحديث الURL المعروض مع واحد جديد , قد مزعج الزر الخلفي . كما أن الصفحة WASN' T حقاً تم تحميلها في التاريخ بصورة صحيحة .

كما هو الحال مع [آخر مقالة عن الزر الخلفي للشنينغان](/blog/htmxtomakeyoursitemorespalike) هذا شيء أردت إصلاحه

# الحل

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

## `_typeahead.cshtml`

```html
<div x-data="window.mostlylucid.typeahead()" class="relative" id="searchelement" x-on:click.outside="results = []">
    @Html.AntiForgeryToken()
    <label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
        <input
            type="text"
            x-model="query"
            x-on:input.debounce.200ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>
    <!-- Dropdown -->
    <ul x-show="results.length > 0"
        id="searchresults"
        class="absolute z-100 my-2 w-full bg-white dark:bg-custom-dark-bg border border-1 text-black dark:text-white border-neutral-600 rounded-lg shadow-lg">
        <template x-for="(result, index) in results" :key="result.slug">
            <li :class="{
                'dark:bg-blue-dark bg-blue-light': index === highlightedIndex,
                'dark:hover:bg-blue-dark hover:bg-blue-light': true
            }"
                class="cursor-pointer text-sm p-2 m-2">
                <!-- These are the key changes.-->
                <a
                    x-on:click="selectResult(index)"
                    @* :href="result.url" *@
                    :hx-get="result.url"
                    hx-target="#contentcontainer"
                    hx-swap="innerHTML"
                    hx-push-url="true"
                    x-text="result.title"
                   >
                </a>
                <-- End of changes -->
            </li>
        </template>
    </ul>

</div>

```

سوف ترون أنني الآن أنتج *على نحو صحيح* الروابط HTMX في هذا بلاك الشيفرة. يسمح لنا باستخدام سلوك HT MX الصحيح.

## `typeahead.js`

لتشغيل هذا في شفرة جاواScript الخلفية خاصتي قمت بإضافة ما يلي إلى طريقة بحثي (مظهرة من الأسفل). `this.$nextTick` هو بناء ألبين .js الذي يؤجل هذا حتى قام ألبين بإنهاء معالجة النموذج الذي عرضته سابقاً .

ثم أستخدم `htmx.process()` على عنصر البحث الذي سيضمن أن خصائص HTMX تعمل كما هو متوقع.

```javascript

.then(data => {
 this.results = data;
this.highlightedIndex = -1; // Reset index on new search
 this.$nextTick(() => {
    htmx.process(document.getElementById('searchresults'));
 });
})
```

<details>
<summary>typeahead.js search </summary>

```javascript
   search() {
            if (this.query.length < 2) {
                this.results = [];
                this.highlightedIndex = -1;
                return;
            }
            let token = document.querySelector('#searchelement input[name="__RequestVerificationToken"]').value;
            console.log(token);
            fetch(`/api/search/${encodeURIComponent(this.query)}`, { // Fixed the backtick and closing bracket
                method: 'GET', // or 'POST' depending on your needs
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': token // Attach the AntiForgery token in the headers
                }
            })
                .then(response => {
                    if(response.ok){
                        return  response.json();
                    }
                    return Promise.reject(response);
                })
                .then(data => {
                    this.results = data;
                    this.highlightedIndex = -1; // Reset index on new search
                    this.$nextTick(() => {
                        htmx.process(document.getElementById('searchresults'));
                    });
                })
                .catch((response) => {
                    console.log(response.status, response.statusText);
                    if(response.status === 400)
                    {
                        console.log('Bad request, reloading page to try to fix it.');
                        window.location.reload();
                    }
                    response.json().then((json) => {
                        console.log(json);
                    })
                    console.log("Error fetching search results");
                });
        }
```

</details>
لاحقاً عند إختيار صفحة أقوم بمعالجة الرمز لإختيار الصفحة , تضغط على الرابط وتزيل النتائج ( لتغلق صندوق البحث

```javascript
selectHighlighted() {
            if (this.highlightedIndex >= 0 && this.highlightedIndex < this.results.length) {
                this.selectResult(this.highlightedIndex);
                
            }
        },

        selectResult(selectedIndex) {
       let links = document.querySelectorAll('#searchresults a');
       links[selectedIndex].click();
            this.$nextTick(() => {
                this.results = []; // Clear the results
                this.highlightedIndex = -1; // Reset the highlighted index
                this.query = ''; // Clear the query
            });
        }
```

هذا يتم اختياره من خلال النقر على الروابط في نتائج البحث.

```html
 <a
  x-on:click="selectResult(index)"
  :hx-get="result.url"
  hx-target="#contentcontainer"
  hx-swap="innerHTML"
  hx-push-url="true"
  x-text="result.title"
  >
  </a>
```

والذي سيقوم بعد ذلك بتحميل الصفحة و تحديث URL بصورة صحيحة.

لدي أيضاً برمجة في الصندوق الأصلي الذي يسمح لك باستخدام مفاتيح السهم وتدخل .

```html
    <label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
        <input
            type="text"
            x-model="query"
            x-on:input.debounce.200ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>

```

ستلاحظون أن هذا يحتوي على كل البرمجة اللازمة لتتمكنوا من الضغط فقط على "إدخال" والانتقال إلى الصفحة المختارة

# في الختام

مجرد تحديث سريع لكتابة البحث الموجودة لتحسين تجربتك المستخدمة عند استخدام البحث. مرة أخرى هذا هو مستخدم صغير يواجه التغيير ولكن فقط يحسن تجربته المستخدمة; الذين كمطور للويب هم اهتمامك الأولي M SK2 بجانب الحصول على المال :)).