How to reference form field created by AngularJS formly - javascript

I use Formly for creating my forms in angularJS
This is my field
$scope.postFields = [
{
key: 'title',
type: 'input',
templateOptions: {
label: "Title",
// required: true,
minlength: 2,
},
validation: {
messages: {
required: function(viewValue, modelValue, scope) {
return scope.to.label + ' is required'
}
}
}
}
]
and I'm trying to access my fields as follows
function failure(response) {
console.log("failure", response)
_.each(response.data, function(errors, key) {
_.each(errors, function(e) {
$scope.form[key].$dirty = true;
$scope.form[key].$setValidity(e, false);
});
});
}
my formly form
<formly-form form="postForm" model="model" fields="postFields" class="col-md-4">
<button type="submit" class="btn btn-primary" ng-click="addPost()">Submit</button>
</formly-form>
but of course I'm getting this error:
TypeError: Cannot read property 'title' of undefined
it's on this line
$scope.form[key].$dirty = true;
do anyone of you know how to reference created formly fields the right way?

If you want to be able to reference the fields from the form, you could provide a name attribute to your fields. The name is generated by default. It's one of the nice things that angular-formly provides (you don't have to think about it). But if you want to reference it directly with a specific key (as you are) then you'd be best served by providing one yourself.
So you'd do something like this:
$scope.postFields = [
{
key: 'title',
name: 'title',
type: 'input',
templateOptions: {
label: "Title",
// required: true,
minlength: 2,
},
validation: {
messages: {
required: function(viewValue, modelValue, scope) {
return scope.to.label + ' is required'
}
}
}
}
]
Alternatively, you could create a fieldTransform to do this automatically (just assign the name the same as the key). Or in your error handler, you could look up the NgModelController from the field's formControl property like this:
function handleError(fields, response) {
_.each(fields, function(field) {
if (response.data[field.key]) {
field.formControl.$setDirty()
_.each(response.data[field.key], function(e) {
field.formControl.$setValidity(e, false)
})
}
})
}
That's probably the best solution :-) Good luck!

Related

How change JQuery Validation Message without refreshing website [duplicate]

This question already has answers here:
jquery .validate() variable error message
(3 answers)
Closed 1 year ago.
I am using jQuery Validation from here: https://jqueryvalidation.org. Currently, I have two types of Messages prepared. They are popping out. When I'm changing language on web variables global variable with messages also is changing, but message in validator don't. Language of the validator change after I refresh the website. How can I change it on live without refreshing the website?
HTML is no needed, because everything is happening in JS file.
This is a function that are changing values in the object.
JS:
function setLang(lang) {
(lang) ? lang: "pol";
if (lang == "pol") {
Messages = {
formFirstnameRequired: 'Pole imie jest wymagane!',
formLastnameRequired: 'Pole nazwisko jest wymangane!',
formEmailRequired: 'Pole email jest wymagane!',
formTitleRequired: 'Pole Tytuł jest wymagane!',
formMessageRequired: 'Pole wiadomość jest wymagane!',
}
} else if (lang == "eng") {
Messages = {
formFirstnameRequired: 'Field firstname is required!',
formLastnameRequired: 'Field lastname is required!',
formEmailRequired: 'Field email is required!',
formTitleRequired: 'Field title is required!',
formMessageRequired: 'Field message is required!',
}
}
}
I'm also using this cookie for managing the language of this form validation:
if (getCookie("lang") == null) {
document.cookie = 'lang=pol';
}
setLang(getCookie("lang"));
$(".nav").on("click", "#polLang", (event) => {
document.cookie = 'lang=pol';
setLang(getCookie("lang"));
});
$(".nav").on("click", "#engLang", (event) => {
document.cookie = 'lang=eng';
setLang(getCookie("lang"));
});
Here I have validation
JS:
$("#contactForm").validate({
rules: {
"formFirstname": {
required: true,
},
"formLastname": {
required: true,
},
"formEmail": {
required: true,
},
"formTitle": {
required: true,
},
"formMessage": {
required: true,
}
},
messages: {
"formFirstname": {
required: Messages.formFirstnameRequired,
string: true
},
"formLastname": {
required: Messages.formLastnameRequired,
string: true
},
"formEmail": {
required: Messages.formEmailRequired,
email: true,
},
"formTitle": {
required: Messages.formTitleRequired,
string: true
},
"formMessage": {
required: Messages.formMessageRequired,
string: true
}
},
onfocusout: false,
errorPlacement: function(error) {
toastr.error(error);
},
submitHandler: function(form) {
toastr.success('success')
return false;
},
invalidHandler: function() {
return;
}
});
Jquery validator msgs are static. If you want to update them dynamically you can create a function, that will return a new msg.
Like this:
"formFirstname": {
required: function getFormFirstname() { return Messages.formFirstnameRequired },
string: true
},

Required property on a field doesn't change dynamically

One of my fields should be either mandatory(required) or not depending on one boolean variable. No matter if it changes or not the field stays required. Not sure what's wrong with my expressionProperties templateOptions.required as this is what triggers that change.
This is part of my formly form
vm.showDeleteButton = false;
vm.fields = [
{
className: 'row',
fieldGroup: [
{
className: 'col-xs-6',
key: 'transferDate',
type: 'datepicker',
templateOptions: {
label: 'Deallocation Date',
type: 'text',
datepickerPopup: 'dd/MM/yyyy',
minDate: vm.model.minDate,
maxDate: vm.model.maxdate,
},
expressionProperties: {
'templateOptions.required': !vm.showDeleteButton
}
}
]
}
];
Also tried this
expressionProperties: {
'templateOptions.required': function() {
if(!vm.showDeleteButton) {
return true;
else {
return false;
}
}
}
I've read the formly expressions documentation but that doesn't help either.
HTML as requested
<formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form"></formly-form>
Kind of 'hacked' it to work.
Changed vm.showDeleteButton to vm.model.showDeleteButton and in my expressionProperties wrote this
expressionProperties: {
'templateOptions.required': '!model.showDeleteButton'
}

Parsing initial values to an ExtraSignUp Fields Meteor

I am trying to add a hidden field for the user registration. The problem is not with the field itself but with its value. I want to parse it a default value. This is my code:
Accounts.ui.config({
requestPermissions: {},
extraSignupFields: [{
fieldName: 'name',
fieldLabel: 'Name',
inputType: 'text',
visible: true,
validate: function(value, errorFunction) {
if (!value) {
errorFunction("Please write your first name");
return false;
} else {
return true;
}
}
},{
fieldName: 'status',
fieldLabel: 'Status',
inputType: 'text',
value: 'somevalue',
visible: false,
}]
});
I want to add the value to the field 'status'.
Actually, I found the answer. The option is the following code in the
server folder:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
user.profile = options.profile;
}
user.profile.status = "sth";
return user;
});
Looking at the implementation of signup in that package, there is no way to set a default value. The code just creates an object in signup() and grabs whatever existing values from the form that it can.

Multiple instances of jQuery Validation Plugin on a JSP page

SOLVED: $.extend() twice in a row simply overwrites the previous object because objects are passed by reference. My fix was to do this:
var updateUserObj = $.extend({}, validationPluginDefaults);
var newUserObj = $.extend({}, validationPluginDefaults);
Then the validators are executed like this:
var whateverValidator = $.extend(updateUserObj,{new rules})
Update: Further research shows that what I'm trying to do appears to be correct but the problem may be in the object I am extending - "validationPluginDefaults"). Here it is below:
var validationPluginDefaults = {
ignore: [],
errorElement: 'p', //default input error message container <p>
errorClass: 'text-error', // default input error message class
focusInvalid: true, //focus on the first invalid field
messages: {},
invalidHandler: function (event, validator) { //display error alert on form submit
},
highlight: function (el) { // hightlight error inputs
//jQuery(el).closest('.control-group').addClass('error'); // set error class to the control group
},
success: function (err, el) {
jQuery(el).next(".text-error").hide();
jQuery(el).next(".text-error").remove();
},
errorPlacement: function (error, element) {
error.insertAfter(element);
},
onfocusin: function () {},
onfocusout: function () {}
};
From my testing it appears that even though I have two separate forms on a JSP page and each has different validation that I can still only use one jQuery Validate Plugin instance per page; is this correct?
Here's my code:
// Define validation rules for form one
var formOneValidator = $.extend(validationPluginDefaults,{
rules: {
firstName: {
minlength: 2,
onlyAlphaAndHyphen: true,
required: true,
},
lastName: {
minlength: 2,
required: true,
},
email: {
required: true,
email: true
}
}
});
var formTwoValidator = $.extend(validationPluginDefaults,{
rules: {
newFirstName: {
minlength: 2,
onlyAlphaAndHyphen: true,
required: true,
},
newLastName: {
minlength: 2,
required: true,
},
newEmail: {
required: true,
email: true,
}
}
});
$("#formOne").validate(formOneValidator);
$("#formTwo").validate(formTwoValidator);
What happens here is that formOne validator takes on all the rules of formTwoValidator after this code executes.
Is the solution to have only one validator and swap out rules depending on which form I'm in? It just seems strange that I wouldn't be able to define separate validators for each form on the page. Am I fundamentally misunderstanding something here?
you could use knockout and give each page a different view model which handles the different validation. That is how i would do it but i'm sure there will be a better answer.

Angular Formly Error Summary when using fieldGroups

Hopefully this will be a more generic Angular JS question rather than something specific to Angular Formly.
I've been following the framework provided here for building an error summary on the angular formly form. All works well.....but!
In the example, their model is as follows:
vm.fields = [
{
key: 'picky',
type: 'customInput',
templateOptions: {
label: 'Picky field...',
placeholder: 'This is required and has a maxlength of 5 and minlength of 3',
required: true,
maxlength: 5,
minlength: 3
}
},
.....
{
key: 'ip',
type: 'customInput',
validators: {
ipAddress: {
expression: function(viewValue, modelValue) {
var value = modelValue || viewValue;
return /(\d{1,3}\.){3}\d{1,3}/.test(value);
},
message: '$viewValue + " is not a valid IP Address"'
}
},
templateOptions: {
label: 'IP Address',
required: true,
type: 'text',
placeholder: '127.0.0.1',
}
}
];
Then, if we look at the HTML, we can see that these fields are being passed into the error summary as such:
<formly-error-summary form="vm.form" fields="vm.fields"></formly-error-summary>
For simple form structure, this works fine, but, if you want to use a Bootstrap layout, as described here then your model ends up looking something mine does:
vm.rentalFields = [
{
template: '<div class="row"><div class="col-xs-12"><h3>About You</h3></div></div>'
},
{
className: 'row',
fieldGroup: [
{
className: 'col-xs-6',
type: 'customInput',
key: 'first_name',
templateOptions: {
type: 'text',
label: 'First Name',
placeholder: 'Enter your first name',
required: true
}
},
{
className: 'col-xs-6',
type: 'customInput',
key: 'last_name',
templateOptions: {
type: 'text',
label: 'Last Name',
placeholder: 'Enter your last name',
required: true
},
expressionProperties: {
'templateOptions.disabled': '!model.first_name'
}
}
]
},
{
template: '<div class="row"><div class="col-xs-12"><h3>License and Insurance Details</h3></div></div>',
hideExpression: '!model.email'
}
.....
Now, when we pass in vm.rentalFields to the error summary, instead of accessing the fields, it instead just validates each object. I can get round this by doing something like:
<formly-error-summary form="vm.rentalForm" fields="vm.rentalFields[1].fieldGroup"></formly-error-summary>
This of course is not ideal since there will be fields in other field groups that I will want to validate, for proving the issue though it's fine for now. I have tried just passing in 'vm.rentalFields.fieldGroup' but as I suspected, that returns nothing.
So, is there a way I can recursively pass in all the fieldGroups within the vm.rentalField object or is this something that I should handle within the code of the Directive itself.
angular.module("formlyApp").directive('formlyErrorSummary', function() {
return {
scope: {},
bindToController: {
form: '=',
fields: '='
},
templateUrl: 'js/Directives/formly-error-summary.html',
controllerAs: 'vm',
controller: function() {
var vm = this;
vm.getErrorAsList = getErrorAsList;
console.log(vm.fields);
function getErrorAsList(field) {
return Object.keys(field.formControl.$error).map(function(error) {
// note, this only works because the customInput type we have defined.
return field.data.getValidationMessage(error);
}).join(', ');
}
}
};
});
EDIT
Ok, so, after taking advice from Ken below, I have been able to modify my formlyErrorSummary directive so that it now is at least able to get the errors for the model. This has numerous issues in it since the $scope.$watch is doing a deep level comparison and even on the first page load, the whole thing is fired 3 times! I've added in some rudimentary escapes to try and combat this and for now at least I have the errors, the next issue I have is within the HTML where I am call ng-repeat="field in vm.fields" which is effectively the same issue, so how would I work round this? Part of me is thinking of some anonymous object that would hold the fields message and whether or not is is valid and then parse that inside the HTML, but I'm not sure if this way of thinking is applicable to Angular?
controller: function($scope) {
var vm = this;
$scope.$watch('vm.fields', function(){
for(var i = 0; i < vm.fields.length; i++)
if(vm.fields[i].fieldGroup) {
for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
if(vm.fields[i].fieldGroup[j].formControl) {
var err = getErrorAsList(vm.fields[i].fieldGroup[j]);
if(err)
vm.getErrorAsList = err;
}
}
}, true);
SOLUTION - POSSIBLY
After much hacking around, I think I finally have this working so that the error messages are now displayed both inline and in a summary at the top.
My final directive function now creates an array each time it is run which will hold all of the error messages, it has to be flushed within the $watch otherwise when the field is valid, the error message will persist within the array so we simply rebuild the entire thing each time.....given I'm already using a deep level watch here, I'm hoping any performance hits will be negligible.
vm.errs = [];
$scope.$watch('vm.fields', function(){
vm.errs = [];
for(var i = 0; i < vm.fields.length; i++)
if(vm.fields[i].fieldGroup) {
for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
if(vm.fields[i].fieldGroup[j].formControl) {
var err = getErrorAsList(vm.fields[i].fieldGroup[j]);
if(err)
if(vm.errs.indexOf(err) === -1)
vm.errs.push(err);
}
}
}, true);
Then, within the directives template, I had to remove the vm.fields reference as that was obviously not going to work in this approach. Since I knew that this summary would only be shown if the form was invlaid, I could remove so of the other checks that were being carried out and eventually ended up with the following HTML:
<div class="row">
<div class="col-xs-12">
<div class="formly-error-summary bg-danger" ng-if="vm.form.$invalid">
<div ng-repeat="err in vm.errs" class="color-error">
<i class="glyphicon glyphicon-remove"></i>
<span>
{{err}}
</span>
</div>
</div>
</div>
</div>
I'm still not 100% happy with this, it gets the job done, but I'm not sure if it's the 'Angular' way of doing this and the fact I'm using $scope.$watch on the fields object is a little bit annoying to my developer OCD, but a solution it is all the same.
If anyone has any refinements or suggestions for improvements to this let me know please, still getting to grips with Angular but this has been a pretty fun learning experience!

Categories

Resources