view Lib/IMPL/Web/View/TTControl.pm @ 310:0a9d51cf6dfd

*TTView: refactoring, document supports custom classes, layouts are become controls
author sergey
date Fri, 19 Apr 2013 16:39:01 +0400
parents 2da2564f115d
children 109f28643025
line wrap: on
line source

package IMPL::Web::View::TTControl;
use strict;

use IMPL::Const qw(:prop);
use IMPL::lang qw(:hash);
use Scalar::Util qw(blessed reftype);
use IMPL::declare {
	require => {
	    TemplateDocument => 'Template::Document',
        TTContext => 'Template::Context',
        Exception => 'IMPL::Exception',
        ArgumentException => '-IMPL::InvalidArgumentException',
        OperationException => '-IMPL::InvalidOperationException'
	},
	base => [
	   'IMPL::Object' => undef
	],
	props => [
	   id => PROP_RO,
	   attributes => PROP_RW,
	   context => PROP_RO,
	   template => PROP_RO
	]
};


{
    my $nextId = 1;
    sub _GetNextId {
        return '_' . $nextId++;
    }
}

our $AUTOLOAD_REGEX = qr/^[a-z]/;

sub CTOR {
    my ($this,$template,$context,$attrs) = @_;
    
   
    $this->template( $template ) or die new IMPL::ArgumentException("A template is required");
    $this->context( $context ) or die new IMPL::ArgumentException("A context is required");
    
    $this->attributes({});
    
    if(ref($attrs) eq 'HASH') {
	    while (my($key,$value) = each %$attrs) {
	        $this->SetAttribute($key,$value);
	    }
    }    
    
    $this->id(_GetNextId()) unless $this->id;
}

sub GetAttribute {
    my ($this,$name) = (shift,shift);
    
    if (my $method = $this->can($name)) {
        unshift @_,$this;
        goto &$method;
    } else {
        return $this->attributes->{$name};
    }
}

sub SetAttribute {
    my $this = shift;
    my $name = shift;
    
    if (my $method = $this->can($name)) {
        unshift @_, $this;
        goto &$method;
    } else {
        return $this->attributes->{$name} = shift;
    }
}

sub Render {
    my ($this,$args) = @_;
    
    $args = {} unless ref $args eq 'HASH';
    
    return $this->context->include(
        $this->template->block,
        {
            %$args,
            this => $this,
            template => $this->template
        }
    );
}

sub GetTemplate {
    my ($this,$name) = @_;
    
    return eval { $this->context->template($name) }; 
}

sub Include {
    my ($this,$template, $args) = @_;
    
    my $tpl = $this->GetTemplate($template)
        or die OperationException->new("The specified template isn't found", $template);
        
    return $this->context->include(
        $tpl,
        $args
    );
}

sub AUTOLOAD {
    our $AUTOLOAD;
    
    my $method = ($AUTOLOAD =~ m/(\w+)$/)[0];
    
    return if $method eq 'DESTROY';
    
    if ($method =~ /$AUTOLOAD_REGEX/) {
        my $this = shift;
        
        die OperationException->new("can't invoke method '$method' on an unblessed reference") unless blessed $this;
    
        return @_ ? $this->SetAttribute($method,@_) : $this->GetAttribute($method);
    } else {
        die OperationException->new("The specified method '$method' doesn't exists");
    }
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Web::View::TTControl>

=head1 SYNPOSIS

=begin text

[%
    META version = 1;
    BLOCK INIT;
        # this is a document scope
        dojo.modules.push( 'dijit/form/Input' );
    END;
    
    # local to this block
    TPreview = require('My/Org/TextPreview');
        
    # init control props 
    visualClass = this.visualClass || 'classic';
%]    
<div id="$id" class="$visualClass" data-dojo-type="dijit/form/Input">
    [% FOREACH item IN model %]
        <div class="itemContainer">
        [% Display(item) %]
        </div>
    [% END %]
</div>

=end text

=head1 DESCRIPTION

Легкая обертка вокруг шаблона, позволяет изолировать пространство имен шаблона,
а также реализовать собственные методы по представлению данных (в случае если
это проще сделать в виде методов класса). 

=head2 BLOCKS

=head3 META

Атрибуты C<META> C<layout>, C<title> будут перенесены в свойства элемента
управления. 

=head3 INIT

Данный блок шаблона управления выполняется один раз при создании первого
экземпляра элемента управления, в пространстве имен документа. Может
использоваться для формирования заголовочной части документа, скрипта
подключающего C<js> модули и т.п.

Выполнение данного блока производится фабрикой элементов управления.

=head2 TEMPLATE VARS

Каждый шаблон имеет собственное пространство имен, вложенное в пространство имен
фабрики элементов (которая разделяет пространство имен документа). В шаблоне
могут определяться новые переменные, однако они останутся локальными для блоков.

Чтобы передать данные между блоками следует использовать ссылку на элемент
управления C<this>.

=begin text

[%
    BLOCK CTOR;
        this.extraCssClass = 'active';
        text = "this text will gone";
    END;
%]

<div class="$this.extraCssClass">some text $text</div>

=end text

В примере выше переменная C<$text> установленная в конструкторе шаблона, при
отображении элемента управления будет неопределена.

=over

=item * C<this>

ссылка на объект элемента управления

=item * C<component>

ссылка на текущий шаблон, устанавливается автоматически в методе
C<Template::Context::process>.

=item * C<template>

ссылка на шаблон элемента управления, для совместимости с C<TT>

=back

=head1 MEMBERS

=head2 C<[get]context>

Контекст элемента управления, хранит пременные шаблона. Фабрика элементов
управления создает новый контекст пространство имен которого вложено в
пространство имен документа.

Контекст следует использовать только при рендеринге документа.

=head2 C<[get,set]template>

C<Template::Document> Шаблон элемента управления.

=head2 C<AUTOLOAD>

Для удобства работы с шаблоном, элементы управления предоставляю доступ к своим
свойствам через метод C<AUTOLOAD>. Имена свойств должны начинаться со строчной
буквы.

=cut