port actionform, field widgets to declarative system
[catagits/Reaction.git] / lib / Reaction / UI / ViewPort / Field.pm
CommitLineData
7adfd53f 1package Reaction::UI::ViewPort::Field;
2
3use Reaction::Class;
4
5class 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
664d660c 67 implements value_string => as { shift->value };
68
7adfd53f 69 implements sync_to_action => as {
70 my ($self) = @_;
71 return unless $self->needs_sync && $self->has_attribute && $self->has_value;
72 my $attr = $self->attribute;
73 if (my $tc = $attr->type_constraint) {
74 my $value = $self->value;
75 if ($tc->has_coercion) {
76 $value = $tc->coercion->coerce($value);
77 }
78 my $error = $tc->validate($self->value);
79 if (defined $error) {
80 $self->message($error);
81 return;
82 }
83 }
84 my $writer = $attr->get_write_method;
85 confess "No writer for attribute" unless defined($writer);
86 $self->action->$writer($self->value);
87 $self->needs_sync(0);
88 };
89
90 implements sync_from_action => as {
91 my ($self) = @_;
92 return unless !$self->needs_sync && $self->has_attribute;
93 $self->message($self->action->error_for($self->attribute)||'');
94 };
95
96 override accept_events => sub { ('value', super()) };
97
98};
99
1001;
101
102=head1 NAME
103
104Reaction::UI::ViewPort::Field
105
106=head1 DESCRIPTION
107
108This viewport is the base class for all field types.
109
110=head1 ATTRIBUTES
111
112=head2 name
113
114=head2 action
115
116L<Reaction::InterfaceModel::Action>
117
118=head2 attribute
119
120L<Reaction::Meta::InterfaceModel::Action::ParameterAttribute>
121
122=head2 value
123
124=head2 needs_sync
125
126=head2 label
127
128User friendly label, by default is based on the name.
129
130=head2 message
131
132Optional string relating to the field.
133
134=head1 SEE ALSO
135
136=head2 L<Reaction::UI::ViewPort>
137
138=head2 L<Reaction::UI::ViewPort::DisplayField>
139
140=head2 L<Reaction::UI::ViewPort::Field::Boolean>
141
142=head2 L<Reaction::UI::ViewPort::Field::ChooseMany>
143
144=head2 L<Reaction::UI::ViewPort::Field::ChooseOne>
145
146=head2 L<Reaction::UI::ViewPort::Field::DateTime>
147
148=head2 L<Reaction::UI::ViewPort::Field::File>
149
150=head2 L<Reaction::UI::ViewPort::Field::HiddenArray>
151
152=head2 L<Reaction::UI::ViewPort::Field::Number>
153
154=head2 L<Reaction::UI::ViewPort::Field::Password>
155
156=head2 L<Reaction::UI::ViewPort::Field::String>
157
158=head2 L<Reaction::UI::ViewPort::Field::Text>
159
160=head2 L<Reaction::UI::ViewPort::Field::TimeRange>
161
162=head1 AUTHORS
163
164See L<Reaction::Class> for authors.
165
166=head1 LICENSE
167
168See L<Reaction::Class> for the license.
169
170=cut