1 | package Mojolicious::Plugin::TagHelpers;
|
---|
2 | use Mojo::Base 'Mojolicious::Plugin';
|
---|
3 |
|
---|
4 | use Mojo::ByteStream;
|
---|
5 | use Mojo::DOM::HTML 'tag_to_html';
|
---|
6 | use Scalar::Util 'blessed';
|
---|
7 |
|
---|
8 | sub register {
|
---|
9 | my ($self, $app) = @_;
|
---|
10 |
|
---|
11 | # Text field variations
|
---|
12 | my @time = qw(date month time week);
|
---|
13 | for my $name (@time, qw(color email number range search tel text url)) {
|
---|
14 | $app->helper("${name}_field" => sub { _input(@_, type => $name) });
|
---|
15 | }
|
---|
16 | $app->helper(datetime_field => sub { _input(@_, type => 'datetime-local') });
|
---|
17 |
|
---|
18 | my @helpers = (
|
---|
19 | qw(csrf_field form_for hidden_field javascript label_for link_to),
|
---|
20 | qw(select_field stylesheet submit_button tag_with_error text_area)
|
---|
21 | );
|
---|
22 | $app->helper($_ => __PACKAGE__->can("_$_")) for @helpers;
|
---|
23 |
|
---|
24 | $app->helper(button_to => sub { _button_to(0, @_) });
|
---|
25 | $app->helper(check_box => sub { _input(@_, type => 'checkbox') });
|
---|
26 | $app->helper(csrf_button_to => sub { _button_to(1, @_) });
|
---|
27 | $app->helper(file_field => sub { _empty_field('file', @_) });
|
---|
28 | $app->helper(image => sub { _tag('img', src => shift->url_for(shift), @_) });
|
---|
29 | $app->helper(input_tag => sub { _input(@_) });
|
---|
30 | $app->helper(password_field => sub { _empty_field('password', @_) });
|
---|
31 | $app->helper(radio_button => sub { _input(@_, type => 'radio') });
|
---|
32 |
|
---|
33 | # "t" is just a shortcut for the "tag" helper
|
---|
34 | $app->helper($_ => sub { shift; _tag(@_) }) for qw(t tag);
|
---|
35 | }
|
---|
36 |
|
---|
37 | sub _button_to {
|
---|
38 | my ($csrf, $c, $text) = (shift, shift, shift);
|
---|
39 | my $prefix = $csrf ? _csrf_field($c) : '';
|
---|
40 | return _form_for($c, @_, sub { $prefix . _submit_button($c, $text) });
|
---|
41 | }
|
---|
42 |
|
---|
43 | sub _csrf_field {
|
---|
44 | my $c = shift;
|
---|
45 | return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_);
|
---|
46 | }
|
---|
47 |
|
---|
48 | sub _empty_field {
|
---|
49 | my ($type, $c, $name) = (shift, shift, shift);
|
---|
50 | return _validation($c, $name, 'input', name => $name, @_, type => $type);
|
---|
51 | }
|
---|
52 |
|
---|
53 | sub _form_for {
|
---|
54 | my ($c, @url) = (shift, shift);
|
---|
55 | push @url, shift if ref $_[0] eq 'HASH';
|
---|
56 |
|
---|
57 | # Method detection
|
---|
58 | my $r = $c->app->routes->lookup($url[0]);
|
---|
59 | my $method = $r ? $r->suggested_method : 'GET';
|
---|
60 | my @post = $method ne 'GET' ? (method => 'POST') : ();
|
---|
61 |
|
---|
62 | my $url = $c->url_for(@url);
|
---|
63 | $url->query({_method => $method}) if @post && $method ne 'POST';
|
---|
64 | return _tag('form', action => $url, @post, @_);
|
---|
65 | }
|
---|
66 |
|
---|
67 | sub _hidden_field {
|
---|
68 | my ($c, $name, $value) = (shift, shift, shift);
|
---|
69 | return _tag('input', name => $name, value => $value, @_, type => 'hidden');
|
---|
70 | }
|
---|
71 |
|
---|
72 | sub _input {
|
---|
73 | my ($c, $name) = (shift, shift);
|
---|
74 | my %attrs = @_ % 2 ? (value => shift, @_) : @_;
|
---|
75 |
|
---|
76 | if (my @values = @{$c->every_param($name)}) {
|
---|
77 |
|
---|
78 | # Checkbox or radiobutton
|
---|
79 | my $type = $attrs{type} || '';
|
---|
80 | if ($type eq 'checkbox' || $type eq 'radio') {
|
---|
81 | my $value = $attrs{value} // 'on';
|
---|
82 | delete $attrs{checked};
|
---|
83 | $attrs{checked} = undef if grep { $_ eq $value } @values;
|
---|
84 | }
|
---|
85 |
|
---|
86 | # Others
|
---|
87 | else { $attrs{value} = $values[-1] }
|
---|
88 | }
|
---|
89 |
|
---|
90 | return _validation($c, $name, 'input', name => $name, %attrs);
|
---|
91 | }
|
---|
92 |
|
---|
93 | sub _javascript {
|
---|
94 | my $c = shift;
|
---|
95 | my $content
|
---|
96 | = ref $_[-1] eq 'CODE' ? "//<![CDATA[\n" . pop->() . "\n//]]>" : '';
|
---|
97 | my @src = @_ % 2 ? (src => $c->url_for(shift)) : ();
|
---|
98 | return _tag('script', @src, @_, sub {$content});
|
---|
99 | }
|
---|
100 |
|
---|
101 | sub _label_for {
|
---|
102 | my ($c, $name) = (shift, shift);
|
---|
103 | my $content = ref $_[-1] eq 'CODE' ? pop : shift;
|
---|
104 | return _validation($c, $name, 'label', for => $name, @_, $content);
|
---|
105 | }
|
---|
106 |
|
---|
107 | sub _link_to {
|
---|
108 | my ($c, $content) = (shift, shift);
|
---|
109 | my @url = ($content);
|
---|
110 |
|
---|
111 | # Content
|
---|
112 | unless (ref $_[-1] eq 'CODE') {
|
---|
113 | @url = (shift);
|
---|
114 | push @_, $content;
|
---|
115 | }
|
---|
116 |
|
---|
117 | # Captures
|
---|
118 | push @url, shift if ref $_[0] eq 'HASH';
|
---|
119 |
|
---|
120 | return _tag('a', href => $c->url_for(@url), @_);
|
---|
121 | }
|
---|
122 |
|
---|
123 | sub _option {
|
---|
124 | my ($values, $pair) = @_;
|
---|
125 |
|
---|
126 | $pair = [$pair => $pair] unless ref $pair eq 'ARRAY';
|
---|
127 | my %attrs = (value => $pair->[1], @$pair[2 .. $#$pair]);
|
---|
128 | delete $attrs{selected} if keys %$values;
|
---|
129 | $attrs{selected} = undef if $values->{$pair->[1]};
|
---|
130 |
|
---|
131 | return _tag('option', %attrs, $pair->[0]);
|
---|
132 | }
|
---|
133 |
|
---|
134 | sub _select_field {
|
---|
135 | my ($c, $name, $options, %attrs) = (shift, shift, shift, @_);
|
---|
136 |
|
---|
137 | my %values = map { $_ => 1 } @{$c->every_param($name)};
|
---|
138 |
|
---|
139 | my $groups = '';
|
---|
140 | for my $group (@$options) {
|
---|
141 |
|
---|
142 | # "optgroup" tag
|
---|
143 | if (blessed $group && $group->isa('Mojo::Collection')) {
|
---|
144 | my ($label, $values, %attrs) = @$group;
|
---|
145 | my $content = join '', map { _option(\%values, $_) } @$values;
|
---|
146 | $groups .= _tag('optgroup', label => $label, %attrs, sub {$content});
|
---|
147 | }
|
---|
148 |
|
---|
149 | # "option" tag
|
---|
150 | else { $groups .= _option(\%values, $group) }
|
---|
151 | }
|
---|
152 |
|
---|
153 | return _validation($c, $name, 'select', name => $name, %attrs, sub {$groups});
|
---|
154 | }
|
---|
155 |
|
---|
156 | sub _stylesheet {
|
---|
157 | my $c = shift;
|
---|
158 | my $content
|
---|
159 | = ref $_[-1] eq 'CODE' ? "/*<![CDATA[*/\n" . pop->() . "\n/*]]>*/" : '';
|
---|
160 | return _tag('style', @_, sub {$content}) unless @_ % 2;
|
---|
161 | return _tag('link', rel => 'stylesheet', href => $c->url_for(shift), @_);
|
---|
162 | }
|
---|
163 |
|
---|
164 | sub _submit_button {
|
---|
165 | my ($c, $value) = (shift, shift // 'Ok');
|
---|
166 | return _tag('input', value => $value, @_, type => 'submit');
|
---|
167 | }
|
---|
168 |
|
---|
169 | sub _tag { Mojo::ByteStream->new(tag_to_html(@_)) }
|
---|
170 |
|
---|
171 | sub _tag_with_error {
|
---|
172 | my ($c, $tag) = (shift, shift);
|
---|
173 | my ($content, %attrs) = (@_ % 2 ? pop : undef, @_);
|
---|
174 | $attrs{class} .= $attrs{class} ? ' field-with-error' : 'field-with-error';
|
---|
175 | return _tag($tag, %attrs, defined $content ? $content : ());
|
---|
176 | }
|
---|
177 |
|
---|
178 | sub _text_area {
|
---|
179 | my ($c, $name) = (shift, shift);
|
---|
180 |
|
---|
181 | my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
|
---|
182 | my $content = @_ % 2 ? shift : undef;
|
---|
183 | $content = $c->param($name) // $content // $cb // '';
|
---|
184 |
|
---|
185 | return _validation($c, $name, 'textarea', name => $name, @_, $content);
|
---|
186 | }
|
---|
187 |
|
---|
188 | sub _validation {
|
---|
189 | my ($c, $name) = (shift, shift);
|
---|
190 | return _tag(@_) unless $c->validation->has_error($name);
|
---|
191 | return $c->helpers->tag_with_error(@_);
|
---|
192 | }
|
---|
193 |
|
---|
194 | 1;
|
---|
195 |
|
---|
196 | =encoding utf8
|
---|
197 |
|
---|
198 | =head1 NAME
|
---|
199 |
|
---|
200 | Mojolicious::Plugin::TagHelpers - Tag helpers plugin
|
---|
201 |
|
---|
202 | =head1 SYNOPSIS
|
---|
203 |
|
---|
204 | # Mojolicious
|
---|
205 | $app->plugin('TagHelpers');
|
---|
206 |
|
---|
207 | # Mojolicious::Lite
|
---|
208 | plugin 'TagHelpers';
|
---|
209 |
|
---|
210 | =head1 DESCRIPTION
|
---|
211 |
|
---|
212 | L<Mojolicious::Plugin::TagHelpers> is a collection of HTML tag helpers for
|
---|
213 | L<Mojolicious>, based on the
|
---|
214 | L<HTML Living Standard|https://html.spec.whatwg.org>.
|
---|
215 |
|
---|
216 | Most form helpers can automatically pick up previous input values and will show
|
---|
217 | them as default. You can also use
|
---|
218 | L<Mojolicious::Plugin::DefaultHelpers/"param"> to set them manually and let
|
---|
219 | necessary attributes always be generated automatically.
|
---|
220 |
|
---|
221 | % param country => 'germany' unless param 'country';
|
---|
222 | <%= radio_button country => 'germany' %> Germany
|
---|
223 | <%= radio_button country => 'france' %> France
|
---|
224 | <%= radio_button country => 'uk' %> UK
|
---|
225 |
|
---|
226 | For fields that failed validation with L<Mojolicious::Controller/"validation">
|
---|
227 | the C<field-with-error> class will be automatically added through
|
---|
228 | L</"tag_with_error">, to make styling with CSS easier.
|
---|
229 |
|
---|
230 | <input class="field-with-error" name="age" type="text" value="250">
|
---|
231 |
|
---|
232 | This is a core plugin, that means it is always enabled and its code a good
|
---|
233 | example for learning how to build new plugins, you're welcome to fork it.
|
---|
234 |
|
---|
235 | See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
|
---|
236 | by default.
|
---|
237 |
|
---|
238 | =head1 HELPERS
|
---|
239 |
|
---|
240 | L<Mojolicious::Plugin::TagHelpers> implements the following helpers.
|
---|
241 |
|
---|
242 | =head2 button_to
|
---|
243 |
|
---|
244 | %= button_to Test => 'some_get_route'
|
---|
245 | %= button_to Test => some_get_route => {id => 23} => (class => 'menu')
|
---|
246 | %= button_to Test => 'http://example.com/test' => (class => 'menu')
|
---|
247 | %= button_to Remove => 'some_delete_route'
|
---|
248 |
|
---|
249 | Generate portable C<form> tag with L</"form_for">, containing a single button.
|
---|
250 |
|
---|
251 | <form action="/path/to/get/route">
|
---|
252 | <input type="submit" value="Test">
|
---|
253 | </form>
|
---|
254 | <form action="/path/to/get/route/23" class="menu">
|
---|
255 | <input type="submit" value="Test">
|
---|
256 | </form>
|
---|
257 | <form action="http://example.com/test" class="menu">
|
---|
258 | <input type="submit" value="Test">
|
---|
259 | </form>
|
---|
260 | <form action="/path/to/delete/route?_method=DELETE" method="POST">
|
---|
261 | <input type="submit" value="Remove">
|
---|
262 | </form>
|
---|
263 |
|
---|
264 | =head2 check_box
|
---|
265 |
|
---|
266 | %= check_box 'employed'
|
---|
267 | %= check_box employed => 1
|
---|
268 | %= check_box employed => 1, checked => undef, id => 'foo'
|
---|
269 |
|
---|
270 | Generate C<input> tag of type C<checkbox>. Previous input values will
|
---|
271 | automatically get picked up and shown as default.
|
---|
272 |
|
---|
273 | <input name="employed" type="checkbox">
|
---|
274 | <input name="employed" type="checkbox" value="1">
|
---|
275 | <input checked id="foo" name="employed" type="checkbox" value="1">
|
---|
276 |
|
---|
277 | =head2 color_field
|
---|
278 |
|
---|
279 | %= color_field 'background'
|
---|
280 | %= color_field background => '#ffffff'
|
---|
281 | %= color_field background => '#ffffff', id => 'foo'
|
---|
282 |
|
---|
283 | Generate C<input> tag of type C<color>. Previous input values will
|
---|
284 | automatically get picked up and shown as default.
|
---|
285 |
|
---|
286 | <input name="background" type="color">
|
---|
287 | <input name="background" type="color" value="#ffffff">
|
---|
288 | <input id="foo" name="background" type="color" value="#ffffff">
|
---|
289 |
|
---|
290 | =head2 csrf_button_to
|
---|
291 |
|
---|
292 | %= csrf_button_to Remove => 'some_delete_route'
|
---|
293 |
|
---|
294 | Same as L</"button_to">, but also includes a L</"csrf_field">.
|
---|
295 |
|
---|
296 | <form action="/path/to/delete/route?_method=DELETE" method="POST">
|
---|
297 | <input name="csrf_token" type="hidden" value="fa6a08...">
|
---|
298 | <input type="submit" value="Remove">
|
---|
299 | </form>
|
---|
300 |
|
---|
301 | =head2 csrf_field
|
---|
302 |
|
---|
303 | %= csrf_field
|
---|
304 |
|
---|
305 | Generate C<input> tag of type C<hidden> with
|
---|
306 | L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">.
|
---|
307 |
|
---|
308 | <input name="csrf_token" type="hidden" value="fa6a08...">
|
---|
309 |
|
---|
310 | =head2 date_field
|
---|
311 |
|
---|
312 | %= date_field 'end'
|
---|
313 | %= date_field end => '2012-12-21'
|
---|
314 | %= date_field end => '2012-12-21', id => 'foo'
|
---|
315 |
|
---|
316 | Generate C<input> tag of type C<date>. Previous input values will automatically
|
---|
317 | get picked up and shown as default.
|
---|
318 |
|
---|
319 | <input name="end" type="date">
|
---|
320 | <input name="end" type="date" value="2012-12-21">
|
---|
321 | <input id="foo" name="end" type="date" value="2012-12-21">
|
---|
322 |
|
---|
323 | =head2 datetime_field
|
---|
324 |
|
---|
325 | %= datetime_field 'end'
|
---|
326 | %= datetime_field end => '2012-12-21T23:59:59'
|
---|
327 | %= datetime_field end => '2012-12-21T23:59:59', id => 'foo'
|
---|
328 |
|
---|
329 | Generate C<input> tag of type C<datetime-local>. Previous input values will
|
---|
330 | automatically get picked up and shown as default.
|
---|
331 |
|
---|
332 | <input name="end" type="datetime-local">
|
---|
333 | <input name="end" type="datetime-local" value="2012-12-21T23:59:59">
|
---|
334 | <input id="foo" name="end" type="datetime-local" value="2012-12-21T23:59:59">
|
---|
335 |
|
---|
336 | =head2 email_field
|
---|
337 |
|
---|
338 | %= email_field 'notify'
|
---|
339 | %= email_field notify => '[email protected]'
|
---|
340 | %= email_field notify => '[email protected]', id => 'foo'
|
---|
341 |
|
---|
342 | Generate C<input> tag of type C<email>. Previous input values will
|
---|
343 | automatically get picked up and shown as default.
|
---|
344 |
|
---|
345 | <input name="notify" type="email">
|
---|
346 | <input name="notify" type="email" value="[email protected]">
|
---|
347 | <input id="foo" name="notify" type="email" value="[email protected]">
|
---|
348 |
|
---|
349 | =head2 file_field
|
---|
350 |
|
---|
351 | %= file_field 'avatar'
|
---|
352 | %= file_field 'avatar', id => 'foo'
|
---|
353 |
|
---|
354 | Generate C<input> tag of type C<file>.
|
---|
355 |
|
---|
356 | <input name="avatar" type="file">
|
---|
357 | <input id="foo" name="avatar" type="file">
|
---|
358 |
|
---|
359 | =head2 form_for
|
---|
360 |
|
---|
361 | %= form_for login => begin
|
---|
362 | %= text_field 'first_name'
|
---|
363 | %= submit_button
|
---|
364 | % end
|
---|
365 | %= form_for login => {format => 'txt'} => (method => 'POST') => begin
|
---|
366 | %= text_field 'first_name'
|
---|
367 | %= submit_button
|
---|
368 | % end
|
---|
369 | %= form_for '/login' => (enctype => 'multipart/form-data') => begin
|
---|
370 | %= text_field 'first_name', disabled => 'disabled'
|
---|
371 | %= submit_button
|
---|
372 | % end
|
---|
373 | %= form_for 'http://example.com/login' => (method => 'POST') => begin
|
---|
374 | %= text_field 'first_name'
|
---|
375 | %= submit_button
|
---|
376 | % end
|
---|
377 | %= form_for some_delete_route => begin
|
---|
378 | %= submit_button 'Remove'
|
---|
379 | % end
|
---|
380 |
|
---|
381 | Generate portable C<form> tag with L<Mojolicious::Controller/"url_for">. For
|
---|
382 | routes that do not allow C<GET>, a C<method> attribute with the value C<POST>
|
---|
383 | will be automatically added. And for methods other than C<GET> or C<POST>, an
|
---|
384 | C<_method> query parameter will be added as well.
|
---|
385 |
|
---|
386 | <form action="/path/to/login">
|
---|
387 | <input name="first_name" type="text">
|
---|
388 | <input type="submit" value="Ok">
|
---|
389 | </form>
|
---|
390 | <form action="/path/to/login.txt" method="POST">
|
---|
391 | <input name="first_name" type="text">
|
---|
392 | <input type="submit" value="Ok">
|
---|
393 | </form>
|
---|
394 | <form action="/path/to/login" enctype="multipart/form-data">
|
---|
395 | <input disabled="disabled" name="first_name" type="text">
|
---|
396 | <input type="submit" value="Ok">
|
---|
397 | </form>
|
---|
398 | <form action="http://example.com/login" method="POST">
|
---|
399 | <input name="first_name" type="text">
|
---|
400 | <input type="submit" value="Ok">
|
---|
401 | </form>
|
---|
402 | <form action="/path/to/delete/route?_method=DELETE" method="POST">
|
---|
403 | <input type="submit" value="Remove">
|
---|
404 | </form>
|
---|
405 |
|
---|
406 | =head2 hidden_field
|
---|
407 |
|
---|
408 | %= hidden_field foo => 'bar'
|
---|
409 | %= hidden_field foo => 'bar', id => 'bar'
|
---|
410 |
|
---|
411 | Generate C<input> tag of type C<hidden>.
|
---|
412 |
|
---|
413 | <input name="foo" type="hidden" value="bar">
|
---|
414 | <input id="bar" name="foo" type="hidden" value="bar">
|
---|
415 |
|
---|
416 | =head2 image
|
---|
417 |
|
---|
418 | %= image '/images/foo.png'
|
---|
419 | %= image '/images/foo.png', alt => 'Foo'
|
---|
420 |
|
---|
421 | Generate portable C<img> tag.
|
---|
422 |
|
---|
423 | <img src="/path/to/images/foo.png">
|
---|
424 | <img alt="Foo" src="/path/to/images/foo.png">
|
---|
425 |
|
---|
426 | =head2 input_tag
|
---|
427 |
|
---|
428 | %= input_tag 'first_name'
|
---|
429 | %= input_tag first_name => 'Default'
|
---|
430 | %= input_tag 'employed', type => 'checkbox'
|
---|
431 |
|
---|
432 | Generate C<input> tag. Previous input values will automatically get picked up
|
---|
433 | and shown as default.
|
---|
434 |
|
---|
435 | <input name="first_name">
|
---|
436 | <input name="first_name" value="Default">
|
---|
437 | <input name="employed" type="checkbox">
|
---|
438 |
|
---|
439 | =head2 javascript
|
---|
440 |
|
---|
441 | %= javascript '/script.js'
|
---|
442 | %= javascript '/script.js', defer => undef
|
---|
443 | %= javascript begin
|
---|
444 | var a = 'b';
|
---|
445 | % end
|
---|
446 |
|
---|
447 | Generate portable C<script> tag for JavaScript asset.
|
---|
448 |
|
---|
449 | <script src="/path/to/script.js"></script>
|
---|
450 | <script defer src="/path/to/script.js"></script>
|
---|
451 | <script><![CDATA[
|
---|
452 | var a = 'b';
|
---|
453 | ]]></script>
|
---|
454 |
|
---|
455 | =head2 label_for
|
---|
456 |
|
---|
457 | %= label_for first_name => 'First name'
|
---|
458 | %= label_for first_name => 'First name', class => 'user'
|
---|
459 | %= label_for first_name => begin
|
---|
460 | First name
|
---|
461 | % end
|
---|
462 | %= label_for first_name => (class => 'user') => begin
|
---|
463 | First name
|
---|
464 | % end
|
---|
465 |
|
---|
466 | Generate C<label> tag.
|
---|
467 |
|
---|
468 | <label for="first_name">First name</label>
|
---|
469 | <label class="user" for="first_name">First name</label>
|
---|
470 | <label for="first_name">
|
---|
471 | First name
|
---|
472 | </label>
|
---|
473 | <label class="user" for="first_name">
|
---|
474 | First name
|
---|
475 | </label>
|
---|
476 |
|
---|
477 | =head2 link_to
|
---|
478 |
|
---|
479 | %= link_to Home => 'index'
|
---|
480 | %= link_to Home => 'index' => {format => 'txt'} => (class => 'menu')
|
---|
481 | %= link_to index => {format => 'txt'} => (class => 'menu') => begin
|
---|
482 | Home
|
---|
483 | % end
|
---|
484 | %= link_to Contact => 'mailto:[email protected]'
|
---|
485 | <%= link_to index => begin %>Home<% end %>
|
---|
486 | <%= link_to '/file.txt' => begin %>File<% end %>
|
---|
487 | <%= link_to 'https://mojolicious.org' => begin %>Mojolicious<% end %>
|
---|
488 | <%= link_to url_for->query(foo => 'bar')->to_abs => begin %>Retry<% end %>
|
---|
489 |
|
---|
490 | Generate portable C<a> tag with L<Mojolicious::Controller/"url_for">, defaults
|
---|
491 | to using the capitalized link target as content.
|
---|
492 |
|
---|
493 | <a href="/path/to/index">Home</a>
|
---|
494 | <a class="menu" href="/path/to/index.txt">Home</a>
|
---|
495 | <a class="menu" href="/path/to/index.txt">
|
---|
496 | Home
|
---|
497 | </a>
|
---|
498 | <a href="mailto:[email protected]">Contact</a>
|
---|
499 | <a href="/path/to/index">Home</a>
|
---|
500 | <a href="/path/to/file.txt">File</a>
|
---|
501 | <a href="https://mojolicious.org">Mojolicious</a>
|
---|
502 | <a href="http://127.0.0.1:3000/current/path?foo=bar">Retry</a>
|
---|
503 |
|
---|
504 | =head2 month_field
|
---|
505 |
|
---|
506 | %= month_field 'vacation'
|
---|
507 | %= month_field vacation => '2012-12'
|
---|
508 | %= month_field vacation => '2012-12', id => 'foo'
|
---|
509 |
|
---|
510 | Generate C<input> tag of type C<month>. Previous input values will
|
---|
511 | automatically get picked up and shown as default.
|
---|
512 |
|
---|
513 | <input name="vacation" type="month">
|
---|
514 | <input name="vacation" type="month" value="2012-12">
|
---|
515 | <input id="foo" name="vacation" type="month" value="2012-12">
|
---|
516 |
|
---|
517 | =head2 number_field
|
---|
518 |
|
---|
519 | %= number_field 'age'
|
---|
520 | %= number_field age => 25
|
---|
521 | %= number_field age => 25, id => 'foo', min => 0, max => 200
|
---|
522 |
|
---|
523 | Generate C<input> tag of type C<number>. Previous input values will
|
---|
524 | automatically get picked up and shown as default.
|
---|
525 |
|
---|
526 | <input name="age" type="number">
|
---|
527 | <input name="age" type="number" value="25">
|
---|
528 | <input id="foo" max="200" min="0" name="age" type="number" value="25">
|
---|
529 |
|
---|
530 | =head2 password_field
|
---|
531 |
|
---|
532 | %= password_field 'pass'
|
---|
533 | %= password_field 'pass', id => 'foo'
|
---|
534 |
|
---|
535 | Generate C<input> tag of type C<password>.
|
---|
536 |
|
---|
537 | <input name="pass" type="password">
|
---|
538 | <input id="foo" name="pass" type="password">
|
---|
539 |
|
---|
540 | =head2 radio_button
|
---|
541 |
|
---|
542 | %= radio_button 'test'
|
---|
543 | %= radio_button country => 'germany'
|
---|
544 | %= radio_button country => 'germany', checked => undef, id => 'foo'
|
---|
545 |
|
---|
546 | Generate C<input> tag of type C<radio>. Previous input values will
|
---|
547 | automatically get picked up and shown as default.
|
---|
548 |
|
---|
549 | <input name="test" type="radio">
|
---|
550 | <input name="country" type="radio" value="germany">
|
---|
551 | <input checked id="foo" name="country" type="radio" value="germany">
|
---|
552 |
|
---|
553 | =head2 range_field
|
---|
554 |
|
---|
555 | %= range_field 'age'
|
---|
556 | %= range_field age => 25
|
---|
557 | %= range_field age => 25, id => 'foo', min => 0, max => 200
|
---|
558 |
|
---|
559 | Generate C<input> tag of type C<range>. Previous input values will
|
---|
560 | automatically get picked up and shown as default.
|
---|
561 |
|
---|
562 | <input name="age" type="range">
|
---|
563 | <input name="age" type="range" value="25">
|
---|
564 | <input id="foo" max="200" min="200" name="age" type="range" value="25">
|
---|
565 |
|
---|
566 | =head2 search_field
|
---|
567 |
|
---|
568 | %= search_field 'q'
|
---|
569 | %= search_field q => 'perl'
|
---|
570 | %= search_field q => 'perl', id => 'foo'
|
---|
571 |
|
---|
572 | Generate C<input> tag of type C<search>. Previous input values will
|
---|
573 | automatically get picked up and shown as default.
|
---|
574 |
|
---|
575 | <input name="q" type="search">
|
---|
576 | <input name="q" type="search" value="perl">
|
---|
577 | <input id="foo" name="q" type="search" value="perl">
|
---|
578 |
|
---|
579 | =head2 select_field
|
---|
580 |
|
---|
581 | %= select_field country => ['de', 'en']
|
---|
582 | %= select_field country => [[Germany => 'de'], 'en'], id => 'eu'
|
---|
583 | %= select_field country => [[Germany => 'de', selected => 'selected'], 'en']
|
---|
584 | %= select_field country => [c(EU => [[Germany => 'de'], 'en'], id => 'eu')]
|
---|
585 | %= select_field country => [c(EU => ['de', 'en']), c(Asia => ['cn', 'jp'])]
|
---|
586 |
|
---|
587 | Generate C<select> and C<option> tags from array references and C<optgroup>
|
---|
588 | tags from L<Mojo::Collection> objects. Previous input values will automatically
|
---|
589 | get picked up and shown as default.
|
---|
590 |
|
---|
591 | <select name="country">
|
---|
592 | <option value="de">de</option>
|
---|
593 | <option value="en">en</option>
|
---|
594 | </select>
|
---|
595 | <select id="eu" name="country">
|
---|
596 | <option value="de">Germany</option>
|
---|
597 | <option value="en">en</option>
|
---|
598 | </select>
|
---|
599 | <select name="country">
|
---|
600 | <option selected="selected" value="de">Germany</option>
|
---|
601 | <option value="en">en</option>
|
---|
602 | </select>
|
---|
603 | <select name="country">
|
---|
604 | <optgroup id="eu" label="EU">
|
---|
605 | <option value="de">Germany</option>
|
---|
606 | <option value="en">en</option>
|
---|
607 | </optgroup>
|
---|
608 | </select>
|
---|
609 | <select name="country">
|
---|
610 | <optgroup label="EU">
|
---|
611 | <option value="de">de</option>
|
---|
612 | <option value="en">en</option>
|
---|
613 | </optgroup>
|
---|
614 | <optgroup label="Asia">
|
---|
615 | <option value="cn">cn</option>
|
---|
616 | <option value="jp">jp</option>
|
---|
617 | </optgroup>
|
---|
618 | </select>
|
---|
619 |
|
---|
620 | =head2 stylesheet
|
---|
621 |
|
---|
622 | %= stylesheet '/foo.css'
|
---|
623 | %= stylesheet '/foo.css', title => 'Foo style'
|
---|
624 | %= stylesheet begin
|
---|
625 | body {color: #000}
|
---|
626 | % end
|
---|
627 |
|
---|
628 | Generate portable C<style> or C<link> tag for CSS asset.
|
---|
629 |
|
---|
630 | <link href="/path/to/foo.css" rel="stylesheet">
|
---|
631 | <link href="/path/to/foo.css" rel="stylesheet" title="Foo style">
|
---|
632 | <style><![CDATA[
|
---|
633 | body {color: #000}
|
---|
634 | ]]></style>
|
---|
635 |
|
---|
636 | =head2 submit_button
|
---|
637 |
|
---|
638 | %= submit_button
|
---|
639 | %= submit_button 'Ok!', id => 'foo'
|
---|
640 |
|
---|
641 | Generate C<input> tag of type C<submit>.
|
---|
642 |
|
---|
643 | <input type="submit" value="Ok">
|
---|
644 | <input id="foo" type="submit" value="Ok!">
|
---|
645 |
|
---|
646 | =head2 t
|
---|
647 |
|
---|
648 | %= t div => 'test & 123'
|
---|
649 |
|
---|
650 | Alias for L</"tag">.
|
---|
651 |
|
---|
652 | <div>test & 123</div>
|
---|
653 |
|
---|
654 | =head2 tag
|
---|
655 |
|
---|
656 | %= tag 'br'
|
---|
657 | %= tag 'div'
|
---|
658 | %= tag 'div', id => 'foo', hidden => undef
|
---|
659 | %= tag 'div', 'test & 123'
|
---|
660 | %= tag 'div', id => 'foo', 'test & 123'
|
---|
661 | %= tag 'div', data => {my_id => 1, Name => 'test'}, 'test & 123'
|
---|
662 | %= tag div => begin
|
---|
663 | test & 123
|
---|
664 | % end
|
---|
665 | <%= tag div => (id => 'foo') => begin %>test & 123<% end %>
|
---|
666 |
|
---|
667 | Alias for L<Mojo::DOM/"new_tag">.
|
---|
668 |
|
---|
669 | <br>
|
---|
670 | <div></div>
|
---|
671 | <div id="foo" hidden></div>
|
---|
672 | <div>test & 123</div>
|
---|
673 | <div id="foo">test & 123</div>
|
---|
674 | <div data-my-id="1" data-name="test">test & 123</div>
|
---|
675 | <div>
|
---|
676 | test & 123
|
---|
677 | </div>
|
---|
678 | <div id="foo">test & 123</div>
|
---|
679 |
|
---|
680 | Very useful for reuse in more specific tag helpers.
|
---|
681 |
|
---|
682 | my $output = $c->tag('meta');
|
---|
683 | my $output = $c->tag('meta', charset => 'UTF-8');
|
---|
684 | my $output = $c->tag('div', '<p>This will be escaped</p>');
|
---|
685 | my $output = $c->tag('div', sub { '<p>This will not be escaped</p>' });
|
---|
686 |
|
---|
687 | Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent
|
---|
688 | accidental double escaping in C<ep> templates.
|
---|
689 |
|
---|
690 | =head2 tag_with_error
|
---|
691 |
|
---|
692 | %= tag_with_error 'input', class => 'foo'
|
---|
693 |
|
---|
694 | Same as L</"tag">, but adds the class C<field-with-error>.
|
---|
695 |
|
---|
696 | <input class="foo field-with-error">
|
---|
697 |
|
---|
698 | =head2 tel_field
|
---|
699 |
|
---|
700 | %= tel_field 'work'
|
---|
701 | %= tel_field work => '123456789'
|
---|
702 | %= tel_field work => '123456789', id => 'foo'
|
---|
703 |
|
---|
704 | Generate C<input> tag of type C<tel>. Previous input values will automatically
|
---|
705 | get picked up and shown as default.
|
---|
706 |
|
---|
707 | <input name="work" type="tel">
|
---|
708 | <input name="work" type="tel" value="123456789">
|
---|
709 | <input id="foo" name="work" type="tel" value="123456789">
|
---|
710 |
|
---|
711 | =head2 text_area
|
---|
712 |
|
---|
713 | %= text_area 'story'
|
---|
714 | %= text_area 'story', cols => 40
|
---|
715 | %= text_area story => 'Default', cols => 40
|
---|
716 | %= text_area story => (cols => 40) => begin
|
---|
717 | Default
|
---|
718 | % end
|
---|
719 |
|
---|
720 | Generate C<textarea> tag. Previous input values will automatically get picked
|
---|
721 | up and shown as default.
|
---|
722 |
|
---|
723 | <textarea name="story"></textarea>
|
---|
724 | <textarea cols="40" name="story"></textarea>
|
---|
725 | <textarea cols="40" name="story">Default</textarea>
|
---|
726 | <textarea cols="40" name="story">
|
---|
727 | Default
|
---|
728 | </textarea>
|
---|
729 |
|
---|
730 | =head2 text_field
|
---|
731 |
|
---|
732 | %= text_field 'first_name'
|
---|
733 | %= text_field first_name => 'Default'
|
---|
734 | %= text_field first_name => 'Default', class => 'user'
|
---|
735 |
|
---|
736 | Generate C<input> tag of type C<text>. Previous input values will automatically
|
---|
737 | get picked up and shown as default.
|
---|
738 |
|
---|
739 | <input name="first_name" type="text">
|
---|
740 | <input name="first_name" type="text" value="Default">
|
---|
741 | <input class="user" name="first_name" type="text" value="Default">
|
---|
742 |
|
---|
743 | =head2 time_field
|
---|
744 |
|
---|
745 | %= time_field 'start'
|
---|
746 | %= time_field start => '23:59:59'
|
---|
747 | %= time_field start => '23:59:59', id => 'foo'
|
---|
748 |
|
---|
749 | Generate C<input> tag of type C<time>. Previous input values will automatically
|
---|
750 | get picked up and shown as default.
|
---|
751 |
|
---|
752 | <input name="start" type="time">
|
---|
753 | <input name="start" type="time" value="23:59:59">
|
---|
754 | <input id="foo" name="start" type="time" value="23:59:59">
|
---|
755 |
|
---|
756 | =head2 url_field
|
---|
757 |
|
---|
758 | %= url_field 'address'
|
---|
759 | %= url_field address => 'https://mojolicious.org'
|
---|
760 | %= url_field address => 'https://mojolicious.org', id => 'foo'
|
---|
761 |
|
---|
762 | Generate C<input> tag of type C<url>. Previous input values will automatically
|
---|
763 | get picked up and shown as default.
|
---|
764 |
|
---|
765 | <input name="address" type="url">
|
---|
766 | <input name="address" type="url" value="https://mojolicious.org">
|
---|
767 | <input id="foo" name="address" type="url" value="https://mojolicious.org">
|
---|
768 |
|
---|
769 | =head2 week_field
|
---|
770 |
|
---|
771 | %= week_field 'vacation'
|
---|
772 | %= week_field vacation => '2012-W17'
|
---|
773 | %= week_field vacation => '2012-W17', id => 'foo'
|
---|
774 |
|
---|
775 | Generate C<input> tag of type C<week>. Previous input values will automatically
|
---|
776 | get picked up and shown as default.
|
---|
777 |
|
---|
778 | <input name="vacation" type="week">
|
---|
779 | <input name="vacation" type="week" value="2012-W17">
|
---|
780 | <input id="foo" name="vacation" type="week" value="2012-W17">
|
---|
781 |
|
---|
782 | =head1 METHODS
|
---|
783 |
|
---|
784 | L<Mojolicious::Plugin::TagHelpers> inherits all methods from
|
---|
785 | L<Mojolicious::Plugin> and implements the following new ones.
|
---|
786 |
|
---|
787 | =head2 register
|
---|
788 |
|
---|
789 | $plugin->register(Mojolicious->new);
|
---|
790 |
|
---|
791 | Register helpers in L<Mojolicious> application.
|
---|
792 |
|
---|
793 | =head1 SEE ALSO
|
---|
794 |
|
---|
795 | L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
---|
796 |
|
---|
797 | =cut
|
---|