n:ifcontent does not work with empty tags

bernhard
Member | 40
+
0
-

Please see this fiddle:

https://fiddle.nette.org/latte/#…

This behaviour is a problem for me! The background is that I'm using the ProcessWire CMS and that CMS has the capability to directly edit fields on the frontend:

https://i.imgur.com/A0rEt88.gif

This is great, but that means that the CMS injects some additional markup into the template.

So a simple <h1>My Headline</h1> gets something like <h1><span class='pw-edit-copy'>My Headline</span><span class='pw-edit-orig'>My Headline</span></h1>

That means that n:ifcontent does not work on these tags, which is annoying. I can use n:if instead and make sure that I return the raw value of the field there, but that's also a little annoying.

Could there be something like n:iftext ? I'd love to build such a feature on my own, but unfortunately I didn't understand the docs regarding how to create custom tags :(

Any pointers would be great! Thx in advance!

Infanticide0
Member | 60
+
0
-

<span> in <p> is content, I expect this behaviour.
n:iftext would be very useful, probably easy to implement (copy&paste n:ifcontent tag class with some changes)

<p n:ifcontent><span n:ifcontent></span></p>
<p n:ifcontent><span n:tag-if="$var">{$var}</span></p>
bernhard
Member | 40
+
0
-

Hey @Infanticide0 yeah I agree that markup is content – <svg> for example would also not be text, but markup and so the tag should appear. I just tried to explain my use case, but the headline is a little misleading I guess, sorry.

I'm quite experienced with PHP but it's not easy for me to copy/paste IfContentNode.php – I'd really appreciate a step by step tutorial. The docs unfortunately don't explain it well enough for me :(

Infanticide0
Member | 60
+
0
-

@bernhard

I've never done a Latte extension, it will probably need some tuning. Still don't understand it completely.
Hope this helps.

template:

+)<p class="keep" n:ifcontent><a href="#"><u>latte</u></a><b></b></p><br>
-)<p class="hide" n:ifcontent></p><br>
+)<p class="keep" n:iftext><a href="#"><u>latte</u></a><b></b></p><br>
-)<p class="hide" n:iftext><a href="#"><u></u></a><b></b></p><br>
+)<p class="keep" n:iftext><a href="#"><u></u></a><b>nette</b></p><br>

output:

+)<p class="keep"><a href="#"><u>latte</u></a><b></b></p><br>
-)<br>
+)<p class="keep"><a href="#"><u>latte</u></a><b></b></p><br>
-)>nothing<<br>
+)<p class="keep"><a href="#"><u></u></a><b>nette</b></p><br>
class MyExtension extends \Latte\Extension
{
    public function getTags(): array
    {
        return [
            "n:iftext" => [IfTextNode::class, 'create']
        ];
    }
}

/**
 * n:iftext
 */
class IfTextNode extends StatementNode
{
    public AreaNode $content;
	public int $id;
	public ElementNode $htmlElement;


	/** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
	public static function create(Tag $tag, TemplateParser $parser): \Generator
	{
		$node = $tag->node = new static;
		$node->id = $parser->generateId();
		[$node->content] = yield;
		$node->htmlElement = $tag->htmlElement;
		if (!$node->htmlElement->content) {
			throw new CompileException("Unnecessary n:ifcontent on empty element <{$node->htmlElement->name}>", $tag->position);
		}

        return $node;
    }

    /** @return bool TRUE = keep, FALSE = remove */
    private function iterateNode(Node $node): bool {
        if($node instanceof TextNode)
            return trim($node->content) !== "";

        if(!($node instanceof ElementNode))
            return true;

        if($node->content instanceof Node) {
            foreach ($node->content as $key => $value) {
                if($this->iterateNode($value)) {
                    return true;
                }
            }
        }
        return false;
    }

	public function print(PrintContext $context): string
	{
        $saved = $this->htmlElement->content;
        $keep = $this->iterateNode($this->htmlElement);
        if($keep)
        {
            $this->htmlElement->content = new AuxiliaryNode(fn() => <<<XX
				ob_start();
				try {
					{$saved->print($context)}
				} finally {
					\$ʟ_ift[$this->id] = rtrim(ob_get_flush()) === '';
				}

				XX);

            return <<<XX
				ob_start(fn() => '');
				try {
					{$this->content->print($context)}
				} finally {
					if (\$ʟ_ift[$this->id] ?? null) {
						ob_end_clean();
					} else {
						echo ob_get_clean();
					}
				}
				XX;
        } else {
            return "echo '>nothing<';"; // TODO ""
        }
	}

	public function &getIterator(): \Generator
	{
		yield $this->content;
	}
}
David Grudl
Nette Core | 8142
+
+2
-

I think it is enough to copy the IfContentNode class and replace rtrim(ob_get_flush()) with rtrim(strip_tags(ob_get_flush()))

bernhard
Member | 40
+
0
-

@DavidGrudl thx but I'm getting this error:

Unexpected attribute n:iftext, did you mean n:ifset? (in ‘…/sections/footer.latte’ on line 118 at column 35)

Any chance to add n:iftext to latte globally?

Infanticide0
Member | 60
+
+1
-

@bernhard

update your app config .neon file (usually common.neon)

latte:
	strictTypes: yes
	extensions:
		- App\MyExtension // register your extension