كيفية استخدام واجهة برمجة تطبيقات البحث في ويكيبيديا لبناء واجهة مستخدم مع RamdaJS

في هذا البرنامج التعليمي ، سننشئ واجهة مستخدم باستخدام واجهة برمجة تطبيقات البحث العام في Wikipedia جنبًا إلى جنب مع بعض JavaScript + RamdaJS.

ابدء

إليك رابط GitHub ورابط Codesandbox. افتح الجهاز واختر دليلًا لاستنساخه.

git clone [//github.com/yazeedb/ramda-wikipedia-search](//github.com/yazeedb/ramda-wikipedia-search) cd ramda-wikipedia-search yarn install (or npm install) 

يحتوي masterالفرع على المشروع النهائي ، لذا تحقق من startالفرع إذا كنت ترغب في الترميز.

git checkout start

وابدأ المشروع!

npm start

يجب أن يفتح المستعرض الخاص بك المضيف المحلي تلقائيًا: 1234.

الحصول على قيمة الإدخال

هذا هو التطبيق الأولي.

لالتقاط مدخلات المستخدم أثناء الكتابة ، inputيحتاج عنصرنا إلى مستمع للحدث.

تم توصيل ملفك src/index.jsبالفعل وجاهز للعمل. ستلاحظ أننا قمنا باستيراد Bootstrap للتصميم.

دعنا نضيف مستمعًا وهميًا للحدث لبدء الأمور.

import 'bootstrap/dist/css/bootstrap.min.css'; const inputElement = document.querySelector('input'); inputElement.addEventListener('keyup', (event) => { console.log('value:', event.target.value); }); 

نحن نعرف event.target.valueالطريقة القياسية للوصول إلى قيمة المدخلات. الآن يظهر القيمة.

كيف يمكن أن تساعدنا رمدة في تحقيق ما يلي؟

  • إختطاف event.target.value
  • تقليم الإخراج (المسافة البيضاء بين السطور / الزائدة للشريط)
  • افتراضي لإفراغ السلسلة إذا undefined

و pathOrظيفة يمكن فعلا التعامل مع النقاط الأولى والثالثة. يأخذ ثلاث معاملات: الافتراضي ، المسار ، والبيانات.

لذا فإن ما يلي يعمل بشكل مثالي

import { pathOr } from 'ramda'; const getInputValue = pathOr('', ['target', 'value']); 

إذا كان event.target.valueالأمر كذلك undefined، فسنستعيد سلسلة فارغة!

Ramda لديها أيضًا trimوظيفة ، بحيث تحل مشكلة المسافات البيضاء لدينا.

import { pathOr, trim } from 'ramda'; const getInputValue = (event) => trim(pathOr('', ['target', 'value'], event)); 

بدلاً من تداخل هذه الوظائف ، دعنا نستخدم pipe. انظر مقالتي على الأنابيب إذا كانت جديدة بالنسبة لك.

import { pathOr, pipe, trim } from 'ramda'; const getInputValue = pipe( pathOr('', ['target', 'value']), trim ); 

لدينا الآن وظيفة مؤلفة تأخذ eventكائنًا ، وتلتقطه ، وتتخلف عن target.valueالسداد ''، وتقطعه.

جميلة.

أوصي بتخزين هذا في ملف منفصل. ربما تسميها getInputValue.jsواستخدم بناء جملة التصدير الافتراضي.

الحصول على عنوان URL لـ Wikipedia

حتى كتابة هذه السطور ، عنوان URL لبحث API الخاص بـ Wikipedia هو //en.wikipedia.org/w/api.php؟origin=*&action=opensearch&search=

للبحث الفعلي ، ما عليك سوى إلحاق موضوع. إذا كنت بحاجة إلى الدببة ، على سبيل المثال ، فسيبدو عنوان URL على النحو التالي:

//en.wikipedia.org/w/api.php؟origin=*&action=opensearch&search=bears

نود وظيفة تأخذ موضوعًا وتعيد عنوان URL الكامل لبحث ويكيبيديا. نظرًا لأن أنواع المستخدمين ، فإننا نبني عنوان URL بناءً على مدخلاتهم.

رامدا concatتعمل بشكل جيد هنا.

import { concat } from 'ramda'; const getWikipediaSearchUrlFor = concat( '//en.wikipedia.org/w/api.php?origin=*&action=opensearch&search=' ); 

concat، طبقًا لاسمها ، تسلسل السلاسل والمصفوفات. إنه أمر رائع ، لذا فإن توفير عنوان URL لأن إحدى الوسيطة تُرجع دالة تتوقع سلسلة ثانية. انظر مقالتي عن الكاري إذا كانت جديدة!

ضع هذا الرمز في وحدة تسمى getUrl.js.

الآن دعنا نحدث index.js. استيراد وحدتين جديدتين ، مع pipeومن tapومن رمدة.

import 'bootstrap/dist/css/bootstrap.min.css'; import { pipe, tap } from 'ramda'; import getInputValue from './getInputValue'; import getUrl from './getUrl'; const makeUrlFromInput = pipe( getInputValue, getUrl, tap(console.warn) ); const inputElement = document.querySelector('input'); inputElement.addEventListener('keyup', makeUrlFromInput); 

يقوم هذا الرمز الجديد بإنشاء عنوان URL لطلبنا من إدخال المستخدم وتسجيله عبر tap.

تحقق من ذلك.

تقديم طلب AJAX

الخطوة التالية هي تعيين عنوان URL هذا لطلب AJAX وتجميع استجابة JSON.

استبدل makeUrlFromInputبوظيفة جديدة ، searchAndRenderResults.

const searchAndRenderResults = pipe( getInputValue, getUrl, (url) => fetch(url) .then((res) => res.json()) .then(console.warn) ); 

لا تنس تغيير مستمع الحدث أيضًا!

inputElement.addEventListener('keyup', searchAndRenderResults); 

ها هي نتيجتنا.

صنع مكون النتائج

الآن بعد أن أصبح لدينا JSON ، فلنقم بإنشاء مكون يُجمله.

أضف Results.jsإلى الدليل الخاص بك.

انظر إلى الوراء في استجابة JSON لبحث ويكيبيديا. لاحظ شكله. إنها مصفوفة بالمؤشرات التالية:

  1. الاستعلام (ما الذي بحثت عنه)
  2. مجموعة أسماء النتائج
  3. صفيف الملخصات
  4. Array of links to results

Our component can take an array of this shape and return a nicely formatted list. Through ES6 array destructuring, we can use that as our function signature.

Edit Results.js

export default ([query, names, summaries, links]) => ` 

Searching for "${query}"

    ${names.map( (name, index) => `
  • ${name}

    ${summaries[index]}

  • ` )}
`;

Let’s go step by step.

  • It’s a function that takes an array of our expected elements: query, names, summaries, and links.
  • Using ES6 template literals, it returns an HTML string with a title and a list.
  • Inside the
      we map names to
    • tags, so one for each.
    • Inside those are tags pointing to each result’s link. Each link opens in a new tab.
    • Below the link is a paragraph summary.

    Import this in index.js and use it like so:

    // ... import Results from './Results'; // ... const searchAndRenderResults = pipe( getInputValue, getUrl, (url) => fetch(url) .then((res) => res.json()) .then(Results) .then(console.warn) ); 

    This passes the Wikipedia JSON to Results and logs the result. You should be seeing a bunch of HTML in your DevTools console!

    All that’s left is to render it to the DOM. A simple render function should do the trick.

    const render = (markup) => { const resultsElement = document.getElementById('results'); resultsElement.innerHTML = markup; }; 

    Replace console.warn with the render function.

    const searchAndRenderResults = pipe( getInputValue, getUrl, (url) => fetch(url) .then((res) => res.json()) .then(Results) .then(render) ); 

    And check it out!

    Each link should open in a new tab.

    Removing Those Weird Commas

    You may have noticed something off about our fresh UI.

    It has extra commas! Why??

    Template Literals

    It’s all about how template literals join things. If you stick in an array, it’ll join it using the toString() method.

    See how this becomes joined?

    const joined = [1, 2, 3].toString(); console.log(joined); // 1,2,3 console.log(typeof joined); // string 

    Template literals do that if you put arrays inside of them.

    const nums = [1, 2, 3]; const msg = `My favorite nums are ${nums}`; console.log(msg); // My favorite nums are 1,2,3 

    You can fix that by joining the array without commas. Just use an empty string.

    const nums = [1, 2, 3]; const msg = `My favorite nums are ${nums.join('')}`; console.log(msg); // My favorite nums are 123 

    Edit Results.js to use the join method.

    export default ([query, names, summaries, links]) => ` 

    Searching for "${query}"

      ${names .map( (name, index) => `
    • ${name}

      ${summaries[index]}

    • ` ) .join('')}
    `;

    Now your UI’s much cleaner.

    Fixing a Little Bug

    I found a little bug while building this. Did you notice it?

    Emptying the input throws this error.

    That’s because we’re sending an AJAX request without a search topic. Check out the URL in your Network tab.

    That link points to a default HTML page. We didn’t get JSON back because we didn’t specify a search topic.

    To prevent this from happening we can avoid sending the request if the input's empty.

    We need a function that does nothing if the input's empty, and does the search if it’s filled.

    Let’s first create a function called doNothing. You can guess what it looks like.

    const doNothing = () => {}; 

    This is better known as noOp, but I like doNothing in this context.

    Next remove getInputValue from your searchAndRenderResults function. We need a bit more security before using it.

    const searchAndRenderResults = pipe( getUrl, (url) => fetch(url) .then((res) => res.json()) .then(Results) .then(render) ); 

    Import ifElse and isEmpty from Ramda.

    import { ifElse, isEmpty, pipe, tap } from 'ramda'; 

    Add another function, makeSearchRequestIfValid.

    const makeSearchRequestIfValid = pipe( getInputValue, ifElse(isEmpty, doNothing, searchAndRenderResults) ); 

    Take a minute to absorb that.

    If the input value’s empty, do nothing. Else, search and render the results.

    You can gather that information just by reading the function. That’s expressive.

    Ramda’s isEmpty function works with strings, arrays, objects.

    This makes it perfect to test our input value.

    ifElse fits here because when isEmpty returns true, doNothing runs. Otherwise searchAndRenderResults runs.

    Lastly, update your event handler.

    inputElement.addEventListener('keyup', makeSearchRequestIfValid); 

    And check the results. No more errors when clearing the input!

    This tutorial was from mycompletely freecourse on Educative.io, Functional Programming Patterns With RamdaJS!

    Please consider taking/sharing it if you enjoyed this content.

    It’s full of lessons, graphics, exercises, and runnable code samples to teach you a basic functional programming style using RamdaJS.

    Thank you for reading ❤️