File Coverage

blib/lib/HTML/FormHandlerX/JQueryRemoteValidator.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package HTML::FormHandlerX::JQueryRemoteValidator;
2              
3 1     1   3843448 use HTML::FormHandler::Moose::Role;
  0            
  0            
4             use Method::Signatures::Simple;
5             use JSON ();
6              
7             =head1 NAME
8              
9             HTML::FormHandlerX::JQueryRemoteValidator - call server-side validation code asynchronously from client-side forms.
10              
11             =head1 VERSION
12              
13             Version 0.03
14              
15             =cut
16              
17             our $VERSION = '0.03';
18              
19              
20             =head1 SYNOPSIS
21              
22             package MyApp::Form::Foo;
23             use HTML::FormHandler::Moose;
24              
25             with 'HTML::FormHandlerX::JQueryRemoteValidator';
26              
27             ...
28              
29             # You need to provide a form validation script at /ajax/formvalidator
30             # In Poet/Mason, something like this in /ajax/formvalidator/dhandler.mp
31              
32             method handle () {
33             $m->res->content_type('application/json');
34              
35             my ($form_name, $field_name) = split '/', $m->path_info;
36              
37             my $form = $.form($form_name);
38             $form->process(params => $.args, no_update => 1);
39              
40             my $err = join ' ', @{$form->field($field_name)->errors};
41             my $result = $err || 'true';
42              
43             $m->print(JSON->new->allow_nonref->encode($result));
44             }
45              
46              
47             =cut
48              
49             has_field _validation_scripts => (type => 'JavaScript', set_js_code => '_js_code_for_validation_scripts');
50              
51             has validation_endpoint => (is => 'rw', isa => 'Str', default => '/ajax/formvalidator');
52              
53             has jquery_validator_link => (is => 'rw', isa => 'Str', default => 'http://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js');
54              
55             has skip_remote_validation_fields => (is => 'rw', isa => 'ArrayRef', default => sub { [ qw(submit) ] });
56             has skip_remote_validation_types => (is => 'rw', isa => 'ArrayRef', default => sub { [ qw(Hidden noCAPTCHA Display JSON JavaScript) ] });
57              
58             has skip_all_remote_validation => (is => 'rw', isa => 'Bool', default => 0);
59              
60             has element_target_class => (is => 'ro', isa => 'Str', default => 'form-group');
61              
62             method _js_code_for_validation_scripts () {
63             my $spec_data = $self->_data_for_validation_spec;
64             my $spec = JSON->new->utf8
65             ->allow_nonref
66             ->pretty(1)
67             # ->relaxed(undef)
68             # ->canonical(undef)
69             ->encode($spec_data)
70             || '';
71              
72             my $form_name = $self->name;
73             $spec =~ s/"${form_name}_data_collector"/${form_name}_data_collector/g;
74             $spec =~ s/\n$//;
75             $spec = "\n var ${form_name}_validation_spec = $spec;\n";
76             return $self->_data_collector_script . $spec . $self->_run_validator_script;
77             }
78              
79             method _data_for_validation_spec () {
80             my $js_profile = { rules => {}, messages => {} };
81              
82             foreach my $field (@{$self->fields}) {
83             next if $self->_skip_remote_validation($field); # don't build rules for these fields
84             $js_profile->{rules}->{$field->id}->{remote} = $self->_build_remote_rule($field);
85             }
86              
87             return $js_profile;
88             }
89              
90             method _build_remote_rule ($field) {
91             my $remote_rule = {
92             url => sprintf("%s/%s/%s", $self->validation_endpoint, $self->name, $field->name),
93             type => 'POST',
94             data => $self->name . "_data_collector",
95             };
96              
97             return $remote_rule;
98             }
99              
100             method _data_collector_script () {
101             my $script = join(",\n",
102             map { sprintf " \"%s.%s\": function () { return \$(\"#%s\\\\.%s\").val() }", $self->name, $_->name, $self->name, $_->name }
103             grep { ! $self->_skip_remote_validation($_) }
104             sort {$a->name cmp $b->name} # the sort is there to keep output consistent for test scripts
105             $self->fields
106             );
107              
108             my $form_name = $self->name;
109             return " var ${form_name}_data_collector = {\n" . $script . "\n };\n";
110             }
111              
112             method _skip_remote_validation ($field) {
113             return 1 if $self->skip_all_remote_validation;
114             my %skip_field = map {$_=>1} @{$self->skip_remote_validation_fields};
115             my %skip_type = map {$_=>1} @{$self->skip_remote_validation_types};
116             return 1 if $skip_field{$field->name};
117             return 1 if $skip_type{$field->type};
118             return 0;
119             }
120              
121             method _run_validator_script () {
122             my $form_name = $self->name;
123             my $link = $self->jquery_validator_link;
124             my $css_target = $self->element_target_class;
125              
126             my $script = <
127              
128             \$(document).ready(function() {
129             \$.getScript("$link", function () {
130             if (typeof ${form_name}_validation_spec !== 'undefined') {
131             \$('form#$form_name').validate({
132             rules: ${form_name}_validation_spec.rules,
133             messages: ${form_name}_validation_spec.messages,
134             highlight: function(element) {
135             \$(element).closest('.$css_target').removeClass('success').addClass('error');
136             },
137             success: function(element) {
138             element
139             .text('dummy').addClass('valid')
140             .closest('.$css_target').removeClass('error').addClass('success');
141             }
142             });
143             }
144             });
145             });
146             SCRIPT
147              
148             return $script;
149             }
150              
151              
152             =head1 CONFIGURATION AND SETUP
153              
154             C adds jQuery scripts to your form to
155             gather form input and send it to the server for validation before the user submits the
156             completed form. The server responds with messages that are displayed on the form.
157             So you will need to set up various bits and pieces. Most have straightforward defaults.
158              
159             =head2 C
160              
161             Default: /ajax/formvalidator
162              
163             The form data will be POSTed to C<[validation_endpoint]/[form_name]/[field_name]>.
164              
165             Note that *all* fields are submitted, not just the field being validated.
166              
167             You must write the code to handle this submission. The response should be a JSON
168             string, either C if the field passed its tests, or a message describing
169             the error. The message will be displayed on the form.
170              
171             The synopsis has an example in Poet/Mason.
172              
173             =head2 jQuery
174              
175             You need to load the jQuery library yourself. See https://jquery.com/download/
176              
177             =head2 C
178              
179             Default: http://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js
180              
181             You can leave this as-is, or if you prefer, you can put the file on your own
182             server and modify this setting to point to it.
183              
184             =head2 C
185              
186             Default: form-group
187              
188             This identifies the CSS class of the elements that will receive the validation
189             success/error messages. The default (C) is for forms styled using
190             Bootstrap 3.
191              
192             =head2 C
193              
194             Default: C<[ qw(Hidden noCAPTCHA Display JSON JavaScript) ]>
195              
196             A list of field types that should not be included in the validation calls.
197              
198             =head2 C
199              
200             Default: C<[ qw(submit) ]>
201              
202             A list of field names that should not be included in the validation calls.
203              
204             =head2 C
205              
206             Boolean, default 0.
207              
208             A flag to turn off remote validation altogether, perhaps useful during form development.
209              
210              
211             =head2 CSS
212              
213             You will probably want to style the C and C classes on your
214             form, for example:
215              
216             label.valid {
217             width: 24px;
218             height: 24px;
219             background: url(/static/images/valid.png) center center no-repeat;
220             display: inline-block;
221             text-indent: -9999px;
222             }
223              
224             label.error {
225             font-weight: normal;
226             color: red;
227             padding: 2px 8px;
228             margin-top: 2px;
229             }
230              
231             =cut
232              
233              
234              
235              
236             =head1 See also
237              
238             =over 4
239              
240             =item L
241              
242             =item L
243              
244             =item L
245              
246             =back
247              
248             =cut
249              
250             =head1 AUTHOR
251              
252             David R. Baird, C<< >>
253              
254             =head1 CODE REPOSITORY
255              
256             L
257              
258             Please report any bugs or feature requests there.
259              
260              
261             =head1 SUPPORT
262              
263             You can find documentation for this module with the perldoc command.
264              
265             perldoc HTML::FormHandlerX::JQueryRemoteValidator
266              
267              
268             You can also look for information at:
269              
270             =over 4
271              
272             =item * MetaCPAN
273              
274             L
275              
276              
277             =item * AnnoCPAN: Annotated CPAN documentation
278              
279             L
280              
281             =item * CPAN Ratings
282              
283             L
284              
285             =item * Search CPAN
286              
287             L
288              
289             =back
290              
291              
292             =head1 ACKNOWLEDGEMENTS
293              
294             This started out as a modification of Aaron Trevana's
295             HTML::FormHandlerX::Form::JQueryValidator
296              
297              
298             =head1 LICENSE AND COPYRIGHT
299              
300             Copyright 2016 David R. Baird.
301              
302             This program is free software; you can redistribute it and/or modify it
303             under the terms of the the Artistic License (2.0). You may obtain a
304             copy of the full license at:
305              
306             L
307              
308             Any use, modification, and distribution of the Standard or Modified
309             Versions is governed by this Artistic License. By using, modifying or
310             distributing the Package, you accept this license. Do not use, modify,
311             or distribute the Package, if you do not accept this license.
312              
313             If your Modified Version has been derived from a Modified Version made
314             by someone other than you, you are nevertheless required to ensure that
315             your Modified Version complies with the requirements of this license.
316              
317             This license does not grant you the right to use any trademark, service
318             mark, tradename, or logo of the Copyright Holder.
319              
320             This license includes the non-exclusive, worldwide, free-of-charge
321             patent license to make, have made, use, offer to sell, sell, import and
322             otherwise transfer the Package with respect to any patent claims
323             licensable by the Copyright Holder that are necessarily infringed by the
324             Package. If you institute patent litigation (including a cross-claim or
325             counterclaim) against any party alleging that the Package constitutes
326             direct or contributory patent infringement, then this Artistic License
327             to you shall terminate on the date that such litigation is filed.
328              
329             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
330             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
331             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
332             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
333             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
334             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
335             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
336             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
337              
338              
339             =cut
340              
341             1; # End of HTML::FormHandlerX::JQueryRemoteValidator