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.