Liferay: Controlling form validators at runtime via JavaScript - validation

I am using Liferay 6.2 and built-in form validators using AUI taglib, ie:
<aui:input ... >
...
<aui:validator name="number" errorMessage="Enter number" />
</aui:input>
Is there any way to disable and re-enable the validation at runtime (after portlet is displayed) via JavaScript?
The only solution I thought of was to re-implement all validators as custom validators with same functionality and on/off switch - this looks like a lot of work.

I did use the suggested method to reimplement the validators as custom validators, it's not a too big work but it would be really great not to have to do it.
Looking deeper in some liferay component I found that the Liferay.auto_field did remove validators and add it back when required. The code doing it look like this: (https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/js/liferay/auto_fields.js#L219)
var errors = formValidator.errors;
rules = formValidator.get('rules');
node.all('input, select, textarea').each(function(item, index) {
var name = item.attr('name') || item.attr('id');
if (rules && rules[name]) {
deletedRules[name] = rules[name];
delete rules[name];
}
if (errors && errors[name]) {
delete errors[name];
}
});
I didn't try to do it myself, but this should work. Important to note, to get the formValidator you need to (can see on https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/js/liferay/auto_fields.js#L501)
Liferay.Form.get(formId).formValidator

Related

Binding Vue.js to all instances of an element, without(?) using Components

Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.

Where to define a filter function for a form field in my Joomla component's preferences

I am creating a component in Joomla 2.5. This component has some options that are defined in its config.xml, so they can be set in the preferences of the component. Now I would like to apply a filter to one of these option fields, using the attribute filter="my_filter".
In the source code of JForm I saw the following lines at the very end of the implementation of JForm::filterField():
if (strpos($filter, '::') !== false && is_callable(explode('::', $filter)))
{
$return = call_user_func(explode('::', $filter), $value);
}
elseif (function_exists($filter))
{
$return = call_user_func($filter, $value);
}
That's what I needed for using a filter function defined by myself!
I managed to do this for form fields used in the views of my component. I defined the filter function as MyComponentHelper::my_filter(), where MyComponentHelper is a helper class which I always load in the very base of my component. And in the form's xml I added filter="MyComponentHelper::my_filter" to the fields that have to be filtered. However... when I am trying to apply the filter function to a form field in my component's preferences, I am not in my own component, but in com_config instead, so my helper class is not available!
So, therefore, my question: where to define my own filter function in such a way that it can be found and called by JForm::filterField() in com_config?? Help is very much appreciated.
May be it is too late, but this topic is only I found about that trouble. May be my solution will be helpfull to someone.
1) Add to tag of .xml form file the attribute 'addfieldpath' like this:
<fieldset name="basic" addfieldpath="PATH_TO_MY_EXTENSION/models/fields">
2) Modify filtered field description like this:
<field
name="MY_FIELD_NAME"
type="myfildtype"
label="MY_FIELD_LABEL"
description="MY_FIELD_DESC"
filter="JFormFieldMyFieldType::filter"
/>
3) Create the file 'PATH_TO_MY_EXTENSION/models/fields/myfildtype.php':
<?php
defined('JPATH_PLATFORM') or die;
JFormHelper::loadFieldClass('text'); // or other standard Joomla! field type
class JFormFieldMyFieldType extends JFormFieldText // or other standard Joomla! field type class
{
protected $type = 'MyFieldType';
public static function filter($value)
{
// filter code
return $value;
}
}
I had to deal with the same issue today. Here is what I did.
Our form field looks like this:
<field name="verwaltungskosten" type="text" class="form-control" size="40" label="Verwaltungskosten" labelclass="col-sm-2
compojoom-control-label"
filter="MyComponentFilterDouble::filter" required="true"/>
As you can see we have a filter. We've specified
MyComponentFilterDouble as class and filter as a method of this class.
If you have a look at libraries/joomla/form/form.php in the
FilterField function toward the end you'll see that the code will try
to execute our custom filter. Now here comes the tricky part. How does
Joomla know where our filters are located? Well, it doesn't! We have
to load our filters in advance. JForm doesn't come with a utility
class that could load a custom filter. I've decided to load our
Filters in our model in the getForm function. As you know each model
that extends from JModelAdmin should have a getForm function. This
function makes sure that we are loading the correct form from a .xml
file. So in this function just before I load the form I did:
JLoader::discover('MyComponentFilter', JPATH_ADMINISTRATOR . '/components/com_mycomponent/models/forms/filters');
The discover method will make sure to auto load our class when we need
it. This way it will be available to our form.
And there we go! Now when our model validates the form. It actually
always first performs filtering on the data. Now in our custom filter
we can modify the data and pass it back for validation. It's that
easy!
The above text is in quotes because I took it from my blogpost about that same issue over here: https://compojoom.com/blog/entry/custom-filtering-for-jform-fields
I think what you're asking about is actually adding custom validation to one of your form fields. If that's the case you actually need to be looking at adding server-side validation in addition to adding configuration. Pay particular attention to the 'addrulepath' in the example under the heading "Using configuration parameters as default value". You'll most likely end up extending JFormRule, of which I've included a very stripped-down example below.
<?php
/** headers */
defined('JPATH_PLATFORM') or die; // Joomla only
class JFormRuleCustom extends JFormRule
{
public $type = 'Custom';
public function test(&$element, $value, $group = null, &$input = null, &$form = null) {
return /* true for passed validation, false for failed validation */
}
}
When you've got that down you can add the validation "custom" to your form fields like so:
<field
name="pw1"
type="password"
label="COM_NEWUSER_UPDATE_LABEL_PASSWORD1"
description="COM_NEWUSER_UPDATE_DESCRIPTION_PASSWORD1"
message="COM_NEWUSER_UPDATE_ERROR_PASSWORD1"
size="40"
required="true"
validate="custom"
minlength="5"
maxlength="20"
specials="!##$%^&*"
/>
Hopefully that answers your question and didn't go totally off-topic.

Joomla editor strips tags

I'm trying to create a custom component in Joomla 2.5 and struggling to get it to stop it stripping all html tags out of the editor field - links, new lines, p tags - the full works. The form field is below:
<field
name="post"
type="editor"
label="COM_HELLO_WORLD_EDITOR_LABEL"
description="COM_HELLO_WORLD_EDITOR_DESC"
class="inputbox"
filter="JComponentHelper::filterText"
required="true"
default=""
/>
Clearly there are many many posts about this around both SO and Joomla forums. However they generally seem to have two clear themes.
Tiny MCE Settings. I've checked after setting my default editor to "None" (i.e. just a text area) and the tags are all still stripped
Joomla Text filter settings. I'm logged in as a Global Admin with the super users set to "no filtering"
I'm overriding the model's save function for this with:
function store()
{
$row =& $this->getTable();
$input = new JInput();
$data = $input->getArray($_POST);
//Sets Users id as current logged in user if not set
if(!$data['jform']['post_user']) {
$data['jform']['post_user']=JFactory::getUser()->id;
}
// Bind the form fields to the post table
if (!$row->bind($data['jform'])) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// Make sure the hello is valid
if (!$row->check()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// Store the hello table to the database
if (!$row->store()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
return true;
}
My gut instinct is that it's to do with JInput stripping HTML tags. But even adding in the extra line into the save file $data['jform']['post']=$input->getHTML('post'); nothing happened. So I'm not really sure where to go from here. Any ideas?
UPDATE
Just to clarify an issue quickly - I want to use the preset Joomla 'Text Filter' Settings under 'Global Configuration' rather than manually setting each tag in the component!
UPDATE 2
I added filter="raw" to the editor form field. I now see the html <p> tags when I dump out the variable $_POST['jform']['post'], null, 'HTML'). However then when applying just a simple JInput Filter function - let alone applying the Joomla Config values - I'm getting null.
$input = new JInput();
$data = $input->getArray($_POST);
$data['jform']['post']=$input->get($_POST['jform']['post'], null, 'HTML');
Is the sentence here "HTML - Returns a string with HTML entities and tags intact, subject to the white or black lists in the filter." describing the JInput HTML filter referring to the Global Config Text filter settings? Just to confirm?
Try something like this
$input_options = JFilterInput::getInstance(
array(
'img','p','a','u','i','b','strong','span','div','ul','li','ol','h1','h2','h3','h4','h5',
'table','tr','td','th','tbody','theader','tfooter','br'
),
array(
'src','width','height','alt','style','href','rel','target','align','valign','border','cellpading',
'cellspacing','title','id','class'
)
);
$postData = new JInput($_POST,array('filter' => $input_options));
First array it is allowed tags, second array it is allowed attributes.
What is this about? filter="JComponentHelper::filterText"? Did you write a custom filter?
The default filtering like most things in Joomla (also acl for example) is very strict so that if you get xss from not filtering it's a deliberate choice you've made not a security risk in the core. But your core filtering should be being applied ... except that you seem to have perhaps overridden with the unknown filter. So I suspect given this unknown filter it's falling back to very string.
Quite some time later, but just for the record, for anyone encountering the same problem, here my solution.
For me this problem was immediately solved by using JRequest instead of JInput. I believe it's deprecated, but it is still used by Joomla 2.5.14 (most up-to-date Joomla 2.5 at this moment) in the save() function of JControllerForm.

knockout.js and Firefox Save Passwords on login form

Firefox populates a form with my username/password. This is using knockout.js to bind the input but it won't update the values on this kind of populating. Am I missing something say on a page load? When it populates and the user hits submits, the values are blank.
(function (app, $, undefined) {
app.viewModel = app.viewModel || {};
app.login = {};
app.viewModel.login = {
userName: ko.observable(''),
password: ko.observable(''),
returnUrl: ''
};
app.viewModel.login.submit = function () {
sso.login(app.viewModel.login.userName(), app.viewModel.login.password(), app.viewModel.login.returnUrl);
};
app.login.init = function (returnUrl) {
app.viewModel.login.returnUrl = returnUrl;
ko.applyBindings(app.viewModel);
};
})(window.app = window.app || {}, jQuery);
The way that I have dealt with this in the past is to use a wrapper to the value binding that initializes the value from the element's current value.
It would look like (this one is simplified to only work with observables):
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
var observable = valueAccessor();
var value = element.value;
observable(value);
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, context);
},
update: ko.bindingHandlers.value.update
};
So, you would use valueWithInit instead of value. You just need to make sure that ko.applyBindings is not called before the autocomplete has been able to do its job.
http://jsfiddle.net/rniemeyer/TeFAX/
I found the solution here not really satisfying. Although the approach is rather interesting, it fails when the user is choosing the account later and the browser does allow to use the stored credentials (e.g. if there are more than one credentials stored). It failed as well when you started typing in the password and deleted to get back to the original password (in Firefox at least).
Additionally, I did not really like the timeout to give the browser time - just not that nice.
My solution:
which isn't really one, but I thought I share nonetheless
Simple update our model manually before doing the login in the submit callback.
Using jQuery, something like self.password($("#password").val()) should do it.
Alternatively, using the existing bindings, triggering a change event seems to work as well - e.g. $("#password").change().
The Pros:
is only for credential fields, so probably a one time thing for your site
is simple and clean - one or two lines at the proper place
seems to always work reliably, no matter what browser, credential setup or usage pattern
The Cons:
breaks again the nice separation Knockout.js provides
is not a solution but rather a workaround
I will stick with that for now because I found it just reliable working. It would be nice to tell Knockout to reevaluate the bindings directly rather than storing the value back manually or triggering it via the change event. But I haven't found anything so far.
Just thinking a bit ahead - the same problem should arise when the browser auto-completes any form (e.g. like an address) - which means means some sort of general function doing the above would be nice (probably calling the change trigger on each input field of the form)
Edit:
Some quick code demonstrating the idea
The HTML:
<form id="myForm" data-bind="submit: login">
Email: <input type="text" data-bind="value: email" /><br/>
Password: <input type="password" data-bind="value: password" /><br/>
<button type="submit">Login</button>
</form>
And the Javascript:
function ViewModel() {
var self = this;
self.email = ko.observable("");
self.password = ko.observable("");
self.login = function() {
$("#myForm").find("input").change();
//Now the observables contain the recent data
alert(ko.mapping.toJSON(self));
};
}

Difference between html rendered when client validation is true in MVC3

I'm creating a brand new MVC3 site.
Client side validation enabled on the web.config
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
Scenario #1: Output HTML generated after a failed (client side) validation:
<span data-valmsg-replace="true" data-valmsg-for="UserName" class="field-validation-error">
<span htmlfor="UserName" generated="true" class="">Please enter an email address</span>
</span>
Note the nested span tag where the innermost tag has a class=""
Scenario #2: Custom Server-side validation. With the same web.config configuration, I added a validation on the server to check for a custom business rule. The validation fails, I add the error to the ModelState.
The HTML generated looks like this:
<span data-valmsg-replace="true" data-valmsg-for="UserName" class="field-validation-error">Please enter a valid email address</span>
Note that just one span tag was generated, NOT a nested tag.
This behavior is making it a pain to deal with my CSS as I can't just style the .field-validation-error class as there are 2 different end results on my generated HTML.
IN SUMMARY: Client side validation generates just 1 span tag, server side validation generates 2 span tags.
QUESTION: Is this the indented behavior of the framework or am I doing something wrong?
Is this the indented behavior of the framework or am I doing something wrong?
You are not doing anything wrong. It's just how the jquery.validate.unobtrusive.js script works. So you may call this a missing feature, discrepancy, PITA, whatever but that's how they did it out of the box.
This being said the jquery validate plugin is extensible and we can tweak it as we like:
$.validator.setDefaults({
// customize the way errors are shown
showErrors: function (errorMap, errorList) {
if (errorList.length < 1) {
// because we have customized the way errors are shown
// we need to clean them up if there aren't any
$('.field-validation-error', this.currentForm).hide().attr('class', 'field-validation-valid');
$('.input-validation-error', this.currentForm).removeClass('input-validation-error');
return;
}
$.each(errorList, function (index, error) {
// simply toggle the necessary classes to the corresponding span
// to make client validation generate the same markup as server
// side validation
var element = $(error.element);
element.attr('class', 'input-validation-error');
element.next('span').show().text(error.message).attr('class', 'field-validation-error');
})
}
});
If you want to always use nested spans for your validation messages after server validation failure (for styling reasons), you can do the following:
$(document).ready(function(){
$('.field-validation-error').each(function(){
$(this).html($('<span>').text($(this).text()));
});
});

Resources