TYPO3 Fluid - Exit a for loop - typo3

Is it somehow possible to break out of a for loop in fluid?
<f:for each="{subItem.image}" as="imageItem">
<f:if condition="{selectedCategory} == {subItem.imagecategory}">
Do Stuff
Exit loop
</f:if>
</f:for>
I need to loop trough a couple of images then render one if the category matches and exit the loop after that since I only want to render the first image with the matching category.

You can use Fluid variable view helper:
<f:variable name="imageRendered" value="0" />
<f:for each="{subItem.image}" as="imageItem">
<f:if condition="{selectedCategory} == {subItem.imagecategory} && {imageRendered} == 0">
Do Stuff
Exit loop
<f:variable name="imageRendered" value="1" />
</f:if>
</f:for>

Maybe you can use a dataprocessor to give you an array with just the images in the category, and then you can just grab the first image with {imageItem.0}.
Have a look at TYPO3\CMS\Frontend\DataProcessing\FilesProcessor.

Related

How do I solve a strange problem with VHS ViewHelper v:replace?

I have an array ('db-titles') whose items are strings composed in the form 'TextA*TextB'.
I want to create the following HTML structure from these array items: <span>TextA</span><span>TextB</span>
For this I use the following fluid script:
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
<f:if condition="{db-titles -> f:count()} > 1">
<f:then>
<!-- This works as expected, the html code will be rendered correctly -->
<v:iterator.for from="0" to="{db-titles -> f:count()}" iteration="i">
<span><v:format.replace content="{db-titles.{i.index}}" substring="*" replacement="</span><span>"/></span>
</v:iterator.for>
</f:then>
<f:else>
<!-- This strangely does not work, although I - instead of running through all values of the array - just want to output the first value of it.... -->
<span><v:format.replace content="{v:iterator.first(haystack: db-titles)}" substring="*" replacement="</span><span>"/></span>
</f:else>
</f:if>
As I commented in the source code, everything works as it should under '<f:then>', but under '<f:else>', where I only want to use the first value of the array, the replaced part ('*' to </span><span>) is strangely rendered as text instead of HTML: <span>TextA</span><span>TextB</span>
How can this be?
Let me mention that I use the app "PHPStorm" for programming - is it possible that the program renders the source code wrong?
Thanks in advance for any help!
Without having found an explanation for the strange behaviour, I have found an solution: wrapping <v:format.replace ... /> with <f:format.raw> ... </> prevents that the tags converted to strings.
<f:if condition="{db-titles -> f:count()} > 1">
<f:then>
<v:iterator.for from="0" to="{db-titles -> f:count()}" iteration="i">
<span><f:format.raw><v:format.replace content="{db-titles.{i.index}}" substring="*" replacement="</span><span>"/></f:format.raw></span>
</v:iterator.for>
</f:then>
<f:else>
<span><f:format.raw><v:format.replace content="{v:iterator.first(haystack: db-titles)}" substring="*" replacement="</span><span>"/></(f:format.raw></span>
</f:else>
</f:if>

Want to change a logo with an if/then condition

I want to select a logo depending on a check box of a page property I have added via extension. The variable selectcompany is shown correctly when dumping the variables.
I am running Typo3 8 LTS.
obj.logo {
<f:if condition="{field:selectcompany}==1">
<f:then>
file = fileadmin/template/private/images/Logo1.png
</f:then>
<f:else>
file = fileadmin/template/private/images/Logo0.png
</f:else>
</f:if>
}
Although the variable is set correctly, always Logo0.png is displayed. When the variable is set to 1, I expected Logo1.png.
If this is a TypoScript snippet, you can not fill in a Fluid condition there, but have to make use of the TypoScript variant of an if condition.
https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/If.html
On TypoScript, it is possible to use a condition (TYPO3 9.5.x syntax):
[page["selectcompany"] == 1]
obj.logo.file= fileadmin/Images/logo1.png
[GLOBAL]
Else, you could solve with fluid templating; it should be just:
<f:if condition="{data.selectcompany}">
<f:then>
<f:image src="fileadmin/template/private/images/Logo1.png" alt="" />
</f:then>
<f:else>
<f:image src="fileadmin/template/private/images/Logo0.png" alt="" />
</f:else>
</f:if>
I used <f:image> but I guess you could also go with a plain <img> tag, too.

In a TYPO3 template, v:variable.set works... but f:variable doesn't

The problem
I have customized something in a file overriding this news extension file Templates/Styles/Twb/Templates/News/List.html. This was working...
<v:variable.set name="special.category" value="1" />
<f:if condition="{special.category}">
<f:then>
Do something for the special category when the variable's boolean value is 1
</f:then>
<f:else>
Otherwise do something else
</f:else>
</f:if>
But when I tried to change it to this, it no longer works...
<f:variable name="special.category" value="1" />
<f:if condition="{special.category}">
<f:then>
Do something for the special category when the variable's boolean value is 1
</f:then>
<f:else>
Otherwise do something else
</f:else>
</f:if>
Some documentation
https://docs.typo3.org/typo3cms/extensions/core/Changelog/8.6/Feature-79402-VariableViewHelperForFluid.html
https://fluidtypo3.org/viewhelpers/vhs/master/Variable/SetViewHelper.html
My platform
I am using TYPO3 9.5.3 but would also like to know the answer for 8.7.
I think its the dot in the variable name. Maybe vhs:variable is implemented different in this case. Maybe <f:variable name="special" value="{category: 1}"/> this works?

Clean way to switch between typo3 fluid page and action link

Is there a more clean way to switch between a page or a action link in fluid based on a var?
Now i used the if then statement but this increases a lot of double code lines. See example:
<f:if condition="{var}">
<f:then>
<f:link.page pageUid="{PageId}">
// a lot of code lines
</f:link.page>
</f:then>
<f:else>
<f:link.action pluginName="Name" controller="Cont">
// the same a lot of code lines again
</f:link.action>
</f:else>
</f:if>
You can extract the code within the links into a partial.
For this, first create a partial template. Inside an Extbase extension, they are placed in Resources/Private/Partials by convention (you can change this by using the setPartialsRootPath() method on the template object, but usually that shouldn't be necessary).
# Resources/Private/Partials/LinkContent.html
<!-- Insert your shared code lines here -->
Then reference the partial in your template:
<f:if condition="{var}">
<f:then>
<f:link.page pageUid="{PageId}">
<f:render partial="LinkContent" />
</f:link.page>
</f:then>
<f:else>
<f:link.action pluginName="Name" controller="Cont">
<f:render partial="LinkContent" />
</f:link.action>
</f:else>
</f:if>
Note that you will have to pass variables explicitly into the partial from the parent template:
<!-- Assuming there's a variable "foo" in your template -->
<f:render partial="LinkContent" arguments="{foo: foo}" />
Alternatively, you can import the entire scope into the partial:
<f:render partial="LinkContent" arguments="{_all}" />

Use v:content.render in a Contentelement

I try to render some Elements from another page via fluid in a Partial and try to use v:content.render. As you can see in the code I want to render 3 elements from the page with the Uid 9. But as soon as I have the v:content.render element in it I just get a blank page.
So my question is how to use v:content.render or what alternative I have? Or do I still need to use Typoscript for that?
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
<div class="footer2">
<v:content.render column="0" limit="3" pageUid="9" as="contentElements">
<f:for each="{contentElements}" as="contentElement" iteration="footerIteration">
<f:format.html>{contentElement.bodytext}</f:format.html>
</f:for>
</v:content.render>
</div>
I am not sure if it is important, but the elements on the Page 9 are also fluid content elements.
I found a solution that works:
<v:variable.set name="contentElements" value="{v:content.get(column:'0', limit:'3', pageUid:'9', render:'FALSE')}" />
<f:for each="{contentElements}" as="contentElement" iteration="footerIteration">
<v:content.render contentUids="{0:contentElement.uid}" />
<f:format.html>{contentElement.bodytext}</f:format.html>
</f:for>
I think your first attempt was more readable. {contentElement} already had what you needed, there is no property bodytext if you leave render to its default true.
<v:content.render column="0" pageUid="9" limit:"3" as="contentElements">
<f:for each="{contentElements}" as="contentElement">
<f:format.raw>{contentElement}</f:format.raw>
</f:for>
</v:content.render>
If you don't know what you get back in as="" to work with, just try a debug:
<v:content.render column="0" pageUid="9" limit:"3" as="contentElements">
<f:for each="{contentElements}" as="contentElement">
<f:debug inline="true">{contentElement}</f:debug>
</f:for>
</v:content.render>
And I replaced <f:format.html> with <f:format.raw> so prevent some unwanted <p></p>