Angularjs has great infrastructure for form validation and showing error messages. But, I am in a situation that I have to show a warning message to a user in a specific scenario. Here is the diagram of my simple form
The form has required and pattern validation applied on both fields. In addition to this validation I want a warning message to be displayed to the user if VatAmount is not 20 percent of the InvoiceAmount. The warning will differ from validation in following aspects
It will not prevent the form submission
It will only appear if both fields (InvoiceAmount and VATAmount) are
valid
The warning should have a button or link that would read "Change and
proceed". When user presses that button the warning message will
hide and focus will be set to VATAmount field.
I believe this is a prefect use case for creating a directive. Actually, I have given it a try and put my effort in the form of a plunker. But my directive does not handle following cases
It appears even if the fields involved in warning are invalid
The show and hide functionality is not implemented (have no idea how
to target it)
Here is the link to the plunker
Your plunkr demo was on the right track; really you just needed to check for the special cases of when one of the values was empty.
I'd suggest calculating the fraction and storing it in the scope, and then watching that to see whether you should display your tax rate warning. Here's how to calculate the fraction. If either invoice.Amount or invoice.VAT is empty, the fraction will be set to null.
if (amt == null || vat == null) {
$scope.warning.fraction = null;
return;
}
$scope.warning.fraction = vat / amt;
This works because those properties will be set to undefined if the user doesn't enter a valid number due to your use of ng-pattern.
However, while it's nice to encapsulate this in a directive, you don't need to compile the template yourself. Just use the built-in ng-transclude directive. Then you can include a button that references the current scope like this:
<vat-warning>
Vat Amount is not 20%.
<button ng-click="invoice.VAT = invoice.Amount / 5">change</button>
</vat-warning>
Your directive would contain this declaration:
transclude: true,
template: '<span class="alert-warning" ng-show="warning.show" ng-transclude></span>'
Plus a controller to update the directive's local scope to manipulate the warning object. Here's an updated demo.
You need to calculate visibility of vat-warning tag in controller on basis of $error.required and $error.pattern of invoiceAmount and vatAmount and then use it as below:
$scope.isInvoiceAmountInvalid = function () {
var error = $scope.invoiceForm.invoiceAmount.$error;
var required = error.hasOwnProperty("required") && error.required;
var pattern = error.hasOwnProperty("pattern") && error.pattern;
console.log("Inside isInvoiceAmountInvalid", error, required, pattern);
return (required || pattern);
};
$scope.isVatAmountInvalid = function () {
var error = $scope.invoiceForm.vatAmount.$error;
var required = error.hasOwnProperty("required") && error.required;
var pattern = error.hasOwnProperty("pattern") && error.pattern;
console.log("Inside isVatAmountInvalid", error, required, pattern);
return (required || pattern);
};
Here is an updated plunker for the same
Related
When I use custom filters and native and design filters in the same problem I get next error
Warning: [antd: Table] `FilteredKeys` should all be controlled or not controlled.
I can use just custom filters on just ant design filters when create table to avoid this warning, but maybe there is a way avoid it different way, or it's just library bug.
I find topic where developer say that this is not a bug and the problem was closed, but
why I get red warning in my project build and development. It's ok?
Since you haven't demonstrated your code I can only show how I resolved the same issue in my case.
Initially, filters for my columns were set like this:
for (let col of columns) {
if (!filter[col.key])
continue;
col.filteredValue = filter[col.key].value || null;
}
The code triggered the same warning as you get. In documentation for the table component is only said that
Defining filteredValue or sortOrder means that it is in the controlled
mode
Whereas if we go down the stack to the file that throws it, we'll find the next function:
var mergedFilterStates = React.useMemo(function () {
var collectedStates = collectFilterStates(mergedColumns, false);
var filteredKeysIsNotControlled = collectedStates.every(function (_ref5) {
var filteredKeys = _ref5.filteredKeys;
return filteredKeys === undefined;
}); // Return if not controlled
if (filteredKeysIsNotControlled) {
return filterStates;
}
var filteredKeysIsAllControlled = collectedStates.every(function (_ref6) {
var filteredKeys = _ref6.filteredKeys;
return filteredKeys !== undefined;
});
(0, _devWarning["default"])(filteredKeysIsNotControlled || filteredKeysIsAllControlled, 'Table', '`FilteredKeys` should all be controlled or not controlled.');
return collectedStates;
}, [mergedColumns, filterStates]);
Simply put, there is a check whether all column filters are either controlled (column.filteredValue !== undefined) or uncontrolled (column.filteredValue === undefined).
So, to get rid of the warning I indicated that all my column filters are controlled (even if they aren't filtered at this particular moment, they aren't undefined either):
for (let col of columns) {
col.filteredValue = (filter[col.key] || {}).value || null;
}
As of why this warning was introduced, we can see here that the change was intended to prevent some edge cases when one could accidentally use filteredValue alongside onFilter and break filters for other columns. That issue could be resolved by changing filteredValue to defaultFilteredValue, but it was decided to add the warning instead. However, in that case satisfying the warned condition wouldn't guarantee the right behaviour: setting up filteredValue for all the columns turns off the warning but don't make the problematic filter workable.
So, answering the second part of your question if this warning makes sense, it depends as in my case all the column filters worked just fine even with this warning displayed.
In short:
Make sure to have filteredValue with a string | undefined where you have filters set for each column
I'm adding tag by selecting from list (which is populated using $http request). The tag is added but the text which I have typed that remains there with ng-invalid-tag class.
ScreenShots
1) Initially,
2) Typing 3 letters to get HTTP Call.
3) Now after selection of first Skill "Angular Js'.
4) It shows that .input.invalid-tag is enabled. And which doesn't clear the placeholder.
My Input Tag is as below.
<tags-input ng-model="employerMyCandidatesCtrl.skillList" placeholder="Skills..."
replace-spaces-with-dashes="false"
add-from-autocomplete-only="true"
display-property="skillName"
on-tag-added="employerMyCandidatesCtrl.addTagToSkillData($tag)"
on-tag-removed="employerMyCandidatesCtrl.removeTagFromSkillData($tag)">
<auto-complete
source="employerMyCandidatesCtrl.loadSkillData($query)"
displayProperty="skillName" debounce-delay="500"
min-length="3">
</auto-complete>
</tags-input>
Controller Code is as below.
vm.skillList = [];
vm.loadSkillData = function(query) {
return EmployerServices.getAllSkillsPromise(query); // $http call.
};
vm.addTagToSkillData = function(tag) {
if (_.findIndex(vm.skillList, tag) < 0) {
vm.skillList.push(tag);
}
};
vm.removeTagFromSkillData = function(tag) {
var ind = _.findIndex(vm.skillList, tag) > -1 ? vm.skillList.splice(ind, 1) : '';
};
Is any configuration mistake I'm doing?
There are 4 attributes for onTagAdding, onTagAdded, onTagRemoving, onTagRemoved so the basic difference between the attributes ending with adding compared to those ending with added is
Adding suffixed tags are expecting a boolean which when true will be added
or removed based on the tag used.
But onTagAdded/Removed already adds the tag, before the function is called hence we can do some additional logic or else strip the ng-model of the added value or add back the removed value(not very easy).
Check the below JSFiddle to see the four attributes in action here
I have made a custom service to supply the data, so the final answer to your question will be to use the appropriate attribute (onTagAdding, onTagAdded, onTagRemoving, onTagRemoved) based on your usecase. From the above code, I think we need not write onTagAdded, onTagRemoved since its done automatically.
I have an input field that is supposed to contain numbers.
It is bound to an object property.
I want input entered as 4,5 to automatically get converted to 4.5 in both model and view.
HTML:
<input data-ng-model="productContent(product.Id).Org" value="{{productContent(product.Id).Org | replaceComma}}" />
Control:
$scope.productContent = function (prodId) {
var content = $.grep($scope.productsContent, function (el) { return el.ProdId === prodId });
return content[0];}
Filter:
app.filter('replaceComma', function () {
return function (val) {
return (typeof val) == "string" ? val.toString().trim().replace(",", ".") : val
};
});
Result:
When I enter a number, at first the model (productContent) retrieves the correct object. Then the filter code is called and returns a correctly converted string. I would expect both the model and view to be updated to the filtered value, but both are updated with the unfiltered value. What am I doing wrong?
I have faced the same problem in the past but instead of creating my own filter, I took a different path and found something ready to use instead.
angular-input-masks by assisrafael one of my favourite angular extensions for this purpose:
https://github.com/assisrafael/angular-input-masks
Examples:
http://assisrafael.github.io/angular-input-masks/
Since the author has written the documentation, I don't want to get extensive on it and be outdated in the future. As a quick reference, look for ui-number-mask.
Maybe this is not a direct answer to your question, since it's not replacing commas with periods, but making you type the decimals instead.
On a side note, you can suppress the thousands separators with ui-hide-group-sep
I hope that's helpful, otherwise leave a comment and I'll be happy to continue to assist you!
-Helvio
I'm using parsleyjs to validate my forms client-side. I have a scenario where, on click of a checkbox, some more form fields are exposed and need validating. If the checkbox is then unclicked the form fields are hidden and validation needs removing.
Is there functionality to achieve this in parsley? I've looked through the docs but can only find details of how to validate through attributes in the html. I'm looking for a method I can call in code to add and remove fields to be validated.
I ran into this exact situation a couple of weeks ago. You do need an extra bit of js to accomplish this. I dug around and found one bit of script that came close but needed some tweaking to suit my needs: it depended on predefined field definitions. No bueno. I wanted it to duplicate fields regardless of their names/ids/whatever. Then, of course, increment each new field name. Also without cloning whatever values had been entered by the user.
Since the fields being cloned already have the necessary parsley validation, that just went right along with them.
Here's the cloning code I came up with. I'm sure it can use improvement. And here's a fiddle with a working example.
this is my first contribution after years of lurking. be gentle. ;)
$('#btnDel').prop('disabled', true);
$('#btnAdd').prop('disabled', false);
$('#btnAdd').click(function() {
var num = $('.clonedInput').length;
var newNum = new Number(num + 1);
var newElem = $('#input1').clone().val('').attr('id', 'input' + newNum);
newElem.find(':input').attr('id', function () {
return this.id + '_' + newNum
});
newElem.find(':input').attr('name', function () {
return this.name + '_' + newNum
});
newElem.find(':input').val('');
$('#input' + num).after(newElem);
$('#btnDel').prop('disabled', false);
if (newNum == 5){
$('#btnAdd').prop('disabled', true);
}
});
$('#btnDel').click(function() {
var num = $('.clonedInput').length;
$('#input' + num).remove();
$('#btnAdd').prop('disabled', false);
if (num-1 == 1){
$('#btnDel').prop('disabled', true);
}
});
$('#btnDel').prop('disabled', true);
});
Parsley is great for most validation scenarios but in the past I've struggled to get it to do what I need - the lack of ability to programmatically interact with the validation lifecycle makes it less useful for more complex validations.
I wrote a library called Okjs that works in a very similar fashion to Parsley but with the added benefit that when you need to do something like add a new validator to a field based on user interaction there's a code API to allow you to do so:
https://github.com/jamesfiltness/Okjs
To add fields on click of a checkbox in Ok would go like so:
$('.my__checkbox').focus(function() {
Ok.Form.addField('checkbox1', ['required']);
Ok.Form.addField('checkbox2', ['required']);
})
I'm trying to create my own validation for password confirm, and putting my error on $error. this is my code:
html:
<input ng-model="user.password2" type="password" name="password2" required
ng-keyup="confirmPassword(user.password, user.password2)">
<div ng-messages="register.password2.$error" ng-if="register.password2.$dirty">
<div ng-message="required">Password is required</div>
<div ng-message="passwordsDontMatch">Passwords don't match</div>
</div>
JS:
$scope.confirmPassword = function (pass1, pass2) {
if (angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()) {
$scope.register.password2.$error["passwordsDontMatch"] = true;
} else {
delete $scope.register.password2.$error["passwordsDontMatch"];
}
console.log($scope.register.password2.$error);
};
it looks like it's working. when the passwords are the same, the message is not displayed and indeed the $error object is empty. But the input is still invalid: ($scope.register.password2.$invalid == true)
you can see what I'm talking about in this plunkr: http://plnkr.co/edit/ETuVqsdSaEBWARvlt4RR?p=preview
try 2 identical passwords. the message will disappear but when you blur from the input, it's still red because internally it's $invalid
The problem probably comes from the fact that you're not typing a password in the first field that matches your regex pattern. The first password is thus undefined, since it doesn't respect the ng-pattern validation rule.
That said, you shouldn't modify the $error array directly. Instead, you should set the validity of the field using $setValidity(). That will not only set and remove the error automatically, but also deal with the $invalid/$valid properties, add and remove the CSS classes, etc.
var valid = !((angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()));
$scope.register.password2.$setValidity("passwordsDontMatch", valid);
Here's a working example. But remember to enter a valid password in the first place.
Also, instead of implementing this check with ng-keyup, you should make it a directive, which would add a validator to the validators of the form input. This would make sure the check is made whatever the way the second password is entered (i.e. via copy/paste using the mouse only, or simply by prepopulating the form programmatically.