class AppRouter
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
UL do
LI { Link('/') { 'Home' } }
LI { Link('?url=https%3A%2F%2Fgithub.com%2Fabout') { 'About' } }
end
Route('/', exact: true, mounts: Home)
Route('?url=https%3A%2F%2Fgithub.com%2Fabout', mounts: About)
end
end
class Home
include Hyperstack::Component
render(DIV) do
H2 { 'Home' }
end
endThis is the Router module which you include in your top level component:
class MyRouter
include Hyperstack::Component
include Hyperstack::Router
endWith the base Router class, you can also specify the history you want to use.
This can be done either using a macro:
class MyRouter
include Hyperstack::Component
include Hyperstack::Router
history :browser # this is the default option if no other is specified
endThe macro accepts three options: :browser, :hash, or :memory.
Or by defining the history method:
class MyRouter
include Hyperstack::Component
include Hyperstack::Router
def history
self.class.browser_history
end
endUse the render macro as normal. Note you cannot redefine the render instance method in a Router componenent
class MyRouter
...
render(DIV) do
H1 { 'Hello world!' }
end
endRoutes are defined with special pseudo components you call inside the router/components. The router determines which of the routes to actually mount based on the current URL.
class MyRouter
...
render(DIV) do
Route('/', mounts: HelloWorld)
end
end
class HelloWorld
render do
H1 { 'Hello world!' }
end
endThe Route method takes a url path, and these options:
mounts: ComponentThe component you want to mount when routed toexact: BooleanWhen true, the path must match the location exactlystrict: BooleanWhen true, the path will only match if the location and path both have/don't have a trailing slash
The Route method can also take a block instead of the mounts option.
class MyRouter
...
render(DIV) do
Route('/', exact: true) do
H1 { 'Hello world!' }
end
end
endThe block will be given the match, location, and history data:
class MyRouter
...
render(DIV) do
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname') do |match, location, history|
H1 { "Hello #{match.params[:name]} from #{location.pathname}, click me to go back!" }
.on(:click) { history.go_back }
end
end
end-
The
Hyperstack::Router::Helpersis useful for components mounted by the router. -
This automatically sets the
match,location, andhistoryparams,and also gives you instance methods with those names.
-
You can use either
params.matchor justmatch.and gives you access to the
Routemethod and more. -
This allows you to create inner routes as you need them.
class MyRouter
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname', mounts: Greet)
end
end
class Greet
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H1 { "Hello #{match.params[:foo]}!" }
Route(match.url, exact: true) do
H2 { 'What would you like to do?' }
end
Route("#{match.url}/:activity", mounts: Activity)
end
end
class Activity
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
H2 { params.match.params[:activity] }
end
endNormally routes will always render alongside sibling routes that match as well.
class MyRouter
...
render(DIV) do
Route('?url=https%3A%2F%2Fgithub.com%2Fgoodbye', mounts: Goodbye)
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname', mounts: Greet)
end
endGoing to /goodbye would match /:name as well and render Greet with the name param with the value 'goodbye'. To avoid this behavior and only render one matching route at a time, use a Switch component.
class MyRouter
...
render(DIV) do
Switch do
Route('?url=https%3A%2F%2Fgithub.com%2Fgoodbye', mounts: Goodbye)
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname', mounts: Greet)
end
end
endNow, going to /goodbye would match the Goodbye route first and only render that component.
Links are provided by both the Hyperstack::Router and Hyperstack::Router::Helper modules.
The Link method takes a url path, and these options:
-
search: Stringadds the specified string to the search query -
hash: Stringadds the specified string to the hash locationit can also take a block of children to render inside it.
class MyRouter
...
render(DIV) do
Link('?url=https%3A%2F%2Fgithub.com%2FGregor+Clegane')
Route('/', exact: true) { H1() }
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname') do |match|
H1 { "Will #{match.params[:name]} eat all the chickens?" }
end
end
endNavLinks are the same as Links, but will add styling attributes when it matches the current url
active_class: Stringadds the class to the link when the url matchesactive_style: Stringadds the style to the link when the url matchesactive: ProcA proc that will add extra logic to determine if the link is active
class MyRouter
...
render(DIV) do
NavLink('?url=https%3A%2F%2Fgithub.com%2FGregor+Clegane', active_class: 'active-link')
NavLink('?url=https%3A%2F%2Fgithub.com%2FRodrik+Cassel', active_style: { color: 'grey' })
NavLink('?url=https%3A%2F%2Fgithub.com%2FOberyn+Martell',
active: ->(match, location) {
match && match.params[:name] && match.params[:name] =~ /Martell/
})
Route('/', exact: true) { H1() }
Route('?url=https%3A%2F%2Fgithub.com%2F%3Aname') do |match|
H1 { "Will #{match.params[:name]} eat all the chickens?" }
end
end
endPre-rendering is automatically taken care for you under the hood.
To setup HyperRouter:
- Install the gem
- Your page should render your router as its top-level-component (first component to be rendered on the page) - in the example below this would be
AppRouter - You will need to configure your server to route all unknown routes to the client-side router (Rails example below)
Assuming your router is called AppRouter, add the following to your routes.rb
root 'Hyperstack#AppRouter' # see note below
match '*all', to: 'Hyperstack#AppRouter', via: [:get] # this should be the last line of routes.rbNote:
root 'Hyperstack#AppRouter' is shorthand which will automagically create a Controller, View and launch AppRouter as the top-level Component. If you are rendering your Component via your own COntroller or View then ignore this line.
Here is the basic JSX example that is used on the react-router site
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="?url=https%3A%2F%2Fgithub.com%2Fabout">About</Link></li>
<li><Link to="?url=https%3A%2F%2Fgithub.com%2Ftopics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="?url=https%3A%2F%2Fgithub.com%2Fabout" component={About}/>
<Route path="?url=https%3A%2F%2Fgithub.com%2Ftopics" component={Topics}/>
</div>
</Router>
)
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
const About = () => (
<div>
<h2>About</h2>
</div>
)
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li><Link to={`${match.url}/rendering`}>Rendering with React</Link></li>
<li><Link to={`${match.url}/components`}>Components</Link></li>
<li><Link to={`${match.url}/props-v-state`}>Props v. State</Link></li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic}/>
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)}/>
</div>
)
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
export default BasicExampleAnd here is the same example in Hyperstack:
class BasicExample
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
UL do
LI { Link('/') { 'Home' } }
LI { Link('?url=https%3A%2F%2Fgithub.com%2Fabout') { 'About' } }
LI { Link('?url=https%3A%2F%2Fgithub.com%2Ftopics') { 'Topics' } }
end
Route('/', exact: true, mounts: Home)
Route('?url=https%3A%2F%2Fgithub.com%2Fabout', mounts: About)
Route('?url=https%3A%2F%2Fgithub.com%2Ftopics', mounts: Topics)
end
end
class Home
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H2 { 'Home' }
end
end
class About
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(:div) do
H2 { 'About' }
end
end
class Topics
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H2 { 'Topics' }
UL() do
LI { Link("#{match.url}/rendering") { 'Rendering with React' } }
LI { Link("#{match.url}/components") { 'Components' } }
LI { Link("#{match.url}/props-v-state") { 'Props v. State' } }
end
Route("#{match.url}/:topic_id", mounts: Topic)
Route(match.url, exact: true) do
H3 { 'Please select a topic.' }
end
end
end
class Topic
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(:div) do
H3 { match.params[:topic_id] }
end
end