red-tape/form-arguments/index.html @ 54ebe659726f
chancery: Update site.
| author | Steve Losh <steve@stevelosh.com> |
|---|---|
| date | Fri, 03 Nov 2017 21:12:45 -0400 |
| parents | 69831b3947d7 |
| children | (none) |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Form Arguments / Red Tape</title> <link rel="stylesheet" href="../_dmedia/tango.css"/> <link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/> <script src="../_dmedia/less.js" type="text/javascript"> </script> </head> <body class="content"> <div class="wrap"> <header><h1><a href="..">Red Tape</a></h1></header> <div class="markdown"> <h1 id="form-arguments"><a href="">Form Arguments</a></h1><p>Sometimes a simple form isn't enough. When you need dynamic functionality in your forms you can use Red Tape's form arguments.</p> <p>Once you've gone through this document, read the <a href="../rendering/">rendering</a> guide.</p> <div class="toc"> <ul> <li><a href="#the-problem">The Problem</a></li> <li><a href="#defining-form-arguments">Defining Form Arguments</a></li> <li><a href="#using-form-arguments">Using Form Arguments</a></li> <li><a href="#initial-data">Initial Data</a></li> </ul></div> <h2 id="the-problem">The Problem</h2> <p>Let's use a simple example to demonstrate why form arguments are necessary and how they work. Suppose you have a site that hosts videos. You'd like to have a "delete video" form to let users delete their videos. A first crack at it might look like this:</p> <div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">video-exists</span> <span class="p">[</span><span class="nv">video-id</span><span class="p">]</span> <span class="c1">; Checks that the given video id exists...</span> <span class="p">)</span> <span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{}</span> <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span><span class="p">])</span> </pre></div> <p>Nothing new here. But this form will let <em>anyone</em> delete <em>any</em> video. In real life you probably only want users to be able to delete their <em>own</em> videos.</p> <h2 id="defining-form-arguments">Defining Form Arguments</h2> <p>To define form arguments you can use the <code>:arguments</code> entry in the form options map, passing a vector of symbols. Let's change our <code>delete-video-form</code> form:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]}</span> <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span><span class="p">])</span> </pre></div> <p>The form will now take a <code>user</code> argument. So calling the form <em>without</em> data would be done like this:</p> <div class="codehilite"><pre><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> <span class="nv">fresh-delete-form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span><span class="p">)])</span> </pre></div> <p>And calling it <em>with</em> data would look like this:</p> <div class="codehilite"><pre><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> <span class="nv">post-data</span> <span class="nv">...</span> <span class="nv">form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span> <span class="nv">post-data</span><span class="p">)])</span> </pre></div> <h2 id="using-form-arguments">Using Form Arguments</h2> <p>Using form arguments relies on one key idea: your cleaner definitions are evaluated in a context where the form arguments are bound to the symbols you specified.</p> <p>Here's how we could use our <code>user</code> argument:</p> <div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">video-owned-by</span> <span class="p">[</span><span class="nv">user</span> <span class="nv">video-id</span><span class="p">]</span> <span class="c1">; Check that the given user owns the given video id.</span> <span class="p">)</span> <span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]}</span> <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span> <span class="o">#</span><span class="p">(</span><span class="nf">video-owned-by</span> <span class="nv">user</span> <span class="nv">%</span><span class="p">)])</span> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> <span class="nv">post-data</span> <span class="nv">...</span> <span class="nv">form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span> <span class="nv">post-data</span><span class="p">)]</span> <span class="nv">...</span><span class="p">)</span> </pre></div> <p>Notice how the second cleaner in the <code>defform</code> is defined as <code>#(video-owned-by user %)</code>. <code>user</code> here is the form argument, so the anonymouse function will be created with the appropriate user each time <code>delete-form</code> is called.</p> <h2 id="initial-data">Initial Data</h2> <p>The initial data map is also evaluated in a context where the form arguments are bound. You can use this to define default values based on the form arguments.</p> <p>For example: suppose you have a profile page where users can change their email address and profile. A form for this might look like:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">profile-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]</span> <span class="ss">:initial</span> <span class="p">{</span><span class="ss">:email</span> <span class="p">(</span><span class="ss">:email</span> <span class="nv">user</span><span class="p">)</span> <span class="ss">:bio</span> <span class="p">(</span><span class="ss">:bio</span> <span class="nv">user</span><span class="p">)}}</span> <span class="ss">:email</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">"\S+@\S+"</span> <span class="nv">%</span> <span class="s">"Please enter a valid email address."</span><span class="p">)]</span> <span class="ss">:bio</span> <span class="p">[])</span> <span class="p">(</span><span class="nf">profile-form</span> <span class="nv">some-user</span><span class="p">)</span> <span class="c1">; =></span> <span class="p">{</span><span class="nv">...</span> <span class="ss">:results</span> <span class="nv">nil</span> <span class="ss">:errors</span> <span class="nv">nil</span> <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span> <span class="ss">:bio</span> <span class="s">"Computers are terrible."</span><span class="p">}}</span> <span class="p">(</span><span class="nf">profile-form</span> <span class="nv">some-user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"this is not an email"</span> <span class="ss">:bio</span> <span class="s">"I like cats."</span><span class="p">})</span> <span class="c1">; =></span> <span class="p">{</span><span class="nv">...</span> <span class="ss">:results</span> <span class="nv">nil</span> <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"Please enter a valid email address"</span><span class="p">}</span> <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"this is not an email"</span> <span class="ss">:bio</span> <span class="s">"I like cats."</span><span class="p">}}</span> </pre></div> <p>Notice how in the fresh form the <code>:data</code> is prepopulated with the user's information that was pulled from the form argument. Once the user actually enters some data, the initial data is ignored.</p> </div> <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve Losh</a>.</p> <p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer> </div> </body> </html>