Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I override Angular's filtering of invalid form values, forcing Angular to persist the $viewValue to $modelValue?

I need to be able to temporarily persist data that isn't fully validated yet, then enforce validation when I'm ready to make it permanent. But Angular is preventing that.

I have a form. The user can saveDraft() on early versions of the form, which are persisted to the server. Then, when the user is ready, they can submit() the form, which will persist it with different flags, thus beginning the actual processing of that data.

The problem I'm running into is with Angular's built-in validations. When a user enters some data into an input with validations on it, that data is cached on the $viewValue property. But if validation fails, it's never copied to the $modelValue property, which is a reference to the actual $scope property I bound the input to. And hence the 'invalid' value will not be persisted.

But we need to persist it. We'll deal with forcing the user to correct their validation failures later, when they submit(). Also, we have no way to know whether the user is going to saveDraft() or submit() on a particular instance of the view/controller, so we can't setup the views and validation differently beforehand.

My thinking is that we need to somehow iterate the form elements and get Angular to somehow let the data go through. But I can't find a way that's flexible and scalable.

I've tried setting $scope.formName.inputName.$modelValue = $scope.formName.inputName.$viewValue, but that seems to just upset the gods, as both values are then null'ed.

I've tried using $scope.formName.inputName.$setValidity('', true), but that only updates the UI state. It never touches $modelValue.

I can successfully use $scope.model.boundPropertyName = $scope.formName.inputName.$viewValue but that feels very imperative and won't allow any variance between the identifiers for boundPropertyName and inputName. In other words, you either need individual functions for all form controls, or you need to rely on naming conventions. Both of which are super-brittle.

So... how can I get that $modelValue updated elegantly? And/Or, is there another, better way to obtain the same results, whereby sometimes I can persist with validation, and sometimes persist without?

Other options I'm considering, but not happy with:

  • Run validation manually, only when the user hits submit(). But that defeats the UX value of instant, inline validation in the UI. We might as well just offload all the validation to the server and do a round-trip each time.
  • Make copies of ngModel and ngModelController, and monkey-patch them to update $modelValue regardless of validity. But that hacks the framework when there should be a more elegant path.

See CodePen here.

(Side-note: Angular appears to be filtering the data according to the validator in both directions. If you set a default value on the model for formData.zip of '1234', which isn't enough characters to validate, Angular doesn't even show it. It never reaches the initial $viewValue.)

like image 796
XML Avatar asked Jan 25 '14 02:01

XML


People also ask

How do I refresh a form in AngularJS?

1 Answer. Show activity on this post. $('#form_id'). trigger("reset");

What is Ng touched?

With Angular 1.3 and ng-touched , you can now set a particular style on a control as soon as the user has visited and then blurred, regardless of whether they actually edited the value or not.


1 Answers

The following solution can be used since Angular version 1.3:

You can set ng-model-options="{ allowInvalid: true }" in the input field of the model where you want to persist invalid attributes.

allowInvalid: boolean value which indicates that the model can be set with values that did not validate correctly instead of the default behavior of setting the model to undefined

https://docs.angularjs.org/api/ng/directive/ngModelOptions

Then, when you are ready to show the user their validation errors, you are free to do it your way. Just remember to give your inputs and forms name attributes, so that you can reference them in your scope.

E.g. if($scope.myFormName.my_input_name.$invalid) { ... }

A relevant tutorial: http://blog.thoughtram.io/angularjs/2014/10/19/exploring-angular-1.3-ng-model-options.html

like image 135
Alexandros Vellis Avatar answered Nov 12 '22 08:11

Alexandros Vellis