Commit | Line | Data |
7adfd53f |
1 | package Reaction::UI::ViewPort::Field; |
2 | |
3 | use Reaction::Class; |
4 | |
5 | class Field is 'Reaction::UI::ViewPort', which { |
6 | |
7 | has name => ( |
8 | isa => 'Str', is => 'rw', required => 1 |
9 | ); |
10 | |
11 | has action => ( |
12 | isa => 'Reaction::InterfaceModel::Action', |
13 | is => 'ro', required => 0, predicate => 'has_action', |
14 | ); |
15 | |
16 | has attribute => ( |
17 | isa => 'Reaction::Meta::InterfaceModel::Action::ParameterAttribute', |
18 | is => 'ro', predicate => 'has_attribute', |
19 | ); |
20 | |
21 | has value => ( |
22 | is => 'rw', lazy_build => 1, trigger_adopt('value'), |
23 | clearer => 'clear_value', |
24 | ); |
25 | |
26 | has needs_sync => ( |
27 | isa => 'Int', is => 'rw', default => 0 |
28 | ); |
29 | |
30 | has label => (isa => 'Str', is => 'rw', lazy_build => 1); |
31 | |
32 | has message => ( |
33 | isa => 'Str', is => 'rw', required => 1, default => sub { '' } |
34 | ); |
35 | |
36 | implements BUILD => as { |
37 | my ($self) = @_; |
38 | if (!$self->has_attribute != !$self->has_action) { |
39 | confess "Should have both action and attribute or neither"; |
40 | } |
41 | }; |
42 | |
89939ff9 |
43 | implements _build_label => as { |
7adfd53f |
44 | my ($self) = @_; |
6ab43711 |
45 | my $label = join(' ', map { ucfirst } split('_', $self->name)); |
de48f4e6 |
46 | # print STDERR "Field " . $self->name . " has label '$label'\n"; |
6ab43711 |
47 | return $label; |
7adfd53f |
48 | }; |
49 | |
89939ff9 |
50 | implements _build_value => as { |
7adfd53f |
51 | my ($self) = @_; |
52 | if ($self->has_attribute) { |
53 | my $reader = $self->attribute->get_read_method; |
54 | my $predicate = $self->attribute->predicate; |
55 | if (!$predicate || $self->action->$predicate) { |
56 | return $self->action->$reader; |
57 | } |
58 | } |
59 | return ''; |
60 | }; |
61 | |
62 | implements adopt_value => as { |
63 | my ($self) = @_; |
64 | $self->needs_sync(1) if $self->has_attribute; |
65 | }; |
66 | |
67 | implements sync_to_action => as { |
68 | my ($self) = @_; |
69 | return unless $self->needs_sync && $self->has_attribute && $self->has_value; |
70 | my $attr = $self->attribute; |
71 | if (my $tc = $attr->type_constraint) { |
72 | my $value = $self->value; |
73 | if ($tc->has_coercion) { |
74 | $value = $tc->coercion->coerce($value); |
75 | } |
76 | my $error = $tc->validate($self->value); |
77 | if (defined $error) { |
78 | $self->message($error); |
79 | return; |
80 | } |
81 | } |
82 | my $writer = $attr->get_write_method; |
83 | confess "No writer for attribute" unless defined($writer); |
84 | $self->action->$writer($self->value); |
85 | $self->needs_sync(0); |
86 | }; |
87 | |
88 | implements sync_from_action => as { |
89 | my ($self) = @_; |
90 | return unless !$self->needs_sync && $self->has_attribute; |
91 | $self->message($self->action->error_for($self->attribute)||''); |
92 | }; |
93 | |
94 | override accept_events => sub { ('value', super()) }; |
95 | |
96 | }; |
97 | |
98 | 1; |
99 | |
100 | =head1 NAME |
101 | |
102 | Reaction::UI::ViewPort::Field |
103 | |
104 | =head1 DESCRIPTION |
105 | |
106 | This viewport is the base class for all field types. |
107 | |
108 | =head1 ATTRIBUTES |
109 | |
110 | =head2 name |
111 | |
112 | =head2 action |
113 | |
114 | L<Reaction::InterfaceModel::Action> |
115 | |
116 | =head2 attribute |
117 | |
118 | L<Reaction::Meta::InterfaceModel::Action::ParameterAttribute> |
119 | |
120 | =head2 value |
121 | |
122 | =head2 needs_sync |
123 | |
124 | =head2 label |
125 | |
126 | User friendly label, by default is based on the name. |
127 | |
128 | =head2 message |
129 | |
130 | Optional string relating to the field. |
131 | |
132 | =head1 SEE ALSO |
133 | |
134 | =head2 L<Reaction::UI::ViewPort> |
135 | |
136 | =head2 L<Reaction::UI::ViewPort::DisplayField> |
137 | |
138 | =head2 L<Reaction::UI::ViewPort::Field::Boolean> |
139 | |
140 | =head2 L<Reaction::UI::ViewPort::Field::ChooseMany> |
141 | |
142 | =head2 L<Reaction::UI::ViewPort::Field::ChooseOne> |
143 | |
144 | =head2 L<Reaction::UI::ViewPort::Field::DateTime> |
145 | |
146 | =head2 L<Reaction::UI::ViewPort::Field::File> |
147 | |
148 | =head2 L<Reaction::UI::ViewPort::Field::HiddenArray> |
149 | |
150 | =head2 L<Reaction::UI::ViewPort::Field::Number> |
151 | |
152 | =head2 L<Reaction::UI::ViewPort::Field::Password> |
153 | |
154 | =head2 L<Reaction::UI::ViewPort::Field::String> |
155 | |
156 | =head2 L<Reaction::UI::ViewPort::Field::Text> |
157 | |
158 | =head2 L<Reaction::UI::ViewPort::Field::TimeRange> |
159 | |
160 | =head1 AUTHORS |
161 | |
162 | See L<Reaction::Class> for authors. |
163 | |
164 | =head1 LICENSE |
165 | |
166 | See L<Reaction::Class> for the license. |
167 | |
168 | =cut |