fix agent bulk billing with quantities, RT#31610
[freeside.git] / rt / docs / extending / using_forms_widgets.pod
1 =head1 Using widgets F<html/Widgets/Form*>
2
3 This widgets was implemented to address several common issues in handling
4 request arguments and allow developers to avoid reinventing the wheel.
5
6 =head2 General info
7
8 Each component shows widget by default and has two methods: Process and 
9 InputOnly. The first one method process arguments and return new value
10 of a parametr. The second one is helper that shows only form elements
11 with minimum of required text labels.
12
13 So you show a widget with:
14     <& /Widgets/Form/Integer,
15         Name => 'NameOfInputElement',
16         Description => 'Input integer',
17     &>
18
19 You can show only C<input> box using:
20     <& /Widgets/Form/Integer:InputOnly,
21         Name => 'NameOfInputElement',
22     &>
23
24 In such a simple case you even can avoid processing. Yeah, most probably
25 you want to check if value is really integer, but these widgets don't
26 do validation for you, but they are more about fetching values from
27 hash of arguments, showing these values to user and preserving state
28 of value between form reloads (see below).
29
30 =head2 Processing
31
32 Processing is required when you use L<extended features|/Extendent features>,
33 such as Default, Multiple or Alternative.
34
35 To process arguments of a request you have to do the following:
36     $ARGS{'NameOfInputElement'} = $m->comp(
37         '/Widgets/Form/Integer:Process',
38         Arguments => \%ARGS,
39         Name      => 'NameOfInputElement',
40     );
41
42 The method returns processed value in canonical form. For different widgets
43 a canonical form is different and depends on activated features, so you must
44 always activate the same features during showing a widget and processing
45 results.
46
47 =head2 Extendent features
48
49 =head3 Default value
50
51 If C<Default> argument is true then widgets expect that there is some
52 default value for argument if user fills nothing. 'Nothing' in each
53 widget is different, for example in select box it's special option
54 which is always the first one, in integer box string '' means empty
55 value, but boolean box uses radio buttons in this case with three
56 options: Yes, No and Default.
57
58 Each widget that supports C<Default> feature as well has C<DefaultLabel> and
59 C<DefaultValue> arguments.
60
61 =head4 Processing and showing with activated Default feature
62
63 When this option is activated then C<Process> method returns undef
64 value if user selected default value. So for integer box it's empty
65 string and so on.
66
67 As well when you show a widget you should pass undef as C<CurrentValue>
68 to inform widget that the current value is default one.
69
70 As all methods of a widget are consistent in this behaviour so you
71 shouldn't care much about that, but this allows you to implement
72 custom actions if processing returned undef, for example delete user's
73 preference record instead of updating it (default value may change later to).
74
75 =head4 C<DefaultValue> when C<Default> is not active
76
77 DefaultValue argument is still actual in the Process method even if
78 C<Default> is not true. This argument defines intial value. If value
79 of a key in Arguments is not defined then it's treated as intial state
80 and the method returns default value.
81
82 =head3 Multiple and Alternative
83
84 These options are only supported by the select widget.
85
86 TODO: Add more info
87
88 =head2 Implementation details
89
90 =head3 Boolean widget
91
92 This widget a little bit tricky. When you use Default option then
93 things are simple and you see three radio buttons, but in other
94 case we use a checkbox. But as you know browsers don't pass unchecked
95 boxes to server, so arguments of a request has no entry for them.
96
97 In the latter case it's hard to figure out case when user unselected
98 value. Imagine form with a checkbox, you want show it checked by
99 default and as well form is reloadable (like Reply forms that have
100 "Add Another File" buttons). User uncheck the box and then upload
101 file, in this case you want to show user's choice instead of default,
102 but browser doesn't send any value and you can not figure out if
103 it's initial state or page reload. To solve this problem we use magic
104 hidden input field with the same name as the box and value equal to
105 zero (0). Mason folds arguments with the same name into array refs, so
106 we get 0 if box is unchecked and [0, 1] if box is checked. An array
107 reference is true value and 0 is defined value so we know that it's
108 not initial state and avoid switching back to default. As well this
109 trick works good in a case when you want show a link to a page and
110 define default choice for some boolean argument, you don't need
111 to set argument twice, you just set it to true value (for ex. 1) and
112 things just work.
113