In my fluid template I have:
<f:form.textfield
id="{propertyName}"
property="{propertyName}"
value="{value}"
placeholder=""
class="form-control"
/>
I want to add condition for "value". Something like:
<f:form.textfield
id="{propertyName}"
property="{propertyName}"
<f:if condition="{value}"> value="{value}" <f:if>
placeholder=""
class="form-control"
/>
For now I'm using workaround
<f:if condition="{value}">
<f:then>
<f:form.textfield id="{propertyName}" property="{propertyName}" value="{value}" placeholder="" class="form-control" />
</f:then>
<f:else>
<f:form.textfield id="{propertyName}" property="{propertyName}" placeholder="" class="form-control" />
</f:else>
</f:if>
But I'd like to avoid double code.
I think this is not possible in this way.
Possible solution is user defined ViewHelper (exclude original f:form.textfield).
Developing a custom ViewHelper
Nesting ViewHelpers in their XML notation is not possible. However, you can use the inline notation, at least to a certain extent:
<f:form.textfield
value="{f:if(condition: '<your-condition>', then: value)}" />
Yes, the value attribute will still be passed this way, however, with a null value. Note that the <f:form.textfield> view helper automatically omits the value attribute if the submitted value is empty (see the source code of TYPO3\CMS\Fluid\ViewHelpers\Form\TextfieldViewHelper, line 74ff.):
public function render($required = false, $type = 'text')
{
// [...]
$value = $this->getValueAttribute();
if ($value !== null) {
$this->tag->addAttribute('value', $value);
}
In the simple case that you just want to omit the value attribute when it's empty, just always passing value="{value}" should suffice. When the value is null, the attribute will not be included in the rendered <input> tag.
You can do it like this:
<f:form.textfield
name="{data.name -> f:format.urlencode()}"
type="{f:if(then: '{data.inputtyp}', else: 'text', condition: '{data.inputtyp}')}"
class="{f:if(then: '{data.class}', else: '', condition: '{data.class}')}"
value="{f:if(then: '{data.populate}', else: '', condition: '{data.populate}')}"
placeholder="{f:if(then: '{data.placeholder}', else: '', condition: '{data.placeholder}')}" />
Related
I'm currently building a form in Vue js and I'm using Vueformulate, the docs have various ways of validation however there is no validation method or implementation that for my use case. I would like the validation to call a custom function because this way I could check if Field1 or Field2 has a value. Is there an implementation like my psuedocode below? thanks in advance!
<FormulateForm #submit="onSubmit" #default="{ hasErrors }">
<FormulateInput
type="text"
label="Field1"
validation="customValidation"
/>
<FormulateInput
type="text"
label="Field2"
validation="customValidation"
/>
<FormulateInput
type="submit"
label="Submit"
input-class="btn btn-primary w-full"
:disabled="hasErrors"
/>
</FormulateForm>
//NOT REAL CODE
customValidation(formValues){
if(formValues.Field1 || formValues.Field2){
return true // CHECK IF EITHER FIELD1 OR FIELD 2 HAS VALUE IF SO MARK THE FIELD AS VALID WHICH ENABLES SUBMIT BUTTON
}
return false
}
You can declare a custom validation rule via <FormulateInput>.validationRules:
<script setup>
const validationRules = {
customValidation({ getFormValues }) {
// property names match the input names
const { field1, field2 } = getFormValues()
if (field1 || field2) {
return true
}
},
}
</script>
<template>
<FormulateInput
type="text"
label="Field1"
name="field1"
validation="customValidation"
:validationRules="validationRules"
/>
<FormulateInput
type="text"
label="Field2"
name="field2"
validation="customValidation"
:validationRules="validationRules"
/>
</template>
But FormulateForm assumes an error state unless all fields are touched (at least in my demo). Not sure how you could get around that.
demo
I want to have a big Fluid form where multiple records can be modified at once. There is only one form on the page to submit all records.
<f:if condition="{records}">
<f:then>
<f:form action="update" enctype="multipart/form-data" name="updateRecords" object="{records}">
<div class="row">
<f:for each="{records}" as="element" iteration="recordsIterator">
<div class="col-md-3">
<h5>{element.title}</h5>
<div>
<f:if condition="{feature}">
<f:then>
<h6>
<f:translate key="records.feature" />
</h6>
<f:form.checkbox property="feature" value="2" />
<label for="tx_example_records[updateRecords][{recordsIterator.index}][feature]">
<f:translate key="records.read" />
</label>
</f:then>
</f:if>
</div>
</div>
</f:for>
</div>
<f:form.submit class="button" value="{f:translate(key: 'submit', default: '[submit]')}"><input class="button" type="submit" name="" value="Submit" /></f:form.submit>
</f:form>
</f:if>
The corresponding controller has only a domain object as parameter.
/**
* action update
*
* #param \Foo\Example\Domain\Model\Records $updateRecords
* #return void
*/
public function updateAction(\Foo\Example\Domain\Model\Records $updateRecords) : void
However I need a solution with an array of all listed Domain\Model\Records passed as a parameter from the submitted form to the updateAction. The attribute feature is a checkbox to be checked if the record's value of feature = '2'; . Is this somehow possible in TYPO3? Or must I use some tricks to accomplish this task?
You can go with a Data Transfer Object here, holding the values you need to submit the form. You can find some general information related to Extbase here: https://usetypo3.com/dtos-in-extbase.html
How can I deliver prepared fluid form elements using PHP and have them process in a fluid template?
Something like:
Controller:
public function indexAction(): void {
$html = '<div class="wrap">
<f:form.textfield name="email" value=""/>
<f:form.textfield name="token" value="a#b.com"/>
</div>';
$this->view->assign('elements', ['data' => $html]);
}
Index Template:
<f:form ...">
<div class="F">{elements.data -> f:format.raw()}</div>
</f:form>
Rendering Fluid is no iterating process and so your Fluid in a variable will not be rendered as Fluid.
If you want variants you could use partials which can be controlled by a variable
<f:if condition="{var1} == 'long'">
<f:then>
<render partial="longVersion" arguments="{_all}" />
</f:then>
<f:else>
<render partial="shortVersion" arguments="{_all}" />
</f:else>
</f:if>
you even can use the variabel to select the partial directly:
<f:render partial="Part_{var1}" arguments="{_all}" />
Another way would be to insert the rendered Fluid in to the variable.
in Typoscript this coul be like this:
10 = FLUIDTEMPLATE
10 {
template = outer
variables {
part1 = FLUIDTEMPLATE
part1 {
template = inner
variables {
:
}
}
:
}
}
or dynamically:
<f:cObject typoscriptObjectPath="lib.subtemplate">
lib.subtemplate = FLUIDTEMPLATE
lib.subtemplate {
template = inner
variables {
:
}
}
I want viewhelper that can be helpful to assign variable in fluid, I dont want variable to be passed from controller.
Install extension called vhs from TYPO3 repository
Define namespace like following at the top of your fluid template
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
Then use set viewhelper
<v:variable.set name="test" value="12345" />
Value of test : {test}
{test} will return value 12345
For registering global variable
<v:variable.register.set name="test" value="12345"/>]
Get value of global variable
Value of global variable : <v:variable.register.get name="test">
Since TYPO3 8.7, fluid introduces viewhelper for the variable (No need
of VHS)
<f:variable name="myvariable">My variable's content</f:variable>
<f:variable name="myvariable" value="My variable's content"/>
With inline style usage
{f:variable(name: 'myvariable', value: 'My variable\'s content')}
{myoriginalvariable -> f:variable.set(name: 'mynewvariable')}
Since TYPO3 8.6, this is possible without extension "vhs":
<f:variable name="myvariable" value="My variable's content"/>
See https://docs.typo3.org/typo3cms/extensions/core/Changelog/8.6/Feature-79402-VariableViewHelperForFluid.html
Since first fluid version it is possible to define variables for a special area: there is the VH f:alias which enables you to define new variables in the range of this VH. And that is the difference to the variable.set VH from ext:vhs.
<f:alias map="{firstName: 'John', lastName: 'Doe'}">
<p>Hello, my name is {firstName} {lastName}</p>
</f:alias>
<f:for each="{users}" as="user" iteration="iterator">
<f:if condition="{iterator.isFirst}">
<v:variable.set name="firstName">{user.firstName}</v:variable.set>
<v:variable.set name="lastName">{user.lastName}</v:variable.set>
</f:if>
:
do other output
:
</f:for>
<p>the first user was {firstName} {lastName}.</p>
The problem with setting variables inside the fluid is the possibility to do programming logic inside fluid:
<v:variable.set name="counter" value="0">
<f:for each="records" as="record">
<f:if condition="{record.flag}">
<v:variable.set name="counter">
<f:cObject typoscriptObjectPath="lib.calc">
{counter}+1
</f:cObject>
</v:variable.set>
</f:if>
</f:for>
<p>there are {counter} records with a flag set.</p>
with this typoscript
lib.calc = TEXT
lib.calc.current = 1
lib.calc.prioriCalc = 1
I would like to create a validator for abide for a set of checkboxes.
Let's consider a set of 5 checkboxes. The user is asked to check 3 max, and at least 1.
So, here is my work-in-progress code:
<div data-abide-validator='checkboxes' data-abide-validator-values='1,3'>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
</div>
<script>
$(document).foundation({
validators: {
checkboxes: function(el, required, parent) {
var countC = el.find(':checked').length;
alert(countC);
}
}
});
</script>
At this point, I just try to count the checked inputs. But it seems I can't even trigger the validator... I think I could manage to code my validation stuff if only I could figure out how to trigger it.
Indeed I didn't find many examples of the custom validator, and the official doc did not help me much.
Your HTML markup is not really "correct" for abide. You should be attaching the data-abide-validator attribute to the inputs, not the parent div. Additionally, you need some better markup so abide's default error display can work (and some better use of foundation's grid system to lay it out). I would point you toward the Abide Validation Page on Zurb's site for some examples of form markup.
I've taken the liberty of restructuring your markup to be something that is more becoming of a foundation layout:
<form action="/echo/html/" method="POST" data-abide>
<div class="row">
<div class="small-12 columns checkbox-group" data-abide-validator-limit="1,3">
<label>Check some boxes</label>
<small class="error">You have checked an invalid number of boxes.</small>
<ul class="small-block-grid-3">
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="1" /> 1
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="2" /> 2
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="3" /> 3
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="4" /> 4
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="5" /> 5
</label>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="small-12 columns">
<button type="submit">Submit</button>
</div>
</div>
</form>
As to your JS code. It's not correct either. You need to address the abide -> validators namespace of the options, not just validators. I've rewritten your JS code to not only do that, but give the desired effect you wanted:
$(document).foundation({
abide: {
validators: {
checkbox_limit: function(el, required, parent) {
var group = parent.closest( '.checkbox-group' );
var limit = group.attr('data-abide-validator-limit').split(',');
var countC = group.find(':checked').length;
if( countC >= limit[0] && countC <= limit[1] ) {
group.find('small.error').hide();
//return true so abide can clear any invalid flags on this element
return true;
} else {
group.find('small.error').css({display:'block'});
//return false and let abide do its thing to make sure the form doesn't submit
return false;
}
}
}
}
});
In order to check adjacent elements when doing custom validation, you need to have something to target. The el variable in the validation function will be the DOM element of the input/field that is being validated. The required variable will tell you if the field is flagged as being required or not (boolean). The parent variable will be set to the "parent" of the field. I say "parent" because although the label tag is technically the parent of the input element, abide is smart enough to realize that the label is part of the field's element structure and skip over it to the li element instead.
From there, you need a way to identify a common parent. So I added the checkbox-group class to whatever element I decided to make the "parent" of all the checkboxes in the group. This is not a Foundation or Abide "magic" class, but rather something of my own creation for use in the validation function.
From there, you can easily trace the few lines of the validation function to see the workflow: Find the group container object, parse the limits off the container's data-abide-validator-limits attribute, count the number of checked inputs in the container, check if the number checked is between the limits, display/hide the error message and return true/false so abide knows if the field validated or not.
I've got a working Fiddle of it if you care to check it out yourself ;) Hopefully this was informative for you, and I wish you the best of luck playing with the awesome that is Foundation!