Talesoft / tale-jade

A complete and fully-functional implementation of the Jade template language for PHP

Home Page:http://jade.talesoft.codes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can't use same layout multiple times in same template.

strobox opened this issue · comments

Simple as possible example (tested in last (1.4.2) and some previous version theat I'm used also):

index.jade (file that we will be rendered as top template):

doctype html5
html(lang='ru', dir='ltr')
  head
    title Problem demonstration
  body
    include section_intro
    include section_second
    include section_third

All three include directives used for include templates that exdends same layout site_section_layout.jade

Content of site_section_layout.jade

section
  h2 Header of any section. 
  h3 "site_section_layout.jade" used
  block site_section_content
    | will be overriden

Content of one of sections, for example section_second.jade

extends site_section_layout
block site_section_content
  p content for setion "SECOND"

And others are differs only in content:

extends site_section_layout
block site_section_content
  p content for setion "INTRO"
extends site_section_layout
block site_section_content
  p content for setion "THIRD"

When trying to render it, all three templetes renders in top template but only one have content:

<body>
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
      
      <p>
        content for setion "THIRD"
      </p>
    </section>
    
    
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
    </section>
    
    
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
    </section>
</body>

And I'm exptected that result:

<body>
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
      
      <p>
        content for setion "INTRO"
      </p>
    </section>
    
    
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
      <p>
        content for setion "SECOND"
      </p>
    </section>
    
    
    <section>
      <h2>
        Header of any section.
      </h2>
      <h3>
        "site_section_layout.jade" used
      </h3>
      <p>
        content for setion "THIRD"
      </p>
    </section>
</body>

This happens because Block-names are pretty unique over the whole Compiler instance of Tale Jade.

In this case you're actually overwriting the block "site_section_content" inside "site_section_layout.jade" 3 times.

The first "block" will always define the target location of the block while all calls to the same block after that, regardless of its location, will use the mode given (replace, append, prepend) and change only the first position.

This mostly has performance reasons, not every Jade file is an instance on its own, the Compiler holds the context and information about the blocks.

Well, you have multiple possibilities to solve this, most are suggesting a different structure to use.

The best one (I assume you want to create some kind of module/widget/block system) would be pre-rendering the blocks, as a new compiler context is created with each call to Compiler->compile or Compiler->compileFile(Not Compiler->compileNode, though, which is what handles included and extended nodes)

That's actually the same way I use it when I need to do this kind of stuff.
It's also more dynamic (e.g. pull the blocks from a database!)

whatever.php

$sectionContents = [];
$sections = ['section_intro', 'section_second', 'section_third'];

foreach ($sections as $section)
    $sectionContents[$section] = $renderer->render($section);

echo $renderer->render('site_section_layout', ['sectionContents' => $sectionContents]);

site_section_layout.php

doctype html5
html(lang='ru', dir='ltr')
  head
    title Problem demonstration
  body
    each $content in $sectionContents
        != $sectionContents

This is actually a pretty good point for me to think about having each template as its own instance and sharing blocks on demand. I'll think about it.

1.5 already contains some kind of Scope mechanism with the State-classes. I guess I could even realize dynamic block names this way.

Yours solution almost as mine:
(First, in some global entry script: index.php)
global $renderer; $renderer = new Jade\Renderer(array(...... initialization skiped .....
(Second, anywhere when multiply layout implementation needed: index.jade)

extends layout
block page
  - global $renderer;
  - echo $renderer->render('blocks/heading')
  - echo $renderer->render('blocks/history')
  - echo $renderer->render('blocks/schedulle')
  - echo $renderer->render('blocks/dress')
  - echo $renderer->render('blocks/contact')
  - echo $renderer->render('blocks/friends')
  - echo $renderer->render('blocks/friends2')
  - echo $renderer->render('blocks/gallery')
  - echo $renderer->render('blocks/blog')

Of course it's temp solution before 1.5, because cache will not work in this case.

The cache works, even better than you might expect, since the contents are completely cached within one single file at the end.

Caching + cross-include is pretty hard, since it's a mess of path-juggling and you mostly want a consistent cache-structure to be able to manage it easily (cleaning, partial cleaning, selective cleaning)

You might think I could just let it use an actual Tale Jade renderer inside again, but that would break the standAlone mode.

I tried to have the least possible dependencies inside a compiled Jade-template so that you can use compiled templates even without the Tale Jade codebase (standAlone mode generates PHP files that you can drop directly on your webspace, without any other dependencies, it's pretty undocumented right now :D)


A little side-note: Please, don't use global ;( I cried a little inside. Pass it as an argument to the Jade-file. You can just pass the renderer.

$renderer->render('base', ['renderer' => $renderer]);

or even just define a small helper function

$renderer->render('base', ['render' => function($path, array $args = null) use ($renderer) {

    return $renderer->render($path, $args);
}]);

Then you could do

block page
    != $render('blocks/heading')
    != $render('blocks/history')
    //...etc...

Looks way better, doesn't it?
It also protects your scopes :)

Thanks for good advice and good explanation! :-)