Ajoutez des blogs, de la documentation et d'autres pages statiques aux applications Phoenix. Cette bibliothèque s'intègre parfaitement à votre routeur et est livrée avec une prise en charge intégrée du rendu des démarques avec frontmatter, de la coloration syntaxique, de la mise en cache au moment de la compilation, etc.
def deps do
[
{ :phoenix_pages , "~> 0.1" }
]
end
La méthode recommandée pour l'installation dans votre application Phoenix est d'ajouter ceci à la fonction de votre router
dans lib/myapp_web.ex
, en remplaçant myapp
par le nom de votre application :
def router do
quote do
use Phoenix.Router , helpers: false
use PhoenixPages , otp_app: :myapp
# ...
end
end
Vous pouvez maintenant ajouter un nouvel itinéraire à l'aide de la macro pages/4
:
scope "/" , MyAppWeb do
pipe_through :browser
get "/" , PageController , :home
pages "/:page" , PageController , :show , from: "priv/pages/**/*.md"
end
Cela lira tous les fichiers markdown de priv/pages
et créera une nouvelle route GET pour chacun. Le segment :page
sera remplacé par le chemin et le nom de fichier (sans l'extension) relatifs au répertoire de base (voir Définir les chemins).
Vous devrez également ajouter le gestionnaire :show
à lib/myapp_web/controllers/page_controller.ex
:
defmodule MyAppWeb.PageController do
use MyAppWeb , :controller
# ...
def show ( conn , _params ) do
render ( conn , "show.html" )
end
end
Enfin, ajoutez un modèle à lib/myapp_web/controllers/page_html/show.html.heex
. La démarque rendue de la page sera disponible dans l'attribut inner_content
:
< main >
<%= @inner_content % >
</ main >
C'est ça! Essayez maintenant de créer un fichier sur priv/pages/hello.md
et de visiter /hello
.
Pour empêcher mix format
d'ajouter des parenthèses à la macro pages
similaire aux autres macros du routeur Phoenix, ajoutez :phoenix_pages
à .formatter.exs
:
[
import_deps: [ :ecto , :ecto_sql , :phoenix , :phoenix_pages ]
]
Frontmatter permet d'inclure des variables spécifiques à une page en haut d'un fichier markdown en utilisant le format YAML. Si vous définissez des variables de premier plan (ce qui est facultatif), elles doivent constituer la première chose du fichier et doivent être définies entre des lignes à trois tirets :
---
title : Hello World
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Pour spécifier quelles valeurs de frontmatter sont attendues dans chaque page, définissez l'option attrs
:
pages "/:page" , PageController , :show ,
from: "priv/pages/**/*.md" ,
attrs: [ :title , author: nil ]
Les valeurs atomiques seront considérées comme obligatoires et une erreur de compilation sera générée si elles sont manquantes dans l'une des pages. Les valeurs-clés doivent figurer en dernier dans la liste et seront considérées comme facultatives en définissant une valeur par défaut. Toutes les valeurs de premier plan non définies dans la liste des attributs seront ignorées en silence.
Les valeurs d'attribut valides seront disponibles dans les attributions :
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >
Phoenix Pages utilise le projet Makeup pour la coloration syntaxique. Pour l'activer, ajoutez un lexer pour votre ou vos langues spécifiques aux dépendances du projet. Phoenix Pages récupérera la nouvelle dépendance et commencera à mettre en évidence vos blocs de code sans aucune configuration supplémentaire. Aucun lexer n'est inclus par défaut.
`{:makeup_c, "~> 0.0"}`
`{:makeup_diff, "~> 0.0"}`
`{:makeup_elixir, "~> 0.0"}`
`{:makeup_erlang, "~> 0.0"}`
`{:makeup_graphql, "~> 0.0"}`
`{:makeup_eex, "~> 0.0"}`
`{:makeup_html, "~> 0.0"}`
`{:makeup_js, "~> 0.0"}`
`{:makeup_json, "~> 0.0"}`
`{:makeup_rust, "~> 0.0"}`
`{:makeup_sql, "~> 0.0"}`
Si la langue de votre choix n'est pas prise en charge, envisagez d'écrire un nouveau lexer de maquillage pour contribuer à la communauté. Sinon, vous pouvez utiliser un surligneur de syntaxe basé sur JS tel que highlight.js en définissant code_class_prefix: "language-"
et syntax_highlighting: false
dans render_options
.
Ensuite, importez un thème répertorié ci-dessous dans votre bundle CSS. Les spécificités de cette opération dépendent fortement de votre configuration CSS, mais quelques exemples sont inclus ci-dessous. Dans la plupart des cas, vous devrez importer phoenix_pages/css/monokai.css
(ou le thème que vous choisissez) dans votre offre groupée et vous assurer que deps
est inclus en tant que répertoire de fournisseurs.
À l'aide du programme d'installation ESBuild, ajoutez l'option env
à config/config.exs
:
config :esbuild ,
version: "0.17.18" ,
default: [
cd: Path . expand ( "../assets" , __DIR__ ) ,
env: % { "NODE_PATH" => Path . expand ( "../deps" , __DIR__ ) } ,
args: ~w ( --bundle --outdir=../priv/static/assets js/app.js )
]
Puis dans app.js
:
import "phoenix_pages/css/monokai.css" ;
À l'aide du programme d'installation Sass, ajoutez l'indicateur --load-path
à config/config.exs
:
config :dart_sass ,
version: "1.62.0" ,
default: [
cd: Path . expand ( "../assets" , __DIR__ ) ,
args: ~w ( --load-path=../deps css/app.scss ../priv/static/assets/app.css )
]
Puis dans app.scss
:
@import " phoenix_pages/css/monokai " ;
Installez le plugin postcss-import
comme décrit ici et ajoutez ce qui suit à assets/postcss.config.js
:
module . exports = {
plugins : {
"postcss-import" : { }
}
}
Puis dans app.css
:
@import "../../deps/phoenix_pages/css/monokai" ;
Pour créer une page d'index avec des liens vers toutes les autres pages, créez une route GET normale et utilisez l'option id
à côté de get_pages/1
et get_pages!/1
dans votre routeur :
get "/blog" , BlogController , :index
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ]
defmodule MyAppWeb.BlogController do
use MyAppWeb , :controller
def index ( conn , _params ) do
pages = MyAppWeb.Router . get_pages! ( :blog )
conn
|> assign ( :pages , pages )
|> render ( "index.html" )
end
def show ( conn , _params ) do
render ( conn , "show.html" )
end
end
<.link :for={page < - @pages} navigate = {page.path} >
<%= page.assigns.title % >
</.link>
Tous les fichiers d'échange sont lus et mis en cache lors de la compilation, de sorte que les fonctions get_pages
ne liront rien du système de fichiers, ce qui les rend très performantes.
Les pages renvoyées par les fonctions get_pages
seront triées par nom de fichier. Si vous souhaitez spécifier un ordre différent lors de la compilation plutôt que dans le contrôleur à chaque chargement de page, utilisez l'option sort
:
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ] ,
sort: { :date , :desc }
Toute valeur d'attribut du frontmatter peut être définie comme valeur de tri.
Lors de la définition du chemin des pages, le segment :page
sera remplacé pour chaque page générée lors de la compilation par les valeurs dérivées de **
et *
. Ceci est différent des segments des routes régulières, qui sont analysés pendant l'exécution dans l'attribut params
de la fonction du contrôleur.
Par exemple, disons que vous disposez de la structure de fichiers suivante :
┌── priv/
│ ┌── pages/
│ │ ┌── foo.md
│ │ ├── bar/
│ │ │ ┌── baz.md
Définir pages "/:page", from: "priv/pages/**/*.md"
dans votre routeur créera deux routes : get "/foo"
et get "/bar/baz"
. Vous pouvez même placer le segment :page
ailleurs dans le chemin, par exemple /blog/:page
, et cela fonctionnera comme prévu en créant get "/blog/foo"
et get "/blog/bar/baz"
.
Pour les scénarios complexes, vous avez la possibilité d'utiliser des variables de groupe de capture au lieu du segment :page
.
Disons que vous avez la même structure de fichiers que ci-dessus, mais que vous ne souhaitez pas que le chemin baz
soit imbriqué sous /bar
. Vous pouvez définir pages "/$2", from: "priv/pages/**/*.md"
, en utilisant $2
au lieu de :page
. Cela créera deux routes : get "/foo"
et get "/bar"
.
Les variables du groupe de capture contiendront la valeur des morceaux **
et *
dans l'ordre, en commençant à $1
. Gardez à l'esprit que **
correspondra à tous les fichiers et à zéro ou plusieurs répertoires et sous-répertoires, et *
correspondra à n'importe quel nombre de caractères jusqu'à la fin du nom de fichier, le point suivant ou la barre oblique suivante.
Pour plus d'informations sur les modèles de caractères génériques, consultez Path.wildcard/2.
En plus des options de démarque personnalisables, le rendu de démarque prend également en charge les attributs IAL par défaut. Cela signifie que vous pouvez ajouter des attributs HTML à n'importe quel élément de niveau bloc en utilisant la syntaxe {:attr}
.
Par exemple, pour créer une sortie rendue de <h1 class="foobar">Header</h1>
:
# Header{:.foobar}
Les attributs peuvent être l'un des suivants :
{:#id}
pour définir un identifiant{:.className}
pour définir un nom de classe{:name=value}
, {:name="value"}
ou {:name='value'}
pour définir tout autre attribut Pour définir plusieurs attributs, séparez-les par des espaces : {:#id name=value}
.
Si vous ajoutez, supprimez ou modifiez des pages lors de l'exécution mix phx.server
, elles seront automatiquement remplacées dans le cache et vous n'aurez pas besoin de redémarrer pour qu'elles prennent effet. Pour recharger en direct lorsqu'une page change, ajoutez à la liste des modèles de la configuration Endpoint dans config/dev.exs
:
config :myapp , MyAppWeb.Endpoint ,
live_reload: [
patterns: [
# ...
~r " priv/pages/.*(md)$ " ,
# ...
]
]