view Lib/IMPL/Web/View/TTDocument.pm @ 190:cd1ff7029a63

IMLP::Web::View refactored, added new method 'require' which is available inside templates. Changed document rendering.
author cin
date Wed, 04 Apr 2012 17:51:27 +0400
parents 08015e2803f1
children 78a18a2b6266
line wrap: on
line source

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

use IMPL::lang qw(:declare :constants);
use IMPL::DOM::Property qw(_dom);
use IMPL::Web::View::TTFactory();
use IMPL::Web::View::TTControl();

use Scalar::Util qw(weaken);


use parent qw(
	IMPL::DOM::Document
	IMPL::Web::View::TTControl
);

BEGIN {
	public _dom property layout => PROP_ALL;
	public property opts => PROP_GET | PROP_OWNERSET;
	public property loader => PROP_GET | PROP_OWNERSET;
	public property controls => PROP_GET | PROP_OWNERSET;
	
	# store the stash separately to make require() method to work correctly
	# even when a stash of the context is modified during the processing 
	public property stash => PROP_GET | PROP_OWNERSET; 
}

sub CTOR {
	my ($this,$template,$refOpts,%args) = @_;
	
	$this->controls({});
	$this->loader($args{loader}) if $args{loader};
	
	$this->layout( $template->layout ) unless $this->layout;
	
	$this->opts($refOpts);
	$this->stash($this->context->stash);
	
	my $self = $this;
	weaken($self);
	
	$this->templateVars('require', sub {
		my $doc = $self;
		die new IMPL::Exception("A document is destroyed or invalid") unless $doc;
		$doc->require(@_);
	});
	
	$this->templateVars('document', sub { $self } );
}

our %CTOR = (
	'IMPL::Web::View::TTControl' => sub {
		'document',
		$_[0], # template
		new Template::Context($_[1])  # context
	},
	'IMPL::DOM::Document' => sub {
		nodeName => 'document'
	}
);

sub templateVars {
	my $this = shift;
	my $name = shift;
	
	if (@_) {
		return $this->stash->set($name, shift);		
	} else {
		return $this->stash->get($name);
	}
}

sub require {
	my ($this, $control) = @_;
	
	if (my $factory = $this->controls->{$control}) {
		return $factory;
	} else {
	
		my $path = $control;
		if ( my $template = $this->loader->template($path) ) {

			my $opts = { %{$this->opts} };
			$opts->{STASH} = $this->stash->clone();
 
			my $ctx = new Template::Context($opts);
			
			$factory = new IMPL::Web::View::TTFactory(
				typeof IMPL::Web::View::TTControl,
				$template,
				$ctx,
				$opts
			);
			
			my @parts = split(/\/+/,$control);
			
			$this->controls->{$control} = $factory;
						
			return $factory;

		} else {
			die new IMPL::KeyNotFoundException($control);
		}
	}
}

sub renderBlock {
	$_[0]->template;	
}

sub Render {
	my ($this,$args) = @_;
	
	my $output;
	
	if ($this->layout) {
		$output = $this->context->include(
			$this->layout,
			{
				content => sub { $output ||= $this->RenderContent($args); }
			}
		);
	} else {
		return $this->RenderContent($args);
	}
	
	return $output;
}

sub RenderContent {
	my $this = shift;
	return $this->SUPER::Render(@_);
}


1;

__END__

=pod

=head1 NAME

C<IMPL::Web::View::TTDocument> - документ для построения HTML страницы на основе шаблонов TT.

=head1 SYNOPSIS

=begin code

use IMPL::Web::View::TTDocument();

my $doc = new IMPL::Wbe::View::TTDocument($template,$ttOptions);

return $doc->Render();

=end code

Однако, более предпочтительный способ использовать C<IMPL::Web::View::TTLoader>.

=head1 DESCRIPTION

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

=over

=item * Объекты из БД

=item * Навигационные цепочки

=item * Меню и т.п. 

=back

Скприт шаблона формирует структуру документа, затем сформированная структура форматируется в готовый документ.
Процесс преобразования объектной модели в готовый документ может быть выполнена как вручную, так и при помощи
вспомогательного шаблона - обертки. Если у шаблона документа указан C<layout> в метаданных, то он будет
использован как шаблон для форматирования объектной модели, вывод самого шаблона будет проигнорирован. Если
обертка не задана, то результатом будет вывод самого скрипта шаблона.


=head2 Порядок обработки документа

=over

=item 1 Создается документ при помощи метода C<TTLoader::document()>

=item 1 При создании документа (в конструкторе), происходит выполнение блока C<CTOR>

=item 1 При вызове метода C<TTDocument::Render()> устанавливаются переменные C<this>, C<document>
и шаблон обрабатывается при помощи метода C<process()> контекста документа.

=back

=head2 Загрузка элемента управления

=over

=item 1 C<require('my/org/input')>

=item 1 Загружает шаблон C<my/org/input.tt>

=item 1 Создает фабрику элементов управления с собственным контекстом, унаследованным от контекст документа.

=item 1 Выполняет шаблон в пространстве имен фабрики

=back

=head2 Создание элемента управления

=over

=item 1 C<< my.org.input.new('login') >>

=item 1 Если это первый элемент управления, то выполняетя статический конструктор в контексте фабрики

=item 1 Создается новый дочерний контекст к контексту фабрики

=item 1 Создается экземпляр элемента управления

=item 1 Выполняется блок конструктора в контексте элемента управления, параметр C<this> имеет значение
нового экземпляра элемента управления  

=back

=head1 MEMBERS

=over

=item C<CTOR($template, %options)>

Создает экземпляр документа с указанным шаблоном и параметрами, параметры 

=back

=cut