Πώς να χρησιμοποιήσετε το API αναζήτησης της Wikipedia για να δημιουργήσετε μια διεπαφή χρήστη με το RamdaJS

Σε αυτό το σεμινάριο, θα δημιουργήσουμε μια διεπαφή χρήστη χρησιμοποιώντας το δημόσιο API αναζήτησης της 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

Το πρόγραμμα περιήγησής σας θα ανοίξει αυτόματα το localhost: 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τον τυπικό τρόπο πρόσβασης στην τιμή μιας εισόδου. Τώρα δείχνει την αξία.

Πώς μπορεί η Ramda να μας βοηθήσει να επιτύχουμε τα εξής;

  • Αρπάζω 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 αναζήτησης της Wikipedia. Καθώς οι τύποι χρηστών δημιουργούμε τη διεύθυνση URL με βάση την είσοδό τους.

Η Ramda 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από τη Ramda.

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 αναζήτησης Wikipedia. Σημειώστε το σχήμα του. Είναι ένας πίνακας με τους ακόλουθους δείκτες:

  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 ❤️