I am using a custom validation rule in CakePHP to be sure a value is entered into a field when a corresponding checkbox is marked:
Here's the validation rule within my model's validation array...
'tv_price'=>array(
'check'=>array(
'rule'=>array('check_for_tv_price'),
'message'=>'Please enter the television pricing information.',
),
)
...and here's my really simple custom validation function:
public function check_for_tv_price($check) {
if($this->data['Client']['tv']==1&&$this->data['Client']['tv_price']=="") {
return false;
}
if($this->data['Client']['tv']==1&&$this->data['Client']['tv_price']!="") {
return true;
}
if($this->data['Client']['tv']==0) {
return true;
}
}
I've tried adding 'required'=>false and 'allowEmpty'=>true at different points in the validation array for my tv_price field, but they always override my custom rule! As a result, a user can not submit the form because the browser prevents it (due to the required attribute).
For reference, the browser spits out the following HTML:
<input id="ClientTvPrice" type="text" required="required" maxlength="255" minyear="2013" maxyear="2018" name="data[Client][tv_price]"></input>
(Note the minyear and maxyear attributes are from the form defaults.)
Has anyone found a way to prevent the automatic insertion of the required attribute when using custom validation rules?
Any guidance would be much appreciated.
Thanks!
Chris
Set required to false and allowEmpty to true, that should do it for you.
'tv_price'=>array(
'check'=>array(
'rule'=>array('check_for_tv_price'),
'message'=>'Please enter the television pricing information.',
'required' => false,
'allowEmpty' => true
),
)
Hope this helps.
These seems like 2.3's new HTML5 magic stuff.
Try adding 'formnovalidate' => true to the $this->FormHelper->input() options in the view.
ref:
http://book.cakephp.org/2.0/en/appendices/2-3-migration-guide.html#formhelper
http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::input
Thank you for the input!
Upon further investigation, I actually do want the required attribute turned on when the form is checked; to achieve this I wrote two simple JavaScript functions (and used a third that I found online) to check the current status of the checkboxes and mark the field as required when appropriate.
In the view file, I added the function calls in two locations. The first block is in the onload function and is called when the window is loaded:
<script type="text/javascript">
window.onload = function() {
toggle_setup(document.getElementById('ClientTv'), 'tvPrice', false)
toggle_required(document.getElementById('ClientTv'), "ClientTvPrice")
}
</script>
These are a mess, I know. But they worked!
Note the fields that appear when the checkbox is clicked are enclosed in a div with a name that is used in the toggle_setup function to show or hide the div as needed. In this case, I named the div 'tvPrice'
<?php echo $this->Form->input('Client.tv', array('label'=>'Purchasing TV? <small>(Check if Yes)</small>', 'onclick'=>'toggle_setup(this, "tvPrice", "TV pricing");toggle_required(this, "ClientTvPrice")'));
echo $this->Form->input('Client.tv_price', array('label'=>'Enter TV Price','div'=>array('class'=>'hidden', 'id'=>'tvPrice')));
?>
And here are the JavaScript functions themselves:
function toggle_required(element, id) {
var object = document.getElementById(id)
if(element.checked) {
object.setAttribute( 'required', "required")
} else {
object.removeAttribute('required')
}
}
function toggle_setup(element, div, human_term) {
if(element.checked) {
document.getElementById(div).style.display='block'
} else {
if(!human_term) {
clearChildren(document.getElementById(''+div))
document.getElementById(div).style.display='none'
} else {
if(confirm('Are you sure you want to clear the client\'s '+human_term+' settings?')) {
clearChildren(document.getElementById(div))
document.getElementById(div).style.display='none'
} else {
element.checked = true
}
}
}
}
function clearChildren(element) {
for (var i = 0; i < element.childNodes.length; i++) {
var e = element.childNodes[i];
if (e.tagName) switch (e.tagName.toLowerCase()) {
case 'input':
switch (e.type) {
case "radio":
case "checkbox": e.checked = false; break;
case "button":
case "submit":
case "image": break;
default: e.value = ''; break;
}
break;
case 'select': e.selectedIndex = 0; break;
case 'textarea': e.innerHTML = ''; e.value=""; break;
default: clearChildren(e);
}
}
}
The above code uses a CSS class called "hidden" which I've defined as below:
.hidden {
display: none;
}
Per the above suggestions, I've added the 'required'=>false and 'allowEmpty'=>true lines to my validation array and it appears to be working.
Thank you for all the help!
Related
<div class="ag-react-container">
<div class="text-center"><input type="checkbox" class="" checked=""></div>
</div>
cy.("#checkbox").then(($ele) => {
if($ele.next().is(':checked')){
cy.("#checkbox").next().should("be.checked");
}
else {
cy.("#checkbox").next().find("input").check();
cy.("#checkbox").next().find("input").should("be.checked");
}
});
My problem is the code above is not going into if condition and keeps on executing else condition.
Learning cypress so code can be wrong and would expect correct full code along with some more examples of same if else condition check for buttons, checkboxes or elements.
The problem appears to be .next(). If you remove it, the test works ok.
Also cy.wrap() command would be useful to perform multiple steps on $el.
cy.get('.ag-react-container')
.find('input')
.then($el => {
if ($el.is(':checked')) {
cy.wrap($el).should("be.checked") // passes if checked
} else {
cy.wrap($el).click()
cy.wrap($el).should("be.checked") // passes if not checked
}
})
You can also do this
cy.get('.ag-react-container')
.find('input')
.then($el => {
if ($el.is(':not(:checked)')) {
cy.wrap($el).click() // checks if not checked
}
})
.should("be.checked") // passes either way
As I can see in your HTML for the checked checkbox you have checked="". So to validate that the checkbox is checked, you have to put in the if condition that element has the attribute checked.
cy.get('input[type="checkbox"]').then(($ele) => {
if ($ele.attr('checked') == '') {
//Do Something
} else {
//Do Something
}
})
I want to display a file selector and a submit button.
The file is only to be submitted once the button is clicked.
The submit/action target is on another server though.
I looked at react examples, but I can't figure out the exact method on how to
realise this with KotlinJS and React.
The duplicates in formMethod and onSubmitFunction are just me trying and seeing what sticks.
I also tried adding method = FormMethod.post, encType = FormEncType.multipartFormData directly to the form, but it didn't help. It doesn't even output the debug print.
EDIT: I do not need to do something with the file otherwise and want to leverage the default form functionality for the upload. The other server is also mine and has a simple html/http upload where this suffices:
<form method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
ENDEDIT
EDIT2: I changed the code a little and now it refers to the other server, but does not append any files.
private fun RBuilder.render() {
form(
method = FormMethod.post,
encType = FormEncType.multipartFormData,
action = "https://otherserver.com/upload"
) {
styledInput {
attrs {
type = InputType.file
accept = ".zip"
multiple = false
}
}
styledInput {
attrs {
type = InputType.submit
value = "Test"
}
}
}
}
It seems like in this case the name parameter is strictly required.
The following works:
private fun RBuilder.render() {
form(
method = FormMethod.post,
encType = FormEncType.multipartFormData,
action = "https://otherserver.com/upload"
) {
styledInput {
attrs {
name = "upload"
type = InputType.file
accept = ".zip"
multiple = false
}
}
styledInput {
attrs {
type = InputType.submit
value = "Test"
}
}
}
}
I have a long set of checkboxes. I would like two groups of three of them to behave as radio buttons. Now, leaving aside my UX choices, how can I make this work?
The checkboxes are implemented as properties on a single object, layer:
data() {
return {
layer: {},
}
},
watch: {
layer: {
handler(val, oldval) {
mapping.updateLayers(val)
},
deep: true,
},
},
That works fine. But intercepting val and updating this.layer inside handler() doesn't:
handler: function(val, oldval) {
if (val.FutureYear) { this.layer.NextYear = false; this.layer.ExistingYear = false; }
if (val.ExistingYear) { this.layer.NextYear = false; this.layer.FutureYear = false; }
if (val.NextYear) { this.layer.ExistingYear = false; this.layer.FutureYear = false; }
mapping.updateFoiLayers(val);
},
How can I achieve this result? (I'd prefer not to have to implement actual radio buttons because that makes managing all the layers and UI more complex.)
Example: https://codepen.io/jacobgoh101/pen/NyRJLW?editors=0010
The main problem is the logic in watch.
If FutureYear is selected already, other field becomes unchangeable. Because if (val.FutureYear) is always the first one being triggered and the other 2 field will always be set to false immediately.
Another thing about watch is that it will be triggered when
user changed the value
program changed the value (this is unnecessary and make things harder to handle)
Therefore, handling #change event is more appropriate in this scenario.
JS
methods: {
handleChange: function(e) {
const name = e.target.name;
if (this.layer[name]) {
Object.keys(this.layer).map((key)=>{
if(key != name) {
this.layer[key] = false;
}
})
}
}
}
html
<div id="app">
<input type="checkbox"/ v-model="layer.FutureYear" name="FutureYear" #change="handleChange($event)">FutureYear<br/>
<input type="checkbox"/ v-model="layer.NextYear" name="NextYear" #change="handleChange($event)">NextYear<br/>
<input type="checkbox"/ v-model="layer.ExistingYear" name="ExistingYear" #change="handleChange($event)">ExistingYear<br/>
</div>
I need to enable/disable all validation rules in Angular form or subform under ng-form="myForm" based on a scope variable $scope.isValidationRequired. So, if isValidationRequired is false, none of the validations set for the designated group of fields will run, and the result will always be myForm.$valid==true, otherwise, the validation rules will run as usual.
I did a lot of research, and realized that this feature is not available out of the box with Angular. However, I found some add-ons or with some customization, it is possible.
For example, I can use the add-on angular-conditional-validation (github and demo) with custom directive enable-validation="isValidationRequired". This will be perfect, except that I cannot apply this feature for a group of fields under ng-form. I have to add this directive for each and every field where applicable.
The other solution is to use custom validation using Angular $validators pipeline. This requires some extra effort and I don't have time since the sprint is almost over and I have to give some results in a few days.
If you have any other suggestions please post an answer.
Use Case:
To clarify the need for this, I will mention the use-case. The end user can fill the form with invalid data and he can click Save button and in this case, the validation rules shouldn't be triggered. Only when the user clicks Validate and Save then the validation rules should be fired.
Solution:
See the final plunker code here.
UPDATE: as per comments below, the solution will cause the browser to hang if inner subforms are used under ng-form. More effort is needed to debug and resolver this issuer. If only one level is used, then it works fine.
UPDATE: The plunker here was updated with a more general solution. Now the code will work with a form that has sub-forms under ng-form. The function setAllInputsDirty() checks if the object is a $$parentForm to stop recursion. Also, the changeValidity() will check if the object is a form using $addControl then it will call itself to validate its child objects. So far, this function works fine, but it needs a bit of additional optimization.
One idea is to reset the errors in the digest loop if the validation flag is disabled. You can iterate through the form errors on change and set them to valid, one by one.
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function() {
if ($scope.isValidationRequired === "false") {
for (var error in $scope.form.$error) {
while ($scope.form.$error[error]) {
$scope.form.$error[error][0].$setValidity(error, true);
}
}
}
}
Here is a plunkr: https://plnkr.co/edit/fH4vGVPa1MwljPFknYHZ
This is the updated answer that will prevent infinite loop and infinite recursion. Also, the code depends on a known root form which can be tweaked a bit to make it more general.
References: Pixelastic blog and Larry's answer
Plunker: https://plnkr.co/edit/ycPmYDSg6da10KdoNCiM?p=preview
UPDATE: code improvements to make it work for multiple errors for each field in each subform, and loop to ensure the errors are cleared on the subform level
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.isValidationRequired = true;
var rootForm = "form";
function setAllInputsDirty(scope) {
angular.forEach(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form except the parent form
if (value.$addControl && key !== "$$parentForm") {
return setAllInputsDirty(value);
}
if (value.$validate){
value.$validate();
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
//debugger;
return value.$setViewValue(value.$viewValue);
}
});
}
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function(theForm) {
debugger;
//This will check if validation is truned off, it will
// clear all validation errors
if (!theForm) {
theForm = $scope[rootForm];
}
if ($scope.isValidationRequired === "false") {
for (var error in theForm.$error) {
errTypeArr = theForm.$error[error];
angular.forEach (errTypeArr, function(value, idx) {
var theObjName = value.$name;
var theObj = value;
if (theObj.$addControl) {
//This is a subform, so call the function recursively for each of the children
var isValid=false;
while (!isValid) {
$scope.changeValidity(theObj);
isValid = theObj.$valid;
}
} else {
while (theObj.$error[error]) {
theObj.$setValidity(error, true);
}
}
})
}
} else {
setAllInputsDirty($scope);
}
}
}]);
In my scenario I have a rest controller that I am validating input data.
I have built a form that I am using purely for custom validation that looks like this:
$validator
->requirePresence('sport_type')
->add('sport_type', 'Require fields missing', [
'rule' => function ($value) {
switch ($value) {
case 'Football':
$this->validator()
->requirePresence('football_id');
break;
case 'Basketball':
$this->validator()
->requirePresence('basketball_id');
break;
default:
return true;
}
return true;
}
])
->notEmpty('football_id')
->notEmpty('basketball_id')
return $validator;
What I need to be able to do, is in the default case, ensure that the other fields are null - if for example, sport_type = 'Motorsport', then the validator must return false if the input data DOES contain something in football_id or basketball_id.
I cannot see any kind of requireEmpty type of method in cake, so can anyone suggest how to accomplish this. Do I need a separate custom validator for that single rule, and how would I call it from this form validator ?