Submit form: Assert that the DOM is completely rendered - angularjs

I’d like to submit a form but add some hidden inputs first. The hidden inputs are added with a ng-repeat. Eventually they will be rendered, but how can I be sure that the DOM is already updated prior to triggering the submit event?
partial:
<form ng-submit="addValuesAndSubmit()">
<input type="hidden" name="{{name}}" value="{{value}}" ng-repeat="(name, value) in order">
<input type="submit">
</form>
controller:
$scope.addValuesAndSubmit = function() {
$scope.order = { param1: 1, param2: 2 };
// TODO: wait until form is rendered
// there should be two <input type="hidden"> now
// trigger submit action
};
As I understand it, a post-linking function for a directive could be used for this. Am I right or am I missing something obvious?
EDIT: The form data needs to be posted to an external website, redirecting the browser to the response. It’s a payment integration where I calculate an HMAC on the server, add it to the form as a hidden element and then post it to the payment provider. The implementation of $http.post() seems not to do that redirect but returns the response instead.

With Angular apps, you often want to submit your model, rather than extract your data from form elements. I.e., use ng-model to specify $scope model object properties, then submit that model object via ng-submit. It would seem to me that you already have access to order in your $scope, since you are trying to get your associated view to use it. So you probably don't need to wait for the view to update -- just use order directly in your addValuesAndSubmit() function.

Related

Angularjs form.$dirty

I'm able to find form data is changed or not using $dirty.
ex: I changed text box or drop down and then $dirty become true. If I reverted to old data still it is true. I need to know if my changes are reverted or not. Do we have any property in Angularjs? If property is true I want to enable save button otherwise it should be disable.
https://docs.angularjs.org/api/ng/type/form.FormController
I need to implement around 10 pages and each page has 10 text boxes and a couple of drop downs. So I don't want track each control manually in my pages.
You can try using this module: https://github.com/betsol/angular-input-modified
From the README file:
This Angular.js module adds additional properties and methods to the
ngModel and ngForm controllers, as well as CSS classes to the
underlying form elements to provide end-user with facilities to detect
and indicate changes in form data.
This extra functionality allows you to provide better usability with
forms. For example, you can add decorations to the form elements that
are actually changed. That way, user will see what values has changed
since last edit.
Also, you can reset an entire form or just a single field to it's
initial state (cancel all user edits) with just a single call to the
reset() method or lock new values (preserve new state) just by calling
overloaded $setPristine() method.
DISCLAIMER: I haven't tried it myself and I notice the author overwrites the ngModel directive instead of adding a decorator, which could be dangerous...but at the very least, you can look at the source and get an idea of how to write your own service or directive with similar functionality.
Even though it does not follow the usage of $dirty, but an implementation similar to this might be helpful for you in the case of a Save button on update.
Inside your html:
<form name="testForm" ng-controller="ExampleController" ng-submit=" save()">
<input ng-model="val" ng-change="change()"/>
<button ng-disabled="disableSave">Save</button>
</form>
Inside your controller:
.controller('ExampleController', ['$scope', function($scope) {
$scope.disableSave = true; // Keep save button disabled initially
$scope.val = 'Initial'; // Initial value of the variable
var copyVal = $scope.val; // Copy Initial value into a temp variable
$scope.change = function() {
$scope.disableSave = $scope.val === copyVal;
};
$scope.save = function() {
// Save the updated value (inside $scope.val)
console.log($scope.val);
// Re-disable the input box (on successful updation)
copyVal = $scope.val;
$scope.disableSave = true;
};
}]);
Here is a working plunkr for the same.

Access an angular page from a form

I would like to access an angular page from an html form. But when I submit the form via method get and with parameter I get some problem with the url.
Here is a simple form to reproduce the problem :
<form action="http://ncel28182/pl/Angular/#/FFCO">
<input type="text" name="test">
<button>toto</button>
</form>
When I click on the submit button, it open a page where the url is
http://ncel28182/pl/Angular/?test=#/FFCO
It seems that the parameters are placed before the #.
Is there a way to fix it ?
I believe you should use $location to change location, and a service to share data, as in the following :
HTML :
<form name="myForm" ng-submit="submit()">
<input type="text" name="test">
<button>toto</button>
</form>
Javascript :
function submit($scope, $location, MyService) {
MyService.data = $scope.myForm.test;
$location.url("/pl/Angular/#/FFCO");
}
In the other's page controller, define the service as a dependency and you will be able to access the up-to-date data.
Please note that I'm not a professional angularjs user or even a webdevelopper, you should probably wait for other users to react to my answer or add their own.
Since you can't edit the form sending data, you can't use ngRoute to directly route to the desired "FFCO" view because ngRoute matches against $location.path, whereas the form sends the "FFCO" part as the $location.hash. Since you have no $location.path provided, the form will target your application root.
What you will want to do is add some js code in this page to handle the location change :
if ($location.hash() === "/FFCO") { $location.path("/FFCO"); }
This should do the trick, translating the url requested by the form to a format understandable by your angularjs application. I don't think this will change the hash, if an extra #/FFCO by the end of your URL is a problem you should remove it with $location.hash('').
I believe the best place to do so would be in an extra controller added somewhere after your app bootstrap code.
This is obviously still sub-optimal, I wish I had a better solution.

Disable Angular Form Validation on single input (url)

I'm building an Angular form with a URL field that can be formatted either with a prefix (http://) or without. Using Angular's default url validation requires http://, so I plugged in the following regex, which accepts with and without a prefix:
<input type="text" id="siteAddress" name="siteAddress" ng-model="user.url" ng-pattern="/^(https?:\/\/)?([\dA-Za-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/" required>
This works great, but when I set type="url", Angular's validation overwrites my custom ng-pattern. This form is for mobile users only, so the type="url" is important so that they receive the correct keyboard, and so their mobile OS does not attempt to autocorrect their input.
Is it possible to use type="url" without Angular applying its default validation, or is it possible to modify Angular's default validation so that it accepts no url prefix?
Thanks!
It seems like there is no official API to do that but you always can override default directive or use your directive with higher priority to change the standard behavior.
For example, angular's input directive gets the type from $attributes object, so you may remove this attribute.
directive('ignoreType', function() {
return {
priority: 500,
compile: function(el, attrs) {
attrs.$set('type',
null, //to delete type from attributes object
false //to preserve type attribute in DOM
);
}
}
});
Here is jsfiddle for it.
Of course this solution may also break behavior of other directives which work with type attribute, so use it carefully.
An idea would be to test the $removeControl method from the FormController to see if the native url validation would go off...
http://docs.angularjs.org/api/ng.directive:form.FormController
Inside your controller you would do something like this:
$scope.form.$removeControl('siteAddress');
Im not sure exactly what it will do, since I haven't got the chance to test it out and the docs about this are incomplete... let me know if this works please.
Hope that could give you some help.

How to automatically submit form when it becomes valid in AngularJS

There's one field in the form which has strict format (validator is already in place).
How to make form submit automatically in AngularJS as soon as it becomes valid?
I am assuming you are talking about ajax post of form data (model data).
You can do a $watch on the $valid property of a form field and submit the form as soon as it becomes true.
See my fiddle here
Something like
$scope.$watch('myForm.userName.$valid',function(newValue,oldvalue) {
if(newValue) {
alert('Model is valid');
//Can do a ajax model submit.
}
});
You can get mode details from the form directive documentation

AngularJS : What's the Angular way to interact with a form?

I understand one of the key principals of Angular is:
Thou shalt not reference thy DOM from withinst thou's controllers.
I'm trying to process a credit card payment, which requires the following steps:
User fills out a form, and clicks a submit button
A portion of that form is sent to our servers, which starts a transaction with the payment gateway
The response from our servers updates values in the form, which must then be submitted directly to the payment gateway, via a form POST.
Other stuff happens.
In this scenario, how do I:
Update the data in the form (without referencing the form from the controller)
Get the form to submit?
The form binds to a model on my controller, so I've tried something like the following:
<form action="{{paymentModel.urlFromTheResponse}}">
<input type="hidden" name="accessCode" value="{{paymentModelaccessCodeFromResponse}}" />
<button ng-click="startTransaction(paymentModel)"></button>
</form>
// in my success handler
.success(function(data) {
paymentModel.urlFromTheResponse = data.url;
paymentModel.accessCode = data.accessCode;
$scope.apply();
}
the theory being here that if I can immediately get the form into the correct state via databinding, I can then do something to submit the form. However, this throws an error:
Digest already in progress
What's the Angular way to support this type of flow? It seems I'm required to interact directly with the DOM, which goes against the nature of controllers.
As others have stated, you shouldn't need to call $scope.$apply() because the form should already be tied to angular by setting ng-model attributes on each of the fields.
However, occasionally it is necessary to call $scope.$apply() to update display when data is pulled in from some other source outside of angular...
In those cases, I've had great luck with this:
// This method will be inherited by all other controllers
// It should be used any time that $scope.$apply() would
// otherwise be used.
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
I place that in my outermost controller, so all other controllers on the page inherit the function from it.. Any time I find I need a call to apply, I instead call $scope.safeApply() which will call apply if there is not already an apply or digest in progress, otherwise, those changes will already be picked up by the currently running apply/digest.
In your code I would change this:
<input type="hidden" name="accessCode" value="{{paymentModelaccessCodeFromResponse}}" />
To this:
<input type="hidden" name="accessCode" ng-model="paymentModel.accessCode" />
I would probably also remove the form action, and instead add something like this in the controller:
$scope.$watch('paymentModel.accessCode', function() {
// Fire off additional form submission here.
})
The error is generated because your success callback is already "inside Angular", so $scope.apply() will be called automatically for you.
If you use ng-model (instead of value) on your form elements, then you can modify the model/$scope properties in your success callback and the form will automatically update (due to two-way databinding via ng-model). However, instead of trying to submit the form, why not just use the $http or $resource service inside your controller to call the web service? (That's why I asked if the user needed to be involved in my comment.)
Assuming you are using something like $http, you are already inside of the angular scope and should not need to manually call $scope.apply(); as you are inside of the angular execution already.
You should be able to ditch the $scope.apply() and simply have an
.success(function(data) {
paymentModel.urlFromTheResponse = data.url;
paymentModel.accessCode = data.accessCode;
$http.post("/finalstep",paymentModel).success(function(data)
{
// other stuff
});
}

Resources