As of http://trac.agavi.org/changeset/1609, the output of the inner
layer is available via the variable $inner in templates. Also
available are $container for the current execution container and
$view for the template's view. Like with factories, the names of
these assigns ("inner", "container" and "view") can be configured
using the "assigns" parameter of a renderer.
Since the wrapping template now does not have to know the inner
layer's name anymore, the example can be simplified quite a bit.
Master.php:
<html>
<head><title>logs!</title></head>
<body>
<?php echo $inner; ?>
</body>
</html>
LiveSuccess.php:
<tbody>
<?php foreach($t['messages'] as $message): ?>
<tr>
<td class="name"><?php echo $message->getNick()->getNick();
?></td>
<td class="message"><?php echo $message->getMessage(); ?></td>
<td class="time"><?php echo $message->getMessageDate(); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
LiveSuccess.wrapper.php:
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<?php echo $inner; ?>
</table>
And the code that makes it happen:
public function executeHtml(AgaviRequestDataHolder $rd)
{
// remember: first, you should have a base view and call a method on
the parent to load the layout instead of doing it here.
// second, loadLayout() would be enough since "standard" is the
default layout
$this->loadLayout('standard');
// now we insert the wrapper template between the decorator and the
actual content layer
$i = $this->appendLayer($this->createLayer
('AgaviFileTemplateLayer', 'wrapper'), $this->getLayer('content'));
$i->setTemplate('LiveSuccess.wrapper');
}
Cheers,
David
Am 29.01.2007 um 14:02 schrieb David Zülke:
> Hi guys,
>
> I thought I'd give you a little example of what the new template
> layer system can do.
>
> We participated in the PHP Throwdown (http://www.phpthrowdown.com)
> and built an IRC bot using Agavi. It's called "Chuckwalla", contains
> a fully fledged IRC library that doesn't suck, has live logs, cool
> web interfaces etc and will be open sourced soon.
>
> One feature is the live logs. You click a channel, and it uses Ajax
> to refresh the contents, so you can follow the discussion in a
> channel. So for the first load, we need a full document, and for
> subsequent XMLHttpRequest, we just want the new messages.
>
> Here is the layout's configuration for output type "html":
> <layout name="standard">
> <layers>
> <layer name="content" class="AgaviFileTemplateLayer" />
> <layer name="decorator" class="AgaviFileTemplateLayer">
> <parameter name="template">Master</parameter>
> </layer>
> </layers>
> </layout>
>
> Let's assume this is our decorator template:
> <html>
> <head><title>logs!</title></head>
> <body>
> <?php echo $slots['content']; ?>
> </body>
> </html>
>
> And here is the content template (LiveSuccess.php):
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
> <tbody>
> <?php foreach($t['messages'] as $message): ?>
> <tr>
> <td class="name"><?php echo
> $message->getNick()->getNick(); ?></td>
> <td class="message"><?php echo $message->getMessage();
> ?></td>
> <td class="time"><?php echo $message->getMessageDate();
> ?></td>
> </tr>
> <?php endforeach; ?>
> </tbody>
> </table>
>
> Now we also have an output type for the AJAX calls, called "json".
> It's configured like this:
> <layout name="standard">
> <layers>
> <layer name="content" class="AgaviFileTemplateLayer" />
> </layers>
> </layout>
>
> But now we have a problem: If an AJAX call is made, we get back the
> entire inner content template, complete with the <divs> and the
> <table>, but we only want the <tbody> to append to the current table
> (remember, tables can have multiple <tbody> elements).
>
> One approach would be to simply have two templates. The other
> approach would be to split them up and include() the inner portion in
> the non-ajax version, and set the templates differently per output
> type.
>
> However, there also is a third approach. Right now, we have these
> layers:
> +------------------------+
> | <html> decorator |
> | +--------------------+ |
> | | <table> content | |
> | | with <tbody>, <tr> | |
> | +--------------------+ |
> | </html> |
> +------------------------+
>
> What we can do now is split up the template like we would with the
> include() solution:
> +------------------------+
> | <html> decorator |
> | +--------------------+ |
> | | <table> wrapper | |
> | | +----------------+ | |
> | | | <tbody> inner | | |
> | | | content | | |
> | | +----------------+ | |
> | | </table> | |
> | +--------------------+ |
> | </html> |
> +------------------------+
>
>
> First, the "wrapper" template (LiveSuccess.wrapper.php):
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
> <?php echo $slots['inner']; ?>
> </table>
>
> And the actual "inner" content template (LiveSuccess.php):
> <tbody>
> <?php foreach($t['messages'] as $message): ?>
> <tr>
> <td class="name"><?php echo $message->getNick()->getNick();
> ?></td>
> <td class="message"><?php echo $message->getMessage(); ?></td>
> <td class="time"><?php echo $message->getMessageDate(); ?></td>
> </tr>
> <?php endforeach; ?>
> </tbody>
>
> For Ajax, everything is fine now. We'll get the <tbody> content. But
> for output type "html", the result will be this:
> <html>
> <head><title>logs!</title></head>
> <body>
> <tbody>
> <tr>
> <td class="name">John McClane</td>
> <td class="message">Yippie-kay-yay motherfucker</td>
> <td class="time">03:27</td>
> </tr>
> </tbody>
> </body>
> </html>
>
> What we need now is insert the wrapper IN BETWEEN "decorator" and
> "content" layer.
>
> Remember that the decorator template expects to output $slots
> ['content']. Hence, we actually have to modify the "content" layer to
> display the wrapper template (LiveSuccess.wrapper.php), and then
> prepend another "inner" layer (LiveSuccess.php) to the layer list. We
> do it like this, in the view:
>
> public function executeHtml(AgaviRequestDataHolder $rd)
> {
> // remember: first, you should have a base view and call a method on
> the parent to load the layout instead of doing it here.
> // second, loadLayout() would be enough since "standard" is the
> default layer
> $this->loadLayout('standard');
>
> // we get the "content" layer and change the template to the wrapper
> $c = $this->getLayer('content');
> $c->setTemplate('LiveSuccess.wrapper');
>
> // and then prepend the actual "inner" content template to the list
> $i = $this->prependLayer($this->createLayer
> ('AgaviFileTemplateLayer', 'inner'));
> $i->setTemplate('LiveSuccess');
> }
>
> And that's it! Now we have the desired result:
> <html>
> <head><title>logs!</title></head>
> <body>
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
> <tbody>
> <tr>
> <td class="name">John McClane</td>
> <td class="message">Yippie-kay-yay motherfucker</td>
> <td class="time">03:27</td>
> </tr>
> </tbody>
> </table>
> </body>
> </html>
>
>
> IMPORTANT: Some advice regarding base views. In the original email, I
> recommended that you always have a base view you extend from where
> execute() throws an exception. This view would also have base methods
> you can call that load layers, maybe set dynamic slots, or do stuff
> like:
> $this->setAttribute('_contentType', $this->container->getOutputType()-
>> getParameter('Content-Type'));
>
> However, there is a problem here. The whole point of having execute()
> throw an exception is that if there is a request using, say, ajax,
> that sets the output type to, say, "json" because of a routing rule
> like this:
> <route pattern="^XMLHttpRequest$" source="_SERVER
> [HTTP_X_REQUESTED_WITH]" stop="false" output_type="json" />
>
> And you then implement a base executeJson() in the base view... all
> of your views DO serve the json output type, and the normal execute()
> is never called, not even for actions/views that don't implement ajax
> features and should thus get an exception!
>
> To solve that problem, name these base methods differently. I suggest
> you call them "setupHtml", "setupJson" and so on, and then call them
> using $this->setupHtml($rd); etc inside your concrete view's
> executeHtml() method.
>
> Again, if there are any questions, let me know.
>
> Cheers,
>
> David
>
> _______________________________________________
> users mailing list
> [email protected]
> http://lists.agavi.org/mailman/listinfo/users
>
_______________________________________________
users mailing list
[email protected]
http://lists.agavi.org/mailman/listinfo/users