Fermer

octobre 8, 2021

Création d'un générateur MadLib statique avec texte portable et fonctions de création à la demande Netlify


Résumé rapide ↬

Dans cet article, nous allons créer une expérience de fiction interactive dans laquelle un utilisateur peut insérer des mots qui correspondent à des parties du discours prononcées par le créateur de contenu. Allons-y!

Créer une expérience interactive avec la fiction peut être une corvée avec les outils de gestion de contenu traditionnels. Rédiger la prose, créer les formulaires, les combiner dans le frontend – ce sont souvent le domaine de trois personnes différentes.

Faisons-en le domaine d'un seul créateur de contenu dans lequel l'utilisateur remplira un formulaire avant de lire l'histoire – créant des histoires étranges et souvent drôles. Ce type d'expérience a été popularisé sous le nom de « Madlibs ».

Le créateur de madlib propose à l'utilisateur final un formulaire à remplir sans savoir comment ses mots seront utilisés dans l'histoire. Une fois le formulaire rempli, il remplit les endroits appropriés de l'histoire pour (espérons-le) des résultats amusants. ( Grand aperçu)

Comment le générateur fonctionnera

Un éditeur peut créer une série de madlibs qu'un utilisateur final peut remplir et enregistrer une copie avec ses réponses uniques. L'éditeur travaillera avec Sanity Studio dans un champ de texte enrichi que nous créerons pour fournir des informations supplémentaires à notre interface pour créer des formulaires.

Pour l'éditeur, cela ressemblera à l'écriture de contenu de paragraphe standard. Ils pourront écrire comme ils ont l'habitude d'écrire. Ils peuvent ensuite créer des blocs spécifiques dans leur contenu qui spécifieront une partie du discours et afficheront le texte.

Le frontal de l'application peut ensuite utiliser ces données pour afficher le texte et créer un formulaire. . Nous allons utiliser 11ty pour créer le frontend avec quelques petits modèles. Le formulaire qui est construit s'affichera pour l'utilisateur avant qu'il ne voie le texte. Ils sauront quel type de discours et quel contexte général pour les phrases et les mots qu'ils peuvent saisir.

Une fois le formulaire soumis, ils recevront leur histoire complète (avec des résultats, espérons-le, hilarants). Cette création ne sera définie que dans leur navigateur. S'ils souhaitent le partager, ils peuvent alors cliquer sur le bouton « Enregistrer ». Cela soumettra le texte entier à une fonction sans serveur dans Netlify pour l'enregistrer dans le magasin de données Sanity. Une fois celui-ci créé, un lien apparaîtra pour que l'utilisateur visualise la version permanente de sa madlib et la partage avec ses amis.

Comme 11ty est un générateur de site statique, on ne peut pas compter sur une reconstruction de site pour générer chacun Madlib enregistré par l'utilisateur à la volée. Nous pouvons utiliser le nouveau mode sans serveur de 11ty pour les créer sur demande à l'aide des constructeurs à la demande de Netlify pour mettre en cache chaque Madlib.

Le flux d'informations via le générateur. ( Grand aperçu)

Sanity.io

Sanity.io est une plate-forme de contenu unifiée qui considère que le contenu est constitué de données et que les données peuvent être utilisées comme contenu. Sanity associe un magasin de données en temps réel à trois outils open source : un puissant langage de requête (GROQ), un CMS (Sanity Studio) et une spécification de données en texte enrichi (Portable Text).

Portable Text

Portable Text est une spécification open source conçue pour traiter le texte enrichi comme des données. Nous utiliserons Portable Text pour le texte riche que nos éditeurs saisiront dans un Sanity Studio. Les données décoreront le texte riche de manière à ce que nous puissions créer un formulaire à la volée en fonction du contenu.

11ty And 11ty Serverless

11ty est un générateur de site statique intégré à Node. Il permet aux développeurs d'ingérer des données provenant de plusieurs sources, d'écrire des modèles dans plusieurs moteurs de modèles et de produire du HTML simple et propre.

Dans la prochaine version 1.0, 11ty introduit le concept de 11ty sans serveur. Cette mise à jour permet aux sites d'utiliser les mêmes modèles et données pour afficher les pages via une fonction sans serveur ou un générateur à la demande. 11ty Serverless commence à brouiller la frontière entre le «générateur de site statique» et la page rendue par le serveur.

Netlify On-Demand Builders

Netlify intègre des fonctions sans serveur à sa plate-forme depuis des années. Par exemple, un « On-Demand Builder » est une fonction sans serveur dédiée au service d'un fichier mis en cache. Chaque générateur fonctionne de la même manière qu'une fonction sans serveur standard lors du premier appel. Netlify met ensuite en cache cette page sur son CDN de périphérie pour chaque appel supplémentaire.

Plus après le saut ! Continuez à lire ci-dessous ↓

Création de l'interface d'édition et du magasin de données

Avant de pouvoir nous plonger dans les fonctions sans serveur et le frontend, il serait utile que nos données soient configurées et prêtes à être interrogées.

Pour ce faire, nous allons configurer un nouveau projet et installer Sanity's Studio (une plate-forme de contenu open source pour gérer les données dans votre Sanity Content Lake).

La vue finale pour un éditeur dans le Studio de santé mentale. ( Grand aperçu)

Pour créer un nouveau projet, nous pouvons utiliser les outils CLI de Sanity.

Tout d'abord, nous devons créer un nouveau répertoire de projet pour héberger à la fois le front-end et le studio. J'ai appelé le mien madlibs.

À partir de ce répertoire dans la ligne de commande, exécutez les commandes suivantes :

npm i -g @sanity/cli
sanity init

La commande sanity init vous posera une série de questions. Nommez votre projet madlibscréez un nouveau jeu de données appelé productiondéfinissez le « chemin de sortie » sur studioet pour « modèle de projet », sélectionnez « Nettoyer le projet sans schémas prédéfinis. »

La CLI crée un nouveau projet Sanity et installe toutes les dépendances nécessaires pour un nouveau studio. Dans le répertoire studio nouvellement créé, nous avons tout ce dont nous avons besoin pour faire notre expérience d'édition.

Avant de créer la première interface, exécutez sanity start dans le studio pour exécuter le studio.

Création du schéma madlib

Un ensemble de schémas définit l'interface d'édition du studio. Pour créer une nouvelle interface, nous allons créer un nouveau schéma dans le dossier schema.

// madlibs/studio/schemas/madlib.js

exporter par défaut {
  // Nom dans les données
  nom : 'madlib',
  // Titre visible par les éditeurs
  titre : 'Modèle Madlib',
  // Type de schéma (à ce stade soit document soit objet)
  tapez : 'document',
  // Un tableau de champs
  champs : [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      }
    },
  ]
}

Le fichier de schéma est un fichier JavaScript qui exporte un objet. Cet objet définit les données nametitletypeet tous les champs que le document aura.

Dans ce cas, nous allons commencer par un title chaîne et un slug qui peut être généré à partir du champ de titre. Une fois le fichier et le code initial créés, nous devons ajouter ce schéma à notre fichier schema.js.

// /madlibs/studio/schema/schema.js

// Tout d'abord, nous devons importer le créateur du schéma
importer createSchema à partir de 'part:@sanity/base/schema-creator'

// Ensuite, importez les types de schéma à partir de tous les plugins qui pourraient les exposer
importer les schemaTypes de 'all:part:@sanity/base/schema-type'

// Importe notre nouveau schéma
importer madlib depuis './madlib'

// Ensuite, nous donnons notre schéma au constructeur et fournissons le résultat à Sanity
exporter par défaut createSchema({
  // On nomme notre schéma
  nom : 'par défaut',
  // Procédez ensuite à la concaténation de notre type de document
  // à ceux fournis par les plugins installés
  types : schemaTypes.concat([
    // document
    // adds the schema to the list the studio will display
    madlib,
  ])
})

Ensuite, nous devons créer un éditeur de texte enrichi pour que nos auteurs madlib écrivent les modèles. Sanity a un moyen intégré de gérer le texte riche qui peut être converti en structure de données flexible Portable Text.

Pour créer l'éditeur, nous utilisons un champ array qui contient un type de schéma spécial : block.

Le type block renverra toutes les options par défaut pour le texte enrichi. Nous pouvons également étendre ce type pour créer des blocs spécialisés pour nos éditeurs.

export default {
  // Nom dans les données
  nom : 'madlib',
  // Titre visible par les éditeurs
  titre : 'Modèle Madlib',
  // Type de schéma (à ce stade soit document soit objet)
  tapez : 'document',
  // Un tableau de champs
  champs : [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      }
    },
    {
      title: 'Madlib Text',
      name: 'text',
      type: 'array',
      of: [
        {
          type: 'block',
          name: 'block',
          of: [
            // A new type of field that we'll create next
            { type: 'madlibField' }
          ]
        },
      ]
    },
  ]
}

Ce code configurera l'éditeur de texte portable. Il construit divers types de « blocs ». Les blocs correspondent à peu près aux données de niveau supérieur dans les données JSON que Portable Text retournera. Par défaut, les blocs standard prennent la forme d'éléments tels que des paragraphes, des en-têtes, des listes, etc.

Des blocs personnalisés peuvent être créés pour des éléments tels que des images, des vidéos et d'autres données. Pour nos champs madlib, nous voulons créer des blocs « inline » – des blocs qui s'écoulent dans l'un de ces blocs plus gros. Pour ce faire, le type block peut accepter son propre tableau of. Ces champs peuvent être de n'importe quel type, mais nous allons créer un type personnalisé et l'ajouter à notre schéma dans notre cas.

Créer un type de schéma personnalisé pour le champ Madlib

Pour créer un nouveau type personnalisé, nous devons créez un nouveau fichier et importez le schéma dans schema.js comme nous l'avons fait pour un nouveau type de document.

Au lieu de créer un schéma avec un type de documentnous devons créer l'un des type : object.

Ce type personnalisé doit avoir deux champs : le texte d'affichage et le type de grammaire. En structurant les données de cette manière, nous ouvrons des possibilités futures d'inspection de notre contenu.

En plus des champs de données pour ce type, nous pouvons également spécifier un aperçu personnalisé pour afficher plusieurs champs affichés dans le texte enrichi. Pour que cela fonctionne, nous définissons un composant React qui acceptera les données des champs et affichera le texte comme nous le souhaitons.

// /madlibs/studio/schemas/object/madLibField.js
importer React à partir de 'react'

// Un composant React qui prend la valeur des données
// et renvoie un simple aperçu des données utilisables
// dans l'éditeur de texte enrichi
fonction madlibAperçu({ valeur }) {
  const { texte, grammaire } = valeur

  revenir (
    
      {texte} ({grammaire})
    
  );
}

exporter par défaut {
  titre : 'Détails du champ Madlib',
  nom : 'madlibField',
  tapez : 'objet',
  champs : [
    {
      name: 'displayText',
      title: 'Display Text',
      type: 'string'
    },
    {
      name: 'grammar',
      title: 'Grammar Type',
      type: 'string'
    }
  ],
  // Définit un aperçu des données dans l'éditeur de texte enrichi
  Aperçu: {
    sélectionnez : {
      // Sélectionne les données à transmettre à notre composant
      texte : 'displayText',
      grammaire : 'grammaire'
    },
    
    // Indique au champ quel aperçu utiliser
    composant : madlibPreview,
  },
}

Une fois que cela est créé, nous pouvons l'ajouter à notre tableau de schémas et l'utiliser comme type dans nos blocs de texte portable.

 // /madlibs/studio/schemas/schema.js
// Tout d'abord, nous devons importer le créateur du schéma
importer createSchema depuis 'part:@sanity/base/schema-creator'

// Ensuite, importez les types de schéma à partir de tous les plugins susceptibles de les exposer
importer les schemaTypes de 'all:part:@sanity/base/schema-type'

importer madlib depuis './madlib'
// Importer le nouvel objet
importer madlibField depuis './objects/madlibField'

// Ensuite, nous donnons notre schéma au constructeur et fournissons le résultat à Sanity
exporter par défaut createSchema({
  // On nomme notre schéma
  nom : 'par défaut',
  // Procédez ensuite à la concaténation de notre type de document
  // à ceux fournis par les plugins installés
  types : schemaTypes.concat([
    // documents
    madlib,
    //objects
    madlibField
  ])
})

Création du schéma pour les madlibs générées par l'utilisateur

Comme les madlibs générées par l'utilisateur seront soumises à partir de notre interface, nous n'avons techniquement pas besoin d'un schéma pour elles. Cependant, si nous créons un schéma, nous obtenons un moyen simple de voir toutes les entrées (et de les supprimer si nécessaire).

Nous voulons que la structure de ces documents soit la même que celle de nos modèles madlib. Les principales différences entre ce schéma et notre schéma madlib sont le nametitleet, éventuellement, rendre les champs en lecture seule.

// /madlibs/studio/schema/userLib.js
exporter par défaut {
  nom : 'userLib',
  titre : « Madlibs générés par l'utilisateur »,
  tapez : 'document',
  champs : [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
      readOnly: true
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      readOnly: true,
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      },
    },
    {
      title: 'Madlib Text',
      name: 'text',
      type: 'array',
      readOnly: true,
      of: [
        {
          type: 'block',
          name: 'block',
          of: [
            { type: 'madlibField' }
          ]
        },
      ]
    },
  ]
}

Avec cela, nous pouvons l'ajouter à notre fichier schema.jset notre administrateur est complet. Avant de continuer, assurez-vous d'ajouter au moins un modèle madlib. J'ai trouvé que le premier paragraphe de Moby Dick fonctionnait étonnamment bien pour des résultats humoristiques. 11ty est un générateur de site statique écrit et étendu par Node. Il fait bien le travail de création HTML à partir de plusieurs sources de données, et avec quelques nouvelles fonctionnalités, nous pouvons étendre cela aux pages rendues par le serveur et aux pages rendues par build.

Configuration 11ty

Tout d'abord, nous aurons besoin pour tout mettre en place.

Dans le répertoire principal madlibscréons un nouveau répertoire site. Ce répertoire abritera notre site 11ty.

Ouvrez un nouveau terminal et changez le répertoire dans le répertoire site. À partir de là, nous devons installer quelques dépendances.

// Créer un nouveau package.json
npm init -y
// Installer les utilitaires 11ty et Sanity
npm install @11ty/eleventy@beta @sanity/block-content-to-html @sanity/client

Une fois ceux-ci installés, nous ajouterons quelques scripts à notre package.json

 // /madlibs/site/package.json

"scripts": {
 "start": "onze --serve",
 "build": "onze"
  },

Maintenant que nous avons un script de construction et de démarrage, ajoutons un modèle de base pour nos pages à utiliser et une page d'index.

Par défaut, 11ty recherchera dans un répertoire _includes nos modèles, alors créez ce répertoire et ajoutez-y un fichier base.njk.





  
  
  Madlibs
  {# Réinitialisation de base #}
  




  

  
{# Insère le contenu d'un fichier de page et le rend au format html #} {{ contenu | en sécurité }}
{% scripts de blocage %} {# Bloquer pour insérer des scripts à partir de modèles enfants #} {% bloc final %}

Une fois que nous avons un modèle, nous pouvons créer une page. Tout d'abord, à la racine du répertoire siteajoutez un fichier index.html. Ensuite, nous utiliserons frontmatter pour ajouter un peu de données – un titre et le fichier de mise en page à utiliser.

---
titre : Madlibs
mise en page : 'base.njk'
---

Des madlibs pour se changer les idées. Ils sont stockés dans Sanity.ioconstruits avec 11tyet font des choses intéressantes avec les fonctions sans serveur de Netlify.

Vous pouvez maintenant démarrer 11ty en exécutant npm start dans le répertoire site.

Maintenant, nous voulons créer des pages dynamiquement à partir des données de Sanity. Pour ce faire, nous allons créer un fichier de données JavaScript et un modèle de pagination.

Avant de nous plonger dans ces fichiers, nous devons créer quelques utilitaires pour travailler avec les données Sanity.

À l'intérieur du sitecréons un répertoire utils.

Le premier utilitaire dont nous avons besoin est un client Sanity JS initialisé. Tout d'abord, créez un fichier nommé sanityClient.js dans le nouveau répertoire utils.

// /madlibs/site/utils/sanityClient.js'
const sanityClient = require('@sanity/client')
module.exports = sanityClient({
  // L'identifiant du projet
  ID de projet : '',
  // L'ensemble de données que nous avons créé
  jeu de données : 'production',
  // La version de l'API que nous voulons utiliser
  // La meilleure pratique consiste à régler cela à la date d'aujourd'hui
  apiVersion: '2021-06-07',
  // Utiliser le CDN au lieu d'aller chercher directement à partir du magasin de données
  useCdn : vrai
})

Étant donné que notre texte enrichi est stocké au format Portable Text JSON, nous avons besoin d'un moyen de convertir les données en HTML. Nous allons créer un utilitaire pour le faire pour nous. Tout d'abord, créez un fichier nommé portableTextUtils.js dans le répertoire utils.

Pour les sites Sanity et 11ty, nous souhaitons généralement convertir le JSON en Markdown ou HTML. Pour ce site, nous utiliserons HTML pour avoir un contrôle granulaire sur la sortie.

Auparavant, nous avons installé @sanity/block-content-to-htmlce qui nous aidera à sérialiser les données en HTML . Le package fonctionnera sur tous les types de base de blocs et de styles de texte portable. Cependant, nous avons un type de bloc personnalisé qui nécessite un sérialiseur personnalisé.

 // Initialise le package
const toHtml = require('@sanity/block-content-to-html')
const h = toHtml.h;

const sérialiseurs = {
  les types: {
    madlibField : ({ nœud }) => {
      // Prend chaque nœud de `type` `madlibField`
      // et renvoie un span HTML avec un identifiant, une classe et un texte
      return h('span', node.displayText, { id: node._key, className: 'empty' })
    }
  }
}

const prepText = (données) => {
  // Prend les données d'un document Sanity spécifique
  // et crée une nouvelle propriété htmlText pour contenir le HTML
  // Cela nous permet de garder intactes les données de texte portable et d'afficher toujours le HTML
  revenir {
    ...Les données,
    htmlText : versHtml({
      blocs : data.text, // Données de texte portable
      serializers: serializers // Le sérialiseur à utiliser
    })
  }
}

// Nous avons seulement besoin d'exporter prepText pour nos fonctions
module.exports = { prepText }

L'objet serializers dans ce code a un objet types. Dans cet objet, nous créons un sérialiseur spécialisé pour tout type. La clé dans l'objet doit correspondre au type donné dans nos données. Dans notre cas, il s'agit de madlibField. Chaque type aura une fonction qui renvoie un élément écrit à l'aide de fonctions hyperscript.

Dans ce cas, nous créons un span avec les enfants du displayText à partir des données actuelles. Plus tard, nous aurons besoin d'identifiants uniques basés sur la _key des données, et nous aurons besoin d'une classe pour les styliser. Nous les fournissons dans un objet comme troisième argument de la fonction h(). Nous utiliserons cette même configuration de sérialiseur pour nos modèles madlib et les madlibs générés par l'utilisateur.

Maintenant que nous avons nos utilitaires, il est temps de créer un fichier de données JavaScript. Tout d'abord, créez un _data dans le répertoire site. Dans ce fichier, nous pouvons ajouter des données globales à notre site 11ty. Ensuite, créez un fichier madlibs.js. Ce fichier est l'endroit où notre JavaScript s'exécutera pour extraire chaque modèle madlib. Les données seront disponibles pour n'importe lequel de nos modèles et pages sous la clé madlibs.

// Obtenez nos utilitaires
client const = require('../utils/sanityClient')
const {prepText} = require('../utils/portableTextUtils')
// La requête GROQ utilisée pour trouver des documents spécifiques et
// forme la sortie
requête const = `*[_type == "madlib"]{
    Titre,
    "slug": slug.current,
    texte,
    _identifiant,
    "formFields": texte[]{
        enfants[_type == "madlibField"]{
            afficherTexte,
            grammaire,
            _clé
      }
      }.enfants[]
  }`

module.exports = fonction async () {
    // Récupérer les données en fonction de la requête
    const madlibs = wait client.fetch(query);

    // Préparer les données de texte portable
    const preppedMadlib = madlibs.map(prepText)
    // Retourne le tableau complet
    retour préparéMadlib
}

Pour récupérer les données, nous devons obtenir les utilitaires que nous venons de créer. Le client Sanity a une méthode fetch() pour passer une requête GROQ. Nous allons mapper le tableau de documents que la requête renvoie pour préparer leur texte portable, puis le renvoyer à la cascade de données de 11ty.

La requête GROQ dans cet exemple de code fait la plupart du travail pour nous. Nous commençons par demander tous les documents avec un _type de madlib à partir de notre lac de contenu Sanity. Ensuite, nous spécifions quelles données nous voulons retourner. Les données commencent simplement : nous avons besoin du titre, du slug, du texte enrichi et de l'identifiant du document, mais nous souhaitons également reformater les données dans un ensemble de champs de formulaire.

Pour ce faire, nous créons un nouveau propriété sur les données renvoyées : formFields. Cela examine les données text (un tableau de texte portable) et les boucle avec l'opérateur [] . Nous pouvons ensuite construire un nouveau projet sur ces données comme nous le faisons avec l'ensemble du document avec l'opérateur {}.

Chaque objet text a un children tableau. Nous pouvons parcourir cela, et si l'élément correspond au filtre à l'intérieur de []nous pouvons exécuter une autre projection sur cela. Dans ce cas, nous filtrons tous les enfants qui ont un _type == "madlibField". En d'autres termes, tout bloc en ligne qui a un élément avec le type que nous avons créé. Nous avons besoin de displayTextgrammar et _key pour chacun d'entre eux. Cela renverra un tableau d'objets text avec les enfants correspondant à notre filtre. Nous devons aplatir cela pour être un tableau d'enfants. Pour ce faire, nous pouvons ajouter les .children[] après les projets. Cela renverra un tableau plat avec uniquement les éléments enfants dont nous avons besoin.

Cela nous donne tous les documents dans un tableau avec uniquement les données dont nous avons besoin (y compris les éléments nouvellement reformatés).

Pour les utiliser dans notre 11ty build, nous avons besoin d'un modèle qui utilisera Pagination.

À la racine du sitecréez un fichier madlib.njk. Ce fichier générera chaque page madlib à partir des données.

---
mise en page : 'base.njk'
pagination:
  données : madlibs
  alias : madlib
  taille: 1
lien permanent : "madlibs/{{ madlib.slug | slug }}/index.html"
---

Au début de ce fichier, nous spécifions certaines données que 11ty peut utiliser pour générer nos pages :

  • layout
    Le modèle à utiliser pour afficher la page.
  • pagination
    Un objet avec pagination information.
  • pagination.data
    La clé de données pour la pagination à lire.
  • pagination.alias
    Une clé à utiliser dans ce fichier pour plus de facilité.
  • pagination.size
    Le nombre de madlibs par page (dans ce cas, 1 par page pour créer des pages individuelles).
  • permalink
    Les URL sur lesquelles chacun d'entre eux devrait vivre (peuvent être partiellement générés à partir des données).

Avec ces données en place , nous pouvons spécifier comment afficher chaque donnée pour un élément du tableau.

---
mise en page : 'base.njk'
pagination:
  données : madlibs
  alias : madlib
  taille: 1
lien permanent : "madlibs/{{ madlib.slug | slug }}/index.html"
---

{{ madlib.title }}

Instructions : Remplissez ce formulaire, soumettez-le et obtenez votre histoire. Espérons que cela n'aura que peu ou pas de sens. Ensuite, vous pouvez enregistrer le madlib et l'envoyer à vos amis.

Enregistrez-le {{ madlib.htmlTexte | en sécurité }}

Form

{% pour la saisie dans madlib.formFields %} {% endfor %}

Nous pouvons formater correctement le titre et le texte HTML. Nous pouvons ensuite utiliser le tableau formFields pour créer un formulaire dans lequel les utilisateurs peuvent saisir leurs réponses uniques.

Il existe des balises supplémentaires à utiliser dans notre JavaScript : un bouton de formulaire et un lien pour enregistrer la madlib finalisée. Le lien et le texte madlib seront masqués (pas de coup d'œil pour nos utilisateurs !).

Pour chaque modèle madlib que vous avez créé dans votre studio, 11ty construira une page unique. Les URL finales devraient ressembler à ceci

http://localhost:8080/madlibs/the-slug-in-the-studio/

Making The Madlibs Interactive

Avec nos madlibs générés, nous devons les rendre interactif. Nous saupoudrons un peu de JavaScript et CSS pour les rendre interactifs. Avant de pouvoir utiliser CSS et JS, nous devons dire à 11ty de copier les fichiers statiques sur notre site construit.

Copying Static Assets To The Final Build

À la racine du répertoire site créez les fichiers et répertoires suivants :

  • assets/css/style.css — pour tout style supplémentaire,
  • assets/js/madlib.js — pour les interactions,
  • .eleventy. js – le fichier de configuration 11ty.

Lorsque ces fichiers sont créés, nous devons dire à 11ty de copier les actifs dans la version finale. Ces instructions résident dans le fichier de configuration .eleventy.js.

module.exports = function(eleventyConfig) {
 onzetyConfig.addPassthroughCopy("actifs/");
}

Cela indique à 11ty de copier l'intégralité du répertoire assets dans la version finale.

Le seul CSS nécessaire pour que le site fonctionne est un extrait pour masquer et afficher le texte madlib. Cependant, si vous voulez le look and feel complet, vous pouvez trouver tous les styles dans ce fichier.

.madlibtext {
 affichage : aucun
}
.madlibtext.show {
 bloc de visualisation;
}

Remplir la Madlib avec l'entrée utilisateur et JavaScript

Tout framework frontal fonctionnera avec 11ty si vous configurez un processus de génération. Pour cet exemple, nous utiliserons du JavaScript simple pour simplifier les choses. La première tâche consiste à prendre les données utilisateur dans le formulaire et à remplir le modèle générique madlib que 11ty a généré à partir de nos données Sanity.

 // Attachez le gestionnaire de formulaire
forme const = document.querySelector('.madlibForm')
form.addEventListener('soumettre', completeLib);

fonction showText() {
  // Recherche du texte madlib dans le document
  const textDiv = document.querySelector('.madlibtext')
  // Bascule la classe "show" pour être présente
  textDiv.classList.toggle('afficher')
}

// Une fonction qui prend l'événement de soumission
// A partir de l'événement, il obtiendra le contenu des entrées
// et les écrire sur la page et afficher le texte complet
fonction completeLib(événement) {
  // Ne pas envoyer le formulaire
  event.preventDefault();
  const { cible } = événement // La cible est l'élément de formulaire

  // Récupère toutes les entrées du formulaire au format tableau
  entrées const = Array.from(target.elements)

  input.forEach(entrée => {
    // Le bouton est une entrée et nous ne voulons pas de cela dans les données finales
    if (input.type != 'text') return
    // Recherche d'un span par le nom de l'entrée
    // Ce seront tous les deux la valeur _key
    const ReplaceContent = document.getElementById(input.name)
    // Remplace le contenu du span par la valeur de l'entrée
    ReplaceContent.innerHTML = input.value
  })
  // Affiche la madlib terminée
  showText();
}

Cette fonctionnalité se compose de trois parties : attacher un écouteur d'événement, prendre la saisie du formulaire, l'insérer dans le code HTML, puis afficher le texte.

Lorsque le formulaire est soumis, le code crée un tableau à partir des entrées du formulaire. . Ensuite, il trouve des éléments sur la page avec des identifiants qui correspondent au nom de l'entrée – tous deux créés à partir des valeurs _key de chaque bloc. Il remplace ensuite le contenu de cet élément par la valeur des données.

Une fois cela fait, nous basculons le texte complet de madlib à afficher sur la page.

Nous devons ajouter ce script à la page. Pour ce faire, nous créons un nouveau modèle pour les madlibs à utiliser. Dans le répertoire _includescréez un fichier nommé lib.njk. Ce modèle étendra le modèle de base que nous avons créé et insère le script au bas du corps de la page.

{% extend 'base.njk' %}

{% scripts de blocage %}   {% endblock %}

Ensuite, notre modèle de pagination madlib.njk doit utiliser ce nouveau modèle pour sa mise en page.

---
mise en page : 'lib.njk'
pagination:
  données : madlibs
  alias : madlib
  taille: 1
lien permanent : "madlibs/{{ madlib.slug | slug }}/index.html"
---

// Contenu de la page

Nous avons maintenant un générateur madlib fonctionnel. Pour rendre cela plus robuste, permettons aux utilisateurs de sauvegarder et de partager leurs madlibs terminés. pour enregistrer, envoyez les informations à Sanity.

Pour ce faire, nous ajouterons des fonctionnalités supplémentaires à notre JavaScript frontal. Mais, d'abord, nous devons ajouter quelques données supplémentaires extraites de Sanity dans notre JavaScript, nous allons donc ajouter quelques nouvelles variables dans le bloc scripts sur le modèle lib.njk .

{% étend 'base.njk' %}

{% scripts de blocage %}   {% endblock %}

Nous pouvons écrire un script pour l'envoyer et les réponses générées par l'utilisateur à une fonction sans serveur à envoyer à Sanity avec ces données supplémentaires.

// /madlibs/site/assets/js/madlib.js

// ... completeLib()

fonction async saveLib(événement) {
  event.preventDefault();

  // Retourne une Map des identifiants et du contenu à transformer en objet
  const blocks = Array.from(document.querySelectorAll('.empty')).map(item => {
    retour [item.id, { content: item.outerText }]
  })
  // Crée un objet prêt pour le stockage à partir de la carte des blocs
  const userContentBlocks = Object.fromEntries (blocs);

  // Formate les données pour la publication
  const donnéefinale = {
    userContentBlocks,
    pt, // De nunjucks à la page
    ...données // De nunjucks à la page
  }

  // Exécute la fonction post data pour createLib
  postData('/.netlify/functions/createLib', finalData)
    .then(données => {
      // Lorsque la publication est réussie
      // Créer un div pour le lien final
      const landingZone = document.createElement('div')
      // Donne une classe au lien
      landingZone.className = "libUrl"
      // Ajoute le div après le lien de sauvegarde
      saver.after(landingZone)
      // Ajout du nouveau lien à l'intérieur de la zone d'atterrissage
      landingZone.innerHTML = `Votre URL est /userlibs/${data._id}/`

    }).catch(erreur => {
      // Lorsque des erreurs se produisent, faites quelque chose avec elles
      console.log(erreur)
    });
}

fonction asynchrone postData(url = '', data = {}) {
  // Une fonction wrapper pour la récupération JS standard
  réponse const = wait fetch (url, {
    méthode : 'POSTER',
    mode : « cors »,
    cache : 'no-cache',
    identifiants : 'même origine',
    en-têtes : {
      « Type de contenu » : « application/json »
    },
    corps : JSON.stringify(données)
  });
  return response.json(); // analyse la réponse JSON en objets JavaScript natifs
}

Nous ajoutons un nouvel écouteur d'événement au lien « Enregistrer » dans notre code HTML.

La fonction saveLib récupérera les données de la page et les données générées par l'utilisateur et les combinera dans un objet être géré par une nouvelle fonction sans serveur. La fonction sans serveur doit prendre ces données et créer un nouveau document Sanity. Lors de la création de la fonction, nous voulons qu'elle renvoie le _id pour le nouveau document. Nous l'utilisons pour créer un lien unique que nous ajoutons à la page. Ce lien sera l'endroit où se trouvera la page nouvellement générée.

Configuration de Netlify Dev

Pour utiliser les fonctions Netlify, nous devrons configurer notre projet sur Netlify. Nous voulons que Netlify soit construit et servi à partir du répertoire site. Pour donner à Netlify ces informations, nous devons créer un fichier netlify.toml à la racine de l'ensemble du projet.

[build]
 command = "npm run build" # Commande à exécuter
 fonctions = "fonctions" # Répertoire nous stockons les fonctions
 publier = "_site" # Dossier à publier (11ty crée automatiquement le dossier _site
 base = "site" # Dossier qui est la racine du build

Pour les développer localement, il est utile d'installer la CLI de Netlify globalement.

npm install -g netlify-cli

Une fois installé, vous pouvez exécuter netlify dev in your project. This will take the place of running your start NPM script.

The CLI will run you through connecting your repository to Netlify. Once it’s done, we’re ready to develop our first function.

Creating A Function To Save Madlibs To Sanity

Since our TOML file sets the functions directory to functionswe need to create the directory. Inside the directory, make a createLib.js file. This will be the serverless function for creating a madlib in the Sanity data store.

The standard Sanity client we’ve been using is read-only. To give it write permissions, we need to reconfigure it to use an API read+write token. To generate a token, log into the project dashboard and go to the project settings for your madlibs project. In the settings, find the Tokens area and generate a new token with “Editor” permissions. When the token is generated, save the string to Netlify’s environment variables dashboard with the name SANITY_TOKEN. Netlify Dev will automatically pull these environment variables into the project while running.

To reconfigure the client, we’ll require the file from our utilities, and then run the .config() method. This will let us set any configuration value for this specific use. We’ll set the token to the new environment variable and set useCdn to false.

// Sanity JS Client
// The build client is read-only
// To use to write, we need to add an API token with proper permissions
const client = require('../utils/sanityClient')
client.config({
    token: process.env.SANITY_TOKEN,
    useCdn: false
})

The basic structure for a Netlify function is to export a handler function that is passed an event and returns an object with a status code and string body.

// Grabs local env variables from .env file
// Not necessary if using Netlify Dev CLI
require('dotenv').config()

// Sanity JS Client
// The build client is read-only
// To use to write, we need to add an API token with proper permissions
const client = require('../utils/sanityClient')
client.config({
  token: process.env.SANITY_TOKEN,
  useCdn: false
})

// Small ID creation package
const { nanoid } = require('nanoid')

exports.handler = async (event) => {
  // Get data off the event body
  const {
    pt,
    userContentBlocks,
    id,
    libTitle
  } = JSON.parse(event.body)

  // Create new Portable Text JSON
  // from the old PT and the user submissions
  const newBlocks = findAndReplace(pt, userContentBlocks)

  // Create new Sanity document object
  // The doc's _id and slug are based on a unique ID from nanoid
  const docId = nanoid()
  const doc = {
    _type: "userLib",
    _id: docId,
    slug: { current: docId },
    madlib: id,
    title: `${libTitle} creation`,
    text: newBlocks,
  }

  // Submit the new document object to Sanity
  // Return the response back to the browser
  return client.create(doc).then((res) => {
    // Log the success into our function log
    console.log(`Userlib was created, document ID is ${res._id}`)
    // return with a 200 status and a stringified JSON object we get from the Sanity API
    return { statusCode: 200, body: JSON.stringify(doc) };
  }).catch(err => {
    // If there's an error, log it
    // and return a 500 error and a JSON string of the error
    console.log(err)
    return {
      statusCode: 500, body: JSON.stringify(err)
    }
  })
}

// Function for modifying the Portable Text JSON
// pt is the original portable Text
// mods is an object of modifications to make 
function findAndReplace(pt, mods) {
  // For each block object, check to see if a mod is needed and return an object
  const newPT = pt.map((block) => ({
    ...block, // Insert all current data
    children: block.children.map(span => {
      // For every item in children, see if there's a modification on the mods object
      // If there is, set modContent to the new content, if not, set it to the original text 
      const modContent = mods[span._key] ? mods[span._key].content : span.text
      // Return an object with all the original data, and a new property
      // displayText for use in the frontends
      return {
        ...span,
        displayText: modContent
      }
    })
  }))
  // Return the new Portable Text JSON
  return newPT
}

The body is the data we just submitted. For ease, we’ll destructure the data off the event.body object. Then, we need to compare the original Portable Text and the user content we submitted and create the new Portable Text JSON that we can submit to Sanity.

To do that, we run a find and replace function. This function maps over the original Portable Text and for every child in the blocks, replace its content with the corresponding data from the modfications object. If there isn’t a modification, it will store the original text.

With modified Portable Text in hand, we can create a new object to store as a document in the Sanity content lake. Each document needs a unique identifier (which we can use the nanoid NPM package to create. We’ll also let this newly created ID be the slug for consistency.

The rest of the data is mapped to the proper key in our userLib schema we created in the studio and submitted with the authenticated client’s .create() method. When success or failure returns from Sanity, we pass that along to the frontend for handling.

Now, we have data being saved to our Sanity project. Go ahead and fill out a madlib and submit. You can view the creation in the studio. Those links that we’re generating don’t work yet, though. This is where 11ty Serverless comes in.

Setting Up 11ty Serverless

You may have noticed when we installed 11ty that we used a specific version. This is the beta of the upcoming 1.0 release. 11ty Serverless is one of the big new features in that release.

Installing The Serverless Plugin

11ty Serverless is an included plugin that can be initialized to create all the boilerplate for running 11ty in a serverless function. To get up and running, we need to add the plugin to our .eleventy.js configuration file.

const { EleventyServerlessBundlerPlugin } = require("@11ty/eleventy");

module.exports = function (eleventyConfig) {
  eleventyConfig.addPassthroughCopy("assets/");

  eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, {
    name: "userlibs", // the name to use for the functions
    functionsDir: "./functions/", // The functions directory
    copy: ["utils/"]// Any files that need to be copied to make our scripts work
    excludeDependencies: ["./_data/madlibs.js"] // Exclude any files you don't want to run
  });
};

After creating this file, restart 11ty by rerunning netlify dev. On the next run, 11ty will create a new directory in our functions directory named userlibs (matching the name in the serverless configuration) to house everything it needs to have to run in a serverless function. The index.js file in this directory is created if it doesn’t exist, but any changes you make will persist.

We need to make one small change to the end of this file. By default, 11ty Serverless will initialize using standard serverless functions. This will run the function on every load of the route. That’s an expensive load for content that can’t be changed after it’s been generated. Instead, we can change it to use Netlify’s On-Demand Builders. This will build the page on the first request and cache the result for any later requests. This cache will persist until the next build of the site.

To update the function, open the index.js file and change the ending of the file.

// Comment this line out
exports.handler = handler

// Uncomment these lines
const { builder } = require("@netlify/functions");
exports.handler = builder(handler);

Since this file is using Netlify’s functions package, we also need to install that package.

npm install @netlify/functions

Creating A Data File For User-generated Madlibs

Now that we have an On-Demand Builder, we need to pull the data for user-generated madlibs. We can create a new JavaScript data file in the _data file named userlibs.js. Like our madlibs data file, the file name will be the key to get this data in our templates.

// /madlibs/site/_data/userlibs.js

const client = require('../utils/sanityClient')
const {prepText} = require('../utils/portableTextUtils')

const query = `*[_type == "userLib"]{
    title,
    "slug": slug.current,
    text,
    _id
  }`

module.exports = async function() {
    const madlibs = await client.fetch(query);
    // Protect against no madlibs returning
    if (madlibs.length == 0) return {"404": {}} 

    // Run through our portable text serializer
    const preppedMadlib = madlibs.map(prepText)

    // Convert the array of documents into an object
    // Each item in the Object will have a key of the item slug
    // 11ty's Pagination will create pages for each one
    const mapLibs = preppedMadlib.map(item => ([item.slug, item]))
    const objLibs = Object.fromEntries(mapLibs)
    return objLibs
}

This data file is similar to what we wrote earlier, but instead of returning the array, we need to return an object. The object’s keys are what the serverless bundle will use to pull the correct madlib on request. In our case, we’ll make the item’s slug the key since the serverless route will be looking for a slug.

Now that the plugin is ready, we can create a new pagination template to use the generated function.

In the root of our siteadd a userlibs.njk template. This template will be like the madlibs.njk template, but it will use different data without any interactivity.

---
layout: 'base.njk'
pagination:
  data: userLibs
  alias: userlib
  size: 1
  serverless: eleventy.serverless.path.slug

permalink: 
  userlibs: "/userlibs/:slug/"
---

{{ userlib.title }}

{{ userlib.htmlText | safe }}

In this template, we use base.njk to avoid including the JavaScript. We specify the new userlibs data for pagination.

To pull the correct data, we need to specify what the lookup key will be. On the pagination object, we do this with the serverless property. When using serverless routes, we get access to a new object: eleventy.serverless. On this object, there’s a path object that contains information on what URL the user requested. In this case, we’ll have a slug property on that object. That needs to correspond to a key on our pagination data.

To get the slug on our path, we need to add it to the permalink object. 11ty Serverless allows for more than one route for a template. The route’s key needs to match the name provided in the .eleventy.js configuration. In this case, it should be userlibs. We specify the static /userlibs/ start to the path and then add a dynamic element: :slug/. This slug will be what gets passed to eleventy.serverless.path.slug.

Now, the link that we created earlier by submitting a madlib to Sanity will work.

Next Steps

Now we have a madlib generator that saves to a data store. We build only the necessary pages to allow a user to create a new madlib. When they create one, we make those pages on-demand with 11ty and Netlify Functions. From here, we can extend this further.

  • Statically build the user-generated content as well as render them on request.
  • Create a counter for the total number of madlibs saved by each madlib template.
  • Create a list of words users use by parts of speech.

When you can statically build AND dynamically render, what sorts of applications does this open up?

Smashing Editorial(vf, yk, il)




Source link