pugjs / pug

Pug – robust, elegant, feature rich template engine for Node.js
https://pugjs.org
21.68k stars 1.95k forks source link

Feature Request: Test for block content #1911

Open fusepilot opened 9 years ago

fusepilot commented 9 years ago

I would like to be able to have defined blocks show only if they have content provided to them. Something like:

if block features
    section.features
        h2 Features
        .content
            block features

if block related
    section.related
        h2 Related Products
        .content
            block related

I'm currently working around this by using jade-lexer and jade-parser to find all defined NamedBlocks and provide them as locals in a object called blocks. I can use this like:

if blocks.features
    section.features
        h2 Features
        .content
            block features

if blocks.related
    section.related
        h2 Related Products
        .content
            block related

Not really ideal. It seems like you should be able to check if the blocks have been defined out of box.

TimothyGu commented 9 years ago

It would be difficult to implement this in practice, since if is always translated to an if () {} in runtime JS code, but blocks for extend are interpreted at compile time.

This differs from mixins, which are literally wrapped JavaScript functions. When you +mixinName('adsf'), you are actually calling a function at render time, so you can check if block (→if (block)) at render time.

fusepilot commented 9 years ago

Sure, I didn't mean to imply that the final syntax should be exactly as I wrote above. Whatever is appropriate to achieve the resulting functionallity.

And are you suggesting there's a clean way to do this with mixins? If so, I'm not seeing how without parsing the AST at runtime. Which seems a bit much to achieve this simple 'check'.

TimothyGu commented 9 years ago

And are you suggesting there's a clean way to do this with mixins?

No. mixins are simply not suited for this purpose.

I also toyed with the idea of wrapping the extend block with a mixin (like this:

mixin related
  if block
    section.related
      h2 Related Products
      .content
        block

html
  body
    +related
      block related

but that didn't work out since block is always defined in the mixin and no return code is emitted.

If so, I'm not seeing how without parsing the AST at runtime. Which seems a bit much to achieve this simple 'check'.

I agree.

TimothyGu commented 9 years ago

Just for fun, I did finally get something extremely ugly and hacky working (DO NOT USE THIS):

//- layout.jade
doctype html

mixin optional(cl, desc)
  //- replace proper buffer with a dummy one to test if block is empty or not
  - var oldBuf = buf
  - buf = []
  block
  - var blockContent = buf.join('').trim()
  - buf = oldBuf
  if blockContent
    section(class=cl)
      h2= desc
      .content
        block

html
  body
    +optional('features', 'Features')
      block features
    +optional('related', 'Related Products')
      block related
//- extend.jade

extends layout.jade

block features
  ul
    li Supports Jade templating engine.
    li Blazing-fast.
<!DOCTYPE html>
<html>
  <body>
    <section class="features">
      <h2>Features</h2>
      <div class="content">
        <ul>
          <li>Supports Jade templating engine.</li>
          <li>Blazing-fast.</li>
        </ul>
      </div>
    </section>
  </body>
</html>

But it's subpar and uses knowledge of Jade internals, so do NOT use this.

TimothyGu commented 9 years ago

So, Jade is extremely opinionated, which contributes to its prettiness in templates that fit its standards and extreme ugliness otherwise.

In real world, you might want to just put the headings and .content into the block itself. It will save you a lot of hassle.

fusepilot commented 9 years ago

Interesting. Thank you for the insight.

Will 2.0 implement anything that would perhaps make this more conise/easier?

Also, just to ensure clairity, I'm only looking for an functional equivalent to Rail's content_for?.

http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for-3F http://stackoverflow.com/questions/193838/rails-check-if-yield-area-is-defined-in-content-for

ForbesLindesay commented 9 years ago

We could do something like this without too much difficulty. This is the reverse problem of dynamic include. i.e. it is impossible to use a value that is only known at runtime and then use it to change behaviour at compile time. It is possible to take a value that is only known at compile time, and make that available at runtime though.

We could, for example, take a regexp like /\bblock ([a-zA-Z_0-9]+)\b/ which would never match any valid JavaScript code, and replace all occurrences of that with either true or false depending on the child templates. This could be done at compile time. This would certainly be a fun/interesting academic exercise.

What I'm less sure of is whether this is a good idea. We do support multiple inheritance, which seems like it should cover this use case quite nicely. i.e. instead of:

doctype html
html
  if block features
    section.features
      h2 Features
      .content
        block features

  if block related
    section.related
      h2 Related Products
      .content
        block related

just do:

doctype html
html
  block content
extends ./layout.jade
block content
  section.features
    h2 Features
    .content
      block features
extends ./layout.jade
block content
  section.related
    h2 Related Products
    .content
      block related

Then just extend either layout-features.jade or layout-related.jade as appropriate.

fridays commented 8 years ago

Hey guys. I am new to Jade but this mixin/block combination worked out for me when trying to wrap a block only if it has contents:

// mixins.jade
mixin content
    if block
        .content-wrapper
            block

// layout.jade
doctype
html
    block content

// example.jade
extends layout
block content
    +content
        p This will be wrapped.
TimothyGu commented 8 years ago

@fridays, no, we are talking about detecting inheritance blocks here (i.e. if block content in your example, in layout.jade).

meodai commented 7 years ago

any news on this? This would be so helpful. Would avoid a lot of file overhead

olivmonnier commented 7 years ago

As temporary solution, maybe should use mixin like that :

mixin layout(isHeader, isContent, isFooter)
  if isHeader
    block header
  if isContent
    block content
  if isFooter
    block footer
+layout(true, true, false)
  block header
    h1 Hello
  block content
    p hi