1.1.0 • Published 7 months ago

@ramstack/alpinegear-router v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

@ramstack/alpinegear-router

NPM MIT

@ramstack/alpinegear-router is a plugin for Alpine.js that provides routing-related directives for Alpine.js, enabling client-side navigation and routing functionality.

Installation

Using CDN

To include the CDN version of this plugin, add the following <script> tag before the core alpine.js file:

<!-- alpine.js plugin -->
<script src="https://cdn.jsdelivr.net/npm/@ramstack/alpinegear-router@1/alpinegear-router.min.js" defer></script>

<!-- alpine.js -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>

Using NPM

Alternatively, you can install the plugin via npm:

npm install --save @ramstack/alpinegear-router

Then, initialize it in your project:

import Alpine from "alpinejs";
import router from "@ramstack/alpinegear-router";

Alpine.plugin(router);
Alpine.start();

Usage

<div x-data x-router>
  <h1>Hello World!</h1>

  <div>
    <b>Current route:</b>
    <pre x-format>{{ JSON.stringify($route, null, 2) }}</pre>
  </div>

  <!-- Inline template -->
  <template x-route="/">
    Home page
  </template>

  <!-- External template -->
  <template x-route="/about" x-route:view="/views/about.html"></template>

  <nav>
    <a x-router:link href="/">Home</a>
    <a x-router:link.replace href="/about">About</a>
  </nav>

  <!-- Render the matching route -->
  <main x-router:outlet></main>
</div>

History modes

The router can be configured to use different history modes. There are two available modes:

  • html5: The default history mode.
  • hash: Uses a hash # in the URL to manage history.

HTML5 mode

The html5 mode uses the browser's history API to manage navigation.

<div x-data x-router:html5>
  ...
</div>

Since this mode is the default, there is no need to specify it explicitly:

<!-- HTML5 mode is used by default -->
<div x-data x-router>
  ...
</div>

Hash mode

This mode uses a hash # in the URL to handle navigation. Unlike html5 mode, it requires no special server-side configuration since the browser does not send the hash portion of the URL to the server.

<div x-data x-router:hash>
  ...
</div>

Route directive

Routes are defined using a <template> element with the x-route attribute.

<div x-router>
  <template x-route="/">
    Home page
  </template>

  <template x-route="/profile/{username}">
    Profile
  </template>

  <!-- Render the matching route -->
  <div x-router:outlet></div>
</div>

In this example, two routes are defined: a static / route representing the homepage, and a dynamic (parameterized) route /profile/{username}, where username is a route parameter enclosed in curly brackets.

This means that URLs like /profile/john and /profile/samantha will both match the same route. When a route is matched, the username parameter can be accessed via $route.params.username.

For more details on dynamic (parameterized) routes, refer to the corresponding section below.

Outlet directive

The x-router:outlet directive is used to render the matching route's content and can be placed anywhere within an x-router element. If no route matches, x-router:outlet will render nothing.

<div x-router>
  <template x-route="/">
    Home page
  </template>

  <template x-route="/profile/{username}">
    Profile
  </template>

  <!-- Render the matching route -->
  <div x-router:outlet></div>
</div>

Link directive

<div x-router:hash>
  ...
  <nav>
    <a x-router:link href="/">Home</a>
    <a x-router:link href="/about">About</a>
  </nav>
</div>

The router does not automatically intercept all links. Only links with the x-router:link directive inside an x-router container are handled by the router. Links should always be specified normally, regardless of the selected history mode.

Applying x-router:link to parent elements

You can also apply x-router:link to parent elements, such as <li>, to enable more flexible markup structures:

<li x-router:link :class="{ active: $active }">
   <a href="/about">About</a>
</li>

In this case:

  • x-router:link will locate the nested <a> element within the parent and use its href for routing.
  • The $active state will correctly reflect whether the nested link's href matches the current route.

Modifier replace

The replace modifier changes the default navigation behavior of the router. When a link with this modifier is clicked, it triggers $router.navigate(href, /* replace */ true).

Unlike the default behavior, which adds a new entry to the browser's history stack, this option replaces the current history entry with the new URL. As a result, the user's navigation history remains unchanged, and pressing the "Back" button will skip over the replaced entry.

<div x-router:hash>
  ...
  <nav>
    <a x-router:link href="/">Home</a>
      <!-- The "replace" modifier ensures that clicking this link
           replaces the current history entry instead of adding a new one -->
    <a x-router:link.replace href="/about">About</a>
  </nav>
</div>

Inline and External templates

Routes can be defined using either inline templates or external templates:

Inline templates: The content of the route is directly written inside the <template> element.

<template x-route="/">
  Home page
</template>

External templates: The content of the route is loaded from an external HTML file specified in the x-route:view directive.

<template x-route="/about" x-route:view="/views/about.html"></template>

Magic functions

Magic $route

Indicates the current active route and contains the following properties:

<dl x-format>
  <dt>Route pattern:</dt>
  <dd>{{ $route.pattern }}</dd>

  <dt>Route path:</dt>
  <dd>{{ $route.path }}</dd>

  <dt>Route params:</dt>
  <dd>{{ $route.params.username }}</dd>
</dl>

Magic $active

Returns true or false, indicating whether a x-router:link corresponds to the active route.

<nav>
  <a x-router:link href="/" class="{ active: $active }">Home</a>
  <a x-router:link href="/about" class="{ active: $active }">About</a>
</nav>

Applying active class to parent element

<li x-router:link :class="{ active: $active }">
   <a href="/about">About</a>
</li>

The $active state will correctly reflect whether the nested link's href matches the current route.

Route templates

Routes can contain parameters enclosed in {} that are dynamically bound when a route is matched. Multiple parameters can be used within a route segment, but they must be separated by a static value. For example:

{controller}{action}

is invalid because {controller} and {action} are not separated by a static value. Instead, valid routes should use static separators:

{controller}/{action}
article/{id}-{title}

Route parameters

Route parameters must have a name and can also include additional attributes. Literal values and path separators (/) must match exactly in the URL. Matching is case-insensitive and based on the decoded URL representation.

Optional and Catch-All Parameters

  • Optional parameters: Defined with ?, meaning they are not required. Example: {id?}
  • Catch-all parameters (*): Capture the remaining part of the URL. Example: blog/{slug*}
    • Matches any URL starting with blog/.
    • The remaining value is assigned to the slug parameter.
  • Required catch-all parameters (+): Like *, but at least one segment must match.

Parameter constraints

Route parameters can have constraints to validate and transform values. Constraints are added using : after the parameter name. If a constraint requires arguments, they are placed in parentheses.

Example:

blog/{id:int:min(100)}/{article:minlength(10)}
  • {id:int:min(100)} ensures id is an integer and at least 100.
  • {article:minlength(10)} requires article to be at least 10 characters long.

Some constraints also transform parameters. For example, {id:int} automatically converts id to a number.

Default values

Route parameters can have default values, specified similarly to constraints but using = or default. Example:

{controller:=(home)}

is equivalent to:

{controller:default(home)}

If no value is provided in the URL, the default is used.

Example route patterns

Route templateURIDescription
hello/helloMatches only /hello.
{controller}/{action}/{id?}/product/listMatches and sets controller=product, action=list.
{controller}/{action}/{id?}/product/list/1Matches and sets controller=product, action=list, id=1.
{controller:=(home)}/{action:=(index)}/{id?}/Matches and sets controller=home, action=index.
{controller:=(home)}/{action:=(index)}/{id?}/productMatches and sets controller=product, action=index.
blog/{slug*:=(start/with/new/blog)}/blogMatches and sets slug=[start,with,new,blog].

Route constraints

ConstraintExampleDescription
int{id:int}Matches an integer.
bool{id:bool}Matches true or false (case-insensitive).
number{speed:number}Matches a valid number.
alpha{name:alpha}Matches alphabetic characters (case-insensitive).
min(value){age:min(25)}Ensures the value is at least 25.
max(value){age:max(125)}Ensures the value is no more than 125.
range(min,max){age:range(25,125)}Ensures the value is between 25 and 125.
length(value){file:length(10)}Requires exactly 10 characters.
length(min,max){file:length(10,20)}Requires between 10 and 20 characters.
minlength(value){name:minlength(10)}Requires at least 10 characters.
maxlength(value){name:maxlength(50)}Requires no more than 50 characters.
regex(expr){id:regex(^\\d{3}-\\d{5}$)}Must match a specific regex pattern.

Source Code

You can find the source code for this plugin on GitHub:

https://github.com/rameel/ramstack.alpinegear.js/tree/main/src/plugins/router

Contributions

Bug reports and contributions are welcome.

License

This package is released as open source under the MIT License.

See the LICENSE file for more details.

1.1.0

7 months ago

1.1.0-preview.6

7 months ago

1.1.0-preview.5

8 months ago

1.1.0-preview.4

8 months ago

1.1.0-preview.3

8 months ago

1.1.0-preview.2

8 months ago

1.1.0-preview.1

8 months ago