comparison Lib/IMPL/Web/View/TTControl.pm @ 352:675cd1829255

working on TTView: added control classes support
author cin
date Thu, 10 Oct 2013 19:51:19 +0400
parents f05634287ac7
children feeb3bc4a818
comparison
equal deleted inserted replaced
351:cfd7570c2af2 352:675cd1829255
1 package IMPL::Web::View::TTControl; 1 package IMPL::Web::View::TTControl;
2 use strict; 2 use strict;
3 3
4 use IMPL::Const qw(:prop); 4 use IMPL::Const qw(:prop);
5 use IMPL::lang qw(:hash :base); 5 use IMPL::lang qw(:hash :base);
6 use Scalar::Util qw(blessed reftype);
7 use IMPL::declare { 6 use IMPL::declare {
8 require => { 7 require => {
9 TemplateDocument => 'Template::Document', 8 Exception => 'IMPL::Exception',
10 TTContext => 'IMPL::Web::View::TTContext', 9 ArgException => '-IMPL::InvalidArgumentException'
11 Exception => 'IMPL::Exception',
12 ArgumentException => '-IMPL::InvalidArgumentException',
13 OperationException => '-IMPL::InvalidOperationException'
14 }, 10 },
15 base => [ 11 base => [
16 'IMPL::Object' => undef 12 'IMPL::Object' => undef
17 ], 13 ],
18 props => [ 14 props => [
19 id => PROP_RO, 15 context => PROP_RO,
20 attributes => PROP_RW, 16 template => PROP_RO,
21 context => PROP_RO, 17 _stash => PROP_RO,
22 template => PROP_RO, 18 id => {
23 parents => PROP_RO 19 get => sub { shift->_stash->get('id') },
20 set => sub { shift->_stash->set('id',shift) }
21 }
24 ] 22 ]
25 }; 23 };
26 24
27 25
28 { 26 {
32 } 30 }
33 } 31 }
34 32
35 our $AUTOLOAD_REGEX = qr/^[a-z]/; 33 our $AUTOLOAD_REGEX = qr/^[a-z]/;
36 34
37 my %mapSkipAttributes = map { $_, 1 } qw(attributes context);
38
39 sub CTOR { 35 sub CTOR {
40 my ($this,$template,$context,$attrs) = @_; 36 my ($this,$context,$template) = @_;
41 37
42 38 $this->context($context)
43 $this->template( $template ) or die new IMPL::ArgumentException("A template is required"); 39 or die ArgException->new(context => 'A context is required');
44 40 $this->template($template)
45 die IMPL::ArgumentException->new(context => "A context is required, supplied: $context") 41 or die ArgException->new(template => 'A template is required');
46 unless is($context,TTContext); 42
47 43 $this->_stash($context->stash);
48 $this->context( $context );
49
50 $this->attributes({});
51
52 if(ref($attrs) eq 'HASH') {
53 while (my($key,$value) = each %$attrs) {
54 next if $mapSkipAttributes{$key};
55 $this->SetAttribute($key,$value);
56 }
57 }
58
59 $this->id(_GetNextId()) unless $this->id;
60 }
61
62 sub GetAttribute {
63 my ($this,$name) = (shift,shift);
64
65 if (my $method = $this->can($name)) {
66 unshift @_,$this;
67 goto &$method;
68 } else {
69 return $this->attributes->{$name};
70 }
71 }
72
73 sub SetAttribute {
74 my $this = shift;
75 my $name = shift;
76
77 if (my $method = $this->can($name)) {
78 unshift @_, $this;
79 goto &$method;
80 } else {
81 return $this->attributes->{$name} = shift;
82 }
83 } 44 }
84 45
85 sub Render { 46 sub Render {
86 my ($this,$args) = @_; 47 my ($this,$args) = @_;
87 48 return $this->context->include($this->template,$args);
88 $args = {} unless ref $args eq 'HASH';
89
90 return $this->context->include(
91 $this->template->block,
92 {
93 %$args,
94 this => $this,
95 template => $this->template
96 }
97 );
98 } 49 }
99 50
100 sub GetTemplate { 51 our $AUTOLOAD;
101 my ($this,$name) = @_; 52 sub AUTOLOAD {
102 53 my ($prop) = ($AUTOLOAD =~ m/(\w+)$/);
103 return eval { $this->context->template($name) }; 54
55 die Exception->new("Control doesn't have method '$prop'") unless $prop=~/$AUTOLOAD_REGEX/;
56
57 no strict 'refs';
58
59 my $method = sub {
60 if (@_ == 1) {
61 return shift->_stash->get($prop);
62 } elsif (@_ == 2) {
63 return shift->_stash->set($prop,shift);
64 } else {
65 return shift->_stash->get([$prop,[@_]]);
66 }
67 };
68
69 *{$AUTOLOAD} = $method;
70
71 goto &$method;
104 } 72 }
105 73
106 sub Include {
107 my ($this,$template, $args) = @_;
108
109 my $tpl = $this->GetTemplate($template)
110 or die OperationException->new("The specified template isn't found", $template);
111
112 return $this->context->include(
113 $tpl,
114 $args
115 );
116 }
117
118 sub HasBlock {
119 my ($this,$block) = @_;
120
121 $this->GetTemplate ? 1 : 0;
122 }
123
124 sub AUTOLOAD {
125 our $AUTOLOAD;
126
127 my $method = ($AUTOLOAD =~ m/(\w+)$/)[0];
128
129 return if $method eq 'DESTROY';
130
131 if ($method =~ /$AUTOLOAD_REGEX/) {
132 my $this = shift;
133
134 die OperationException->new("can't invoke method '$method' on an unblessed reference") unless blessed $this;
135
136 return @_ ? $this->SetAttribute($method,@_) : $this->GetAttribute($method);
137 } else {
138 die OperationException->new("The specified method '$method' doesn't exists");
139 }
140 }
141
142 sub CreateControlFromTemplate {
143 my ($this,$template,$args) = @_;
144
145 if (not ref($template)) {
146 return $this->context->stash->get([
147 require => [
148 $template
149 ]
150 ])->new($args);
151 } else {
152 return $this->new(
153 $template,
154 $this->context->clone(),
155 $args
156 );
157 }
158 }
159 74
160 1; 75 1;
161 76
162 __END__ 77 __END__
163 78
164 =pod 79 =pod
165 80
166 =head1 NAME 81 =head1 NAME
167 82
168 C<IMPL::Web::View::TTControl> 83 C<IMPL::Web::View::TTControl> расширяет функциональность шаблонов
169 84
170 =head1 SYNPOSIS 85 =head1 SYNPOSIS
171 86
172 =begin text 87 =begin code
173 88
174 [% 89 package My::View::Menu;
175 META version = 1; 90 use IMPL::declare {
176 BLOCK INIT; 91 base => [
177 # this is a document scope 92 'IMPL::Web::View::TTControl' => '@_'
178 dojo.modules.push( 'dijit/form/Input' ); 93 ]
179 END; 94 };
180
181 # local to this block
182 TPreview = require('My/Org/TextPreview');
183
184 # init control props
185 visualClass = this.visualClass || 'classic';
186 %]
187 <div id="$id" class="$visualClass" data-dojo-type="dijit/form/Input">
188 [% FOREACH item IN model %]
189 <div class="itemContainer">
190 [% Display(item) %]
191 </div>
192 [% END %]
193 </div>
194 95
195 =end text 96 sub Render {
97 my ($this,$args) = @_;
98
99 $this->PrepareItems($args);
100
101 return $this->next::method($args);
102 }
103
104 sub PrepareItems
105
106 =end code
196 107
197 =head1 DESCRIPTION 108 =head1 DESCRIPTION
198 109
199 Легкая обертка вокруг шаблона, позволяет изолировать пространство имен шаблона,
200 а также реализовать собственные методы по представлению данных (в случае если
201 это проще сделать в виде методов класса).
202
203 =head2 BLOCKS
204
205 =head3 META
206
207 Атрибуты C<META> C<layout>, C<title> будут перенесены в свойства элемента
208 управления.
209
210 =head3 INIT
211
212 Данный блок шаблона управления выполняется один раз при создании первого
213 экземпляра элемента управления, в пространстве имен документа. Может
214 использоваться для формирования заголовочной части документа, скрипта
215 подключающего C<js> модули и т.п.
216
217 Выполнение данного блока производится фабрикой элементов управления.
218
219 =head2 TEMPLATE VARS
220
221 Каждый шаблон имеет собственное пространство имен, вложенное в пространство имен
222 фабрики элементов (которая разделяет пространство имен документа). В шаблоне
223 могут определяться новые переменные, однако они останутся локальными для блоков.
224
225 Чтобы передать данные между блоками следует использовать ссылку на элемент
226 управления C<this>.
227
228 =begin text
229
230 [%
231 BLOCK CTOR;
232 this.extraCssClass = 'active';
233 text = "this text will gone";
234 END;
235 %]
236
237 <div class="$this.extraCssClass">some text $text</div>
238
239 =end text
240
241 В примере выше переменная C<$text> установленная в конструкторе шаблона, при
242 отображении элемента управления будет неопределена.
243
244 =over
245
246 =item * C<this>
247
248 ссылка на объект элемента управления
249
250 =item * C<component>
251
252 ссылка на текущий шаблон, устанавливается автоматически в методе
253 C<Template::Context::process>.
254
255 =item * C<template>
256
257 ссылка на шаблон элемента управления, для совместимости с C<TT>
258
259 =back
260
261 =head1 MEMBERS
262
263 =head2 C<[get]context>
264
265 Контекст элемента управления, хранит пременные шаблона. Фабрика элементов
266 управления создает новый контекст пространство имен которого вложено в
267 пространство имен документа.
268
269 Контекст следует использовать только при рендеринге документа.
270
271 =head2 C<[get,set]template>
272
273 C<Template::Document> Шаблон элемента управления.
274
275 =head2 C<AUTOLOAD>
276
277 Для удобства работы с шаблоном, элементы управления предоставляю доступ к своим
278 свойствам через метод C<AUTOLOAD>. Имена свойств должны начинаться со строчной
279 буквы.
280 110
281 =cut 111 =cut