angular ng-model don't update on dirty value - angularjs

i would like to know if it's possible to 'prevent' an update of an form field (input, select....) that it's dirty due to user interaction.
If i update the ng-model primitive (ie: set pippo from 1 to X) the input
will get updated even if i manually edit the control.
It's possible to prevent that?
demo page:
http://plnkr.co/edit/ClXoS7YVcEDtcApsNpde
the input field count UP every X secs.
if i enter "AAA" i would like that update STOP, because
the input it's "dirty" due to user interaction.

You can use ngModelController to tell if the input is dirty. https://docs.angularjs.org/api/ng/directive/ngModel
Modified your plunker to http://plnkr.co/edit/oJMrQZLugnzisqdXScvN?p=preview
The update function looks like
this.update = function() {
if(!$scope.frmPippo.pippo.$dirty)
$scope.pippo += 1;
}

add ngChange attribute on input element,
Update Plunker
<input type=text ng-model="pippo" ng-change="userChanged()"/>
//controller
$scope.pippo = 1;
$scope.isUserChanged = false;
$scope.counter= 1;
$scope.userChanged = function(){
$scope.isUserChanged = true;
}
this.update = function() {
if(!$scope.isUserChanged){
$scope.pippo += 1;
}
}
$interval(this.update, 3000);
userChanged would be called when user typing, but would not be called when pippo changed by other methods in controller, so we could set a flag in userChanged to indicate whether the input has been overwrite by user

If I got the question maybe this is what you are trying to do?
$scope.$watch("pippo", function(){
if($scope.pippotmp != $scope.pippo)
$scope.useredited = true;
});
UPDATED Answer
plunker
you could keep the model separated from that input changes... in a temporary variable

I might get the question wrong, but if I understand you correctly, then ng-model-option is what you are looking for.
For example, you can update the model only when the user leave the input
ng-model-options="{ updateOn: 'blur' }"
You can read more about it in the doc: https://docs.angularjs.org/api/ng/directive/ngModelOptions

Related

angularjs calling select on corresponding HTMLinput doesn't highlight cell contents if value comes from ng-model?

I am trying to select (highlight) the text in an input element whose value comes from a model, but it doesn't seem to work.
The element (part of component template) in question is as follows:
<input id="table-input-cell" ng-blur="$ctrl.inputBlur()"
ng-focus="$ctrl.inputFocus($event)" ng-model="$ctrl.activeText"
class="input-cell"/>
The controller function looks like this:
ctrl.inputFocus = function(focusEvent) {
let el = ctrl.inputView[0];
console.log(el.constructor.name);
console.log(`inputFocus called. contents: ${el.value} `);
el.select();
};
What I am seeing as the result on the page is this:
What I would like to see is this:
Here's the console output:
HTMLInputElement
budget_controller.source.js:261 inputFocus called. contents:
So it looks like the problem is that the ng-focus may be called before the value of the input box is populated by ng-model, which is why .select() isn't highlight anything.
Or maybe it something else? Anyway, is there some other life-cycle event in AngularJs I can use to set the selection range once the element is bound as has focus? I don't see any thing ngBound= in the dox.
Use the event target:
ctrl.inputFocus = function(focusEvent) {
̶l̶e̶t̶ ̶e̶l̶ ̶=̶ ̶c̶t̶r̶l̶.̶i̶n̶p̶u̶t̶V̶i̶e̶w̶[̶0̶]̶;̶
let el = focusEvent.target;
console.log(el.constructor.name);
console.log(`inputFocus called. contents: ${el.value} `);
el.select();
};
The DEMO on PLNKR

Angular can I have 2 ng-pattern to validate the same filed

My current input looks like this
<input type="email" name="email"
ng-pattern="emailRegex"
ng-change="emailChanged()"
required />
My ng-pattern="ctrl.emailRegex" validates if an email is valid or not
/^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
But I would like to block info#, admin#, help#, sales# emails, so I changed the regex to
/^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/
So far so good, but I would like to show
Invalid email
to "invalid#!!!!!.com!"
and
info#, admin#, help#, sales# emails are not allowed
to info#test.com
How can I have 2 ng-pattern in the same input?
Thanks
You can validate only one pattern for an input. And, even if you can sort of do it somehow by using a directive, it would be too dirty a solution. Instead, I would recommend validating the input against regex(es) inside the function of ng-change and use formName.inputName.$setValidity to set custom validity of the input. This lets you have a fallback if one pattern is passed.
So, for example, ctrl.emailChanged could probably have something like this,
ctrl.emailChanged = function() {
var emailPattern = /^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
var customValidateEmail = /^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
if(!emailPattern.test(ctrl.registrationForm.email)) {
// Invalid email
} else if (customValidateEmail.test(ctrl.registrationForm.email)) {
// handle accordingly
}
// rest of the things
...
}
Alternatively, you can move the validation logic to another function and just call it from emailChanged.

Angular - using ngModel.$parsers.push and forms

I am using ngModel.$parsers.push to change the user input:
ngModel.$parsers.push(function (value) {
//some logic here - changing the vaue
//set the new value
ngModel.$setViewValue(value);
// renders the input with the new viewValue
ngModel.$render();
return ture; //in any case
});
It works fine but it makes the form field unvalid and shows form.invalid.parse under it.
I have tried to return the value or nothing but it still does not work.
Any ideas?
Thanks.
From AngularDoc
Returning undefined from a parser means a parse error occurred. In
that case, no $validators will run and the ngModel will be set to
undefined unless ngModelOptions.allowInvalid is set to true. The parse
error is stored in ngModel.$error.parse.
Thus, try this in your input field
<input ng-model-options="{ allowInvalid: true }" />

Ask to confirm when changing tabs in angular bootstrap

I have tabs with forms and I want ask the user to confirm or discard their changes when changing tabs. My current code works
<uib-tab heading="..." index="3" deselect="main.onDeselect($event)" ... >
this.onDeselect = function($event) {
if(...isDirty...) {
if($window.confirm("Do you want to discard?")) {
... discard (and go to new tab) ...
} else {
$event.preventDefault(); //stays on current tab
}
}
}
The problem is I want to change confirm to javascript dialog and I will get result in callback.
I planed to preventDefault() all and then switch manually, but I cannot figure out where to get new tab id.
Any solution is appreciated. Even if it is easier in other tab implementations.
I use AngularJS v1.4.7, ui-bootstrap-tpls-1.3.3.min.js
You can make use of $selectedIndex and the active property for that purpose. See this Plunk
One thing to be noted here is that when we manually change the active property, it again fires the deselect event which needed to be handled. Otherwise it seems to do what you wanted.
Edit
Indeed as noted in the comments, the deselect carries the HTML index rather than what is passed in in the tab index property. A workaround could be in this: Another Plunk. Here I'm pulling the actual index from the HTML index.
And a little research indicates that this issue might as well be fixed already with 3.0 bootstrap tpl See this.
I spent some time with different approaches and this one is stable for some time. What I do is to prevent deselect at the beginning and set the new tab in callback if confirmed to loose changes...
this.onDeselect = function($event, $selectedIndex) {
var me = this;
if(this.tabs.eventDirty || this.tabs.locationDirty || this.tabs.contractDirty) {
$event.preventDefault();
var alert = $mdDialog.confirm({
title: 'Upozornění',
textContent: 'Na záložce jsou neuložené změny. Přejete si tyto změny zrušit a přejít na novou záložku?',
ok: 'Přijít o změny',
cancel: 'Zůstat na záložce'
});
$mdDialog
.show( alert )
.then(function() {
$rootScope.$emit("discardChanges");
me.tabs.activeTab = $selectedIndex;
})
}
};

Warnings in AngularJs

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

Resources