reitit/frontend/controllers.html

927 lines
28 KiB
HTML
Raw Normal View History

<!DOCTYPE HTML>
<html lang="" >
<head>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Controllers · GitBook</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="">
<meta name="generator" content="GitBook 3.2.3">
<link rel="stylesheet" href="../gitbook/style.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-hints/plugin-hints.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-highlight/website.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-search/search.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-fontsettings/website.css">
<meta name="HandheldFriendly" content="true"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="../gitbook/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="../gitbook/images/favicon.ico" type="image/x-icon">
<link rel="next" href="../advanced/configuring_routers.html" />
<link rel="prev" href="browser.html" />
</head>
<body>
<div class="book">
<div class="book-summary">
<div id="book-search-input" role="search">
<input type="text" placeholder="Type to search" />
</div>
<nav role="navigation">
<ul class="summary">
<li class="header">Introduction</li>
<li class="chapter " data-level="1.1" data-path="../">
<a href="../">
Introduction
</a>
</li>
<li class="header">Basics</li>
<li class="chapter " data-level="2.1" data-path="../basics/route_syntax.html">
<a href="../basics/route_syntax.html">
Route Syntax
</a>
</li>
<li class="chapter " data-level="2.2" data-path="../basics/router.html">
<a href="../basics/router.html">
Router
</a>
</li>
<li class="chapter " data-level="2.3" data-path="../basics/path_based_routing.html">
<a href="../basics/path_based_routing.html">
Path-based Routing
</a>
</li>
<li class="chapter " data-level="2.4" data-path="../basics/name_based_routing.html">
<a href="../basics/name_based_routing.html">
Name-based Routing
</a>
</li>
<li class="chapter " data-level="2.5" data-path="../basics/route_data.html">
<a href="../basics/route_data.html">
Route Data
</a>
</li>
<li class="chapter " data-level="2.6" data-path="../basics/route_data_validation.html">
<a href="../basics/route_data_validation.html">
Route Data Validation
</a>
</li>
<li class="chapter " data-level="2.7" data-path="../basics/route_conflicts.html">
<a href="../basics/route_conflicts.html">
Route Conflicts
</a>
</li>
<li class="header">Coercion</li>
<li class="chapter " data-level="3.1" data-path="../coercion/coercion.html">
<a href="../coercion/coercion.html">
Coercion Explained
</a>
</li>
<li class="chapter " data-level="3.2" data-path="../coercion/schema_coercion.html">
<a href="../coercion/schema_coercion.html">
Plumatic Schema
</a>
</li>
<li class="chapter " data-level="3.3" data-path="../coercion/clojure_spec_coercion.html">
<a href="../coercion/clojure_spec_coercion.html">
Clojure.spec
</a>
</li>
<li class="chapter " data-level="3.4" data-path="../coercion/data_spec_coercion.html">
<a href="../coercion/data_spec_coercion.html">
Data-specs
</a>
</li>
<li class="header">Ring</li>
<li class="chapter " data-level="4.1" data-path="../ring/ring.html">
<a href="../ring/ring.html">
Ring-router
</a>
</li>
<li class="chapter " data-level="4.2" data-path="../ring/reverse_routing.html">
<a href="../ring/reverse_routing.html">
Reverse-routing
</a>
</li>
<li class="chapter " data-level="4.3" data-path="../ring/default_handler.html">
<a href="../ring/default_handler.html">
Default handler
</a>
</li>
<li class="chapter " data-level="4.4" data-path="../ring/slash_handler.html">
<a href="../ring/slash_handler.html">
Slash handler
</a>
</li>
<li class="chapter " data-level="4.5" data-path="../ring/static.html">
<a href="../ring/static.html">
Static Resources
</a>
</li>
<li class="chapter " data-level="4.6" data-path="../ring/dynamic_extensions.html">
<a href="../ring/dynamic_extensions.html">
Dynamic Extensions
</a>
</li>
<li class="chapter " data-level="4.7" data-path="../ring/data_driven_middleware.html">
<a href="../ring/data_driven_middleware.html">
Data-driven Middleware
</a>
</li>
<li class="chapter " data-level="4.8" data-path="../ring/transforming_middleware_chain.html">
<a href="../ring/transforming_middleware_chain.html">
Transforming Middleware Chain
</a>
</li>
<li class="chapter " data-level="4.9" data-path="../ring/middleware_registry.html">
<a href="../ring/middleware_registry.html">
Middleware Registry
</a>
</li>
<li class="chapter " data-level="4.10" data-path="../ring/default_middleware.html">
<a href="../ring/default_middleware.html">
Default Middleware
</a>
</li>
<li class="chapter " data-level="4.11" data-path="../ring/coercion.html">
<a href="../ring/coercion.html">
Pluggable Coercion
</a>
</li>
<li class="chapter " data-level="4.12" data-path="../ring/route_data_validation.html">
<a href="../ring/route_data_validation.html">
Route Data Validation
</a>
</li>
<li class="chapter " data-level="4.13" data-path="../ring/compiling_middleware.html">
<a href="../ring/compiling_middleware.html">
Compiling Middleware
</a>
</li>
<li class="chapter " data-level="4.14" data-path="../ring/swagger.html">
<a href="../ring/swagger.html">
Swagger Support
</a>
</li>
<li class="header">HTTP</li>
<li class="chapter " data-level="5.1" data-path="../http/interceptors.html">
<a href="../http/interceptors.html">
Interceptors
</a>
</li>
<li class="chapter " data-level="5.2" data-path="../http/pedestal.html">
<a href="../http/pedestal.html">
Pedestal
</a>
</li>
<li class="chapter " data-level="5.3" data-path="../http/sieppari.html">
<a href="../http/sieppari.html">
Sieppari
</a>
</li>
<li class="chapter " data-level="5.4" data-path="../http/default_interceptors.html">
<a href="../http/default_interceptors.html">
Default Interceptors
</a>
</li>
<li class="header">Frontend</li>
<li class="chapter " data-level="6.1" data-path="basics.html">
<a href="basics.html">
Basics
</a>
</li>
<li class="chapter " data-level="6.2" data-path="browser.html">
<a href="browser.html">
Browser integration
</a>
</li>
<li class="chapter active" data-level="6.3" data-path="controllers.html">
<a href="controllers.html">
Controllers
</a>
</li>
<li class="header">Advanced</li>
<li class="chapter " data-level="7.1" data-path="../advanced/configuring_routers.html">
<a href="../advanced/configuring_routers.html">
Configuring Routers
</a>
</li>
<li class="chapter " data-level="7.2" data-path="../advanced/composing_routers.html">
<a href="../advanced/composing_routers.html">
Composing Routers
</a>
</li>
<li class="chapter " data-level="7.3" data-path="../advanced/different_routers.html">
<a href="../advanced/different_routers.html">
Different Routers
</a>
</li>
<li class="chapter " data-level="7.4" data-path="../advanced/route_validation.html">
<a href="../advanced/route_validation.html">
Route Validation
</a>
</li>
<li class="chapter " data-level="7.5" data-path="../advanced/dev_workflow.html">
<a href="../advanced/dev_workflow.html">
Dev Workflow
</a>
</li>
<li class="chapter " data-level="7.6" data-path="../advanced/shared_routes.html">
<a href="../advanced/shared_routes.html">
Shared Routes
</a>
</li>
<li class="header">Misc</li>
<li class="chapter " data-level="8.1" data-path="../performance.html">
<a href="../performance.html">
Performance
</a>
</li>
<li class="chapter " data-level="8.2" data-path="../development.html">
<a href="../development.html">
Development Instructions
</a>
</li>
<li class="chapter " data-level="8.3" data-path="../faq.html">
<a href="../faq.html">
FAQ
</a>
</li>
<li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<div class="book-header" role="navigation">
<!-- Title -->
<h1>
<i class="fa fa-circle-o-notch fa-spin"></i>
<a href=".." >Controllers</a>
</h1>
</div>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<div id="book-search-results">
<div class="search-noresults">
<section class="normal markdown-section">
<h1 id="controllers">Controllers</h1>
<ul>
<li><a href="https://github.com/metosin/reitit/tree/master/examples/frontend-controllers" target="_blank">https://github.com/metosin/reitit/tree/master/examples/frontend-controllers</a></li>
</ul>
<p>Controllers run code when a route is entered and left. This can be useful to:</p>
<ul>
<li>Load resources</li>
<li>Update application state</li>
</ul>
<h2 id="how-controllers-work">How controllers work</h2>
<p>A controller consists of three functions:</p>
<ul>
<li><code>params</code> which takes a Match and returns an arbitrary value.</li>
<li><code>start</code> which takes the result of params and whose return value is discarded.</li>
<li><code>stop</code> which takes the result of params and whose return value is discarded.</li>
</ul>
<p>When you navigate to a route that has a controller, <code>params</code> gets called first
and then <code>start</code> is called with its return value. When you exit that route,
<code>stop</code> is called with the return value of <code>params.</code></p>
<p>If you navigate to the same route with different parameters, <code>params</code> gets
called again. If the return value changes from the previous return value, <code>stop</code>
and <code>start</code> get called again.</p>
<p>You can add controllers to a route by adding them to the route data in the
<code>:controllers</code> vector. For example:</p>
<pre><code class="lang-clojure">[<span class="hljs-string">&quot;/item/:id&quot;</span>
{<span class="hljs-symbol">:controllers</span> [{<span class="hljs-symbol">:params</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [match] (<span class="hljs-name"><span class="hljs-builtin-name">get-in</span></span> match [<span class="hljs-symbol">:path-params</span> <span class="hljs-symbol">:id</span>]))
<span class="hljs-symbol">:start</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [item-id] (<span class="hljs-name">js/console.log</span> <span class="hljs-symbol">:start</span> item-id))
<span class="hljs-symbol">:stop</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [item-id] (<span class="hljs-name">js/console.log</span> <span class="hljs-symbol">:stop</span> item-id))}]}]
</code></pre>
<p>If you leave out <code>params</code>, <code>start</code> and <code>stop</code> get called with <code>nil</code>. You can
leave out <code>start</code> or <code>stop</code> if you do not need both of them.</p>
<h2 id="enabling-controllers">Enabling controllers</h2>
<p>You need to
call
<a href="https://cljdoc.org/d/metosin/reitit-frontend/CURRENT/api/reitit.frontend.controllers#apply-controllers" target="_blank"><code>reitit.frontend.controllers/apply-controllers</code></a> whenever
the URL changes. You can call it from the <code>on-navigate</code> callback of
<code>reitit.frontend.easy</code>:</p>
<pre><code class="lang-clojure">
(<span class="hljs-name"><span class="hljs-builtin-name">ns</span></span> frontend.core
(<span class="hljs-symbol">:require</span> [reitit.frontend.easy <span class="hljs-symbol">:as</span> rfe]
[reitit.frontend.controllers <span class="hljs-symbol">:as</span> rfc]))
(<span class="hljs-name"><span class="hljs-builtin-name">defonce</span></span> match-a (<span class="hljs-name"><span class="hljs-builtin-name">atom</span></span> <span class="hljs-literal">nil</span>))
(<span class="hljs-name"><span class="hljs-builtin-name">def</span></span> routes
[<span class="hljs-string">&quot;/&quot;</span> ...])
(<span class="hljs-name"><span class="hljs-builtin-name">defn</span></span> init! []
(<span class="hljs-name">rfe/start!</span>
routes
(<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [new-match]
(<span class="hljs-name"><span class="hljs-builtin-name">swap!</span></span> match-a
(<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [old-match]
(<span class="hljs-name"><span class="hljs-builtin-name">when</span></span> new-match
(<span class="hljs-name"><span class="hljs-builtin-name">assoc</span></span> new-match
<span class="hljs-symbol">:controllers</span> (<span class="hljs-name">rfc/apply-controllers</span> (<span class="hljs-symbol">:controllers</span> old-match) new-match))))))))
</code></pre>
<p>See also <a href="https://github.com/metosin/reitit/tree/master/examples/frontend-controllers" target="_blank">the full example</a>.</p>
<h2 id="nested-controllers">Nested controllers</h2>
<p>When you nest routes in the route tree, the controllers get nested as well.
Consider this route tree:</p>
<pre><code class="lang-clojure">[<span class="hljs-string">&quot;/&quot;</span> {<span class="hljs-symbol">:controllers</span> [{<span class="hljs-symbol">:start</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [_] (<span class="hljs-name">js/console.log</span> <span class="hljs-string">&quot;root start&quot;</span>))}]}
[<span class="hljs-string">&quot;/item/:id&quot;</span>
{<span class="hljs-symbol">:controllers</span> [{<span class="hljs-symbol">:params</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [match] (<span class="hljs-name"><span class="hljs-builtin-name">get-in</span></span> match [<span class="hljs-symbol">:path-params</span> <span class="hljs-symbol">:id</span>]))
<span class="hljs-symbol">:start</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [item-id] (<span class="hljs-name">js/console.log</span> <span class="hljs-string">&quot;item start&quot;</span> item-id))
<span class="hljs-symbol">:stop</span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [item-id] (<span class="hljs-name">js/console.log</span> <span class="hljs-string">&quot;item stop&quot;</span> item-id))}]}]]
</code></pre>
<ul>
<li>When you navigate to any route at all, the root controller gets started.</li>
<li>If you navigate to <code>/item/something</code>, the root controller gets started first
and then the item controller gets started.</li>
<li>If you then navigate from <code>/item/something</code> to <code>/item/something-else</code>, first
the item controller gets stopped with parameter <code>something</code> and then it gets
started with the parameter <code>something-else</code>. The root controller stays on the
whole time since its parameters do not change.</li>
</ul>
<h2 id="authentication">Authentication</h2>
<p>Controllers can be used to load resources from a server. If and when your
API requires authentication you will need to implement logic to prevent controllers
trying to do requests if user isn&apos;t authenticated yet.</p>
<h3 id="run-controllers-and-check-authentication">Run controllers and check authentication</h3>
<p>If you have both unauthenticated and authenticated resources, you can
run the controllers always and then check the authentication status
on controller code, or on the code called from controllers (e.g. re-frame event
handler).</p>
<h3 id="disable-controllers-until-user-is-authenticated">Disable controllers until user is authenticated</h3>
<p>If all your resources require authentication an easy way to prevent bad
requests is to enable controllers only after authentication is done.
To do this you can check authentication status and call <code>apply-controllers</code>
only after authentication is done (also remember to manually call <code>apply-controllers</code>
with current <code>match</code> when authentication is done). Or if no navigation is possible
before authentication is done, you can start the router only after
authentication is done.</p>
<h2 id="alternatives">Alternatives</h2>
<p>Similar solution could be used to describe required resources as data (maybe
even GraphQL query) per route, and then have code automatically load
missing resources.</p>
<h2 id="controllers-elsewhere">Controllers elsewhere</h2>
<ul>
<li><a href="https://keechma.com/guides/controllers/" target="_blank">Controllers in Keechma</a></li>
</ul>
</section>
</div>
<div class="search-results">
<div class="has-results">
<h1 class="search-results-title"><span class='search-results-count'></span> results matching "<span class='search-query'></span>"</h1>
<ul class="search-results-list"></ul>
</div>
<div class="no-results">
<h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1>
</div>
</div>
</div>
</div>
</div>
</div>
<a href="browser.html" class="navigation navigation-prev " aria-label="Previous page: Browser integration">
<i class="fa fa-angle-left"></i>
</a>
<a href="../advanced/configuring_routers.html" class="navigation navigation-next " aria-label="Next page: Configuring Routers">
<i class="fa fa-angle-right"></i>
</a>
</div>
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Controllers","level":"6.3","depth":1,"next":{"title":"Configuring Routers","level":"7.1","depth":1,"path":"advanced/configuring_routers.md","ref":"advanced/configuring_routers.md","articles":[]},"previous":{"title":"Browser integration","level":"6.2","depth":1,"path":"frontend/browser.md","ref":"frontend/browser.md","articles":[]},"dir":"ltr"},"config":{"plugins":["hints","editlink","github","highlight"],"root":"doc","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"github":{"url":"https://github.com/metosin/reitit"},"editlink":{"label":"Edit This Page","multilingual":false,"base":"https://github.com/metosin/reitit/tree/master/doc"},"search":{},"hints":{"danger":"fa fa-exclamation-circle","info":"fa fa-info-circle","tip":"fa fa-mortar-board","working":"fa fa-wrench"},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"gitbook":"*"},"file":{"path":"frontend/controllers.md","mtime":"2018-12-27T14:01:51.070Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2018-12-27T14:02:13.115Z"},"basePath":"..","book":{"language":""}});
});
</script>
</div>
<script src="../gitbook/gitbook.js"></script>
<script src="../gitbook/theme.js"></script>
<script src="../gitbook/gitbook-plugin-editlink/plugin.js"></script>
<script src="../gitbook/gitbook-plugin-github/plugin.js"></script>
<script src="../gitbook/gitbook-plugin-search/search-engine.js"></script>
<script src="../gitbook/gitbook-plugin-search/search.js"></script>
<script src="../gitbook/gitbook-plugin-lunr/lunr.min.js"></script>
<script src="../gitbook/gitbook-plugin-lunr/search-lunr.js"></script>
<script src="../gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="../gitbook/gitbook-plugin-fontsettings/fontsettings.js"></script>
</body>
</html>