I'm trying to automate some form entry on a website that I do not have the source code to. Finding the appropriate fields and filling them out with js and submitting the form programmatically is a simple enough task. But the website is built in Angular and, when the form submit is clicked, all the validation flags for the input fields pop up as if none of the fields were filled out.
Browsing several other posts, I came across the realization that I need to somehow either set the variable inside a scope, like this:
$scope.$apply(function() {
$scope.fieldName = varname;
});
or to set the field to dirty, like this:
$scope.fieldname.$dirty = true;
Unfortunately, not having access to the code, I'm unsure of what the scope might be, or how to appropriately tell Angular that the fields on the form have been updated programmatically.
Edit
I'm using Roblox as an example for this error.
Different forms on the site such as the registration form (and forms behind the login) have validation on them, which throws the errors I mentioned.
Here's an example I made of trying to use the same logic as the login script on the register script:
// ==UserScript==
// @name Roblox Account Create
// @namespace http://roblox.com/
// @version 0.1
// @description Create Roblox Account
// @author You
// @match https://www.roblox.com/
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @grant none
// ==/UserScript==
/* jshint -W097 */
'use strict';
var _adj = [ 'Cool', 'Masked', 'Bloody', 'Lame' ];
var _animals = [ 'Hamster', 'Moose', 'Lama', 'Duck' ];
var _months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
var _username = _adj[ parseInt( Math.random( ) * _adj.length ) ], _pass = Math.random( ).toString( 36 ).substring( 2, 10 );
_username += _animals[ parseInt( Math.random( ) * _animals.length ) ];
_username += parseInt( Math.random( ) * 1000 );
var _dt_month = _months[ Math.floor( Math.random( ) * ( 12 ) + 0 ) ];
var _dt_day = Math.floor( Math.random( ) * ( 28 ) + 1 );
var _dt_year = Math.floor( Math.random( ) * ( 2005 - 1916 + 1 ) + 1916 );
$( '#Username' ).val( _username );
$( '#Password' ).val( _pass );
$( '#PasswordConfirm' ).val( _pass );
$( '#MonthDropdown' ).val( _dt_month );
$( '#DayDropdown' ).val( _dt_day );
$( '#YearDropdown' ).val( _dt_year );
$( '#FemaleButton' ).click( );
$( '#SignupButton' ).click( );
I have tried to add both the input and change events to my calls after changing the values, but there was no change in updating the validation to the values I added to the input fields. For example:
$( '#Username' ).val( _username ).trigger( 'input' ); // Also .trigger( 'change' )
You can test both of those script by adding them to Tampermonkey, and navigating to the ssl roblox homepage.
The key point is that that Angular code (and similar JS) use change
events to trigger their validators. So, just setting the value is not enough; you must also send a change event.
So:
.change()
, or .trigger()
, from a userscript that is not injected, seldom works. Send a proper change event; see below.@require
d jQuery (good), but used @grant none
(bad), your script was causing the page to crash and you would see various errors in the console.window.load
seems to be enough, but you may have to step up to something like waitForKeyElements()
if you should see more timing problems.$('#SignupButton').click ();
If so, that's outside the scope of this question.With that, the script becomes:
// ==UserScript==
// @name Roblox Account Create
// @match https://www.roblox.com/
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @grant GM_addStyle
// ==/UserScript==
var _adj = [ 'Cool', 'Masked', 'Bloody', 'Lame' ];
var _animals = [ 'Hamster', 'Moose', 'Lama', 'Duck' ];
var _months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
var _username = _adj[ parseInt( Math.random( ) * _adj.length ) ], _pass = Math.random( ).toString( 36 ).substring( 2, 10 );
_username += _animals[ parseInt( Math.random( ) * _animals.length ) ];
_username += parseInt( Math.random( ) * 1000 );
var _dt_month = _months[ Math.floor( Math.random( ) * ( 12 ) + 0 ) ];
var _dt_day = Math.floor( Math.random( ) * ( 28 ) + 1 );
var _dt_year = Math.floor( Math.random( ) * ( 2005 - 1916 + 1 ) + 1916 );
window.addEventListener ("load", function load () {
setControl ('Username', _username );
setControl ('Password', _pass );
setControl ('PasswordConfirm', _pass );
setControl ('MonthDropdown', _dt_month );
setControl ('DayDropdown', _dt_day );
setControl ('YearDropdown', _dt_year );
$( '#FemaleButton' ).click( );
$( '#SignupButton' ).click( );
} );
function setControl (elemID, value) {
var zInput = $( '#' + elemID );
zInput.val( value );
var changeEvent = document.createEvent ("HTMLEvents");
changeEvent.initEvent ("change", true, true);
zInput[0].dispatchEvent (changeEvent);
}
Note that we use a valid @grant
value, other than none
. That's crucial to avoid conflicts with the page's javascript.
it turns out there are several different events that trigger validation on various websites (input, keyup, change). here is the function i use that seems to work on all websites (jquery, angularjs, or plain old javascript):
function fireChangeEvents(element){
var changeEvent = null;
changeEvent = document.createEvent ("HTMLEvents");
changeEvent.initEvent ("input", true, true);
element.dispatchEvent (changeEvent);
console.debug('input event dispatched for element: '+element.id);
changeEvent = document.createEvent ("HTMLEvents");
changeEvent.initEvent ("keyup", true, true);
element.dispatchEvent (changeEvent);
console.debug('keyup event dispatched for element: '+element.id);
changeEvent = document.createEvent ("HTMLEvents");
changeEvent.initEvent ("change", true, true);
element.dispatchEvent (changeEvent);
console.debug('change event dispatched for element: '+element.id);
}
in this specific case, i think this jquery should work:
fireChangeEvents(document.getElementById('Username'));
you might want to be careful to only fire the "change" event if the value was actually changed. i have seen some sloppy code on checkboxes toggling "valid" on every "change" event rather than reading the actual checkbox value.
note: tested on chrome 46.0.2490.71 (portable_apps edition)
I was not able to get it work using the code here to set a contact field in the nextcloud contacts app (that is written in angular). This is what worked for me:
function setAngularInputValue (targetInput, newValue) {
targetInput.value = newValue;
const event = new Event('input', { bubbles: true });
targetInput.dispatchEvent(event);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With