13

I'm trying to run javascript in a browser extension to automate a process of filling in a form on a website and then clicking a button -- the extension code can be simulated by typing javascript:code into the address bar.

The website that I'm having problems with uses angularJS. I have the names of the input field ids and am using those to change the input field values. The fields fill up but when I click the button it says they are not filled, there are no values and they are all in error. Some validation is going on that does not "see" my changes unless I type in the values manually.

Is there a simple way to change the value of the AngularJS input fields that have validation using just the id of the input field.

Here is the code:

<input id="shippingAddr-first-name" type="text" class="first-name ng-pristine ng-valid" maxlength="16" data-ng-model="addressTypes[addressType].FirstName" focus-me="commonAddressTypeProps[addressType].focusOnFirstName" client-validation="onExit" data-required-on-exit="">

My attempts using document.getElementById("shippingAddr-first-name").value="Dave"; change the field but do not register correctly during the form submission. It does work however if I type it in manually. I've also tried click(), blur(), and focus(), to simulate some things I might be doing manually but those do not work either.

divibisan
  • 11,659
  • 11
  • 40
  • 58
techdog
  • 1,451
  • 3
  • 19
  • 38

4 Answers4

7

Trigger input event on element with ng-model attribute that is observable by AngularJS. Event input is the way from where Angular knows that some changes occurs and it must run $digest loop

Some source code:

// if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
  // input event on backspace, delete or cut
  if ($sniffer.hasEvent('input')) {
    element.on('input', listener);
  } else {
    var timeout;

    var deferListener = function(ev, input, origValue) {
      if (!timeout) {
        timeout = $browser.defer(function() {
          timeout = null;
          if (!input || input.value !== origValue) {
            listener(ev);
          }
        });
      }
    };

    element.on('keydown', function(event) {
      var key = event.keyCode;

      // ignore
      //    command            modifiers                   arrows
      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;

      deferListener(event, this, this.value);
    });

    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
    if ($sniffer.hasEvent('paste')) {
      element.on('paste cut', deferListener);
    }
  }

Some working proof of concept:

angular.module('app', []).controller('app', function($scope) {
  $scope.user = {}
  $scope.handleSubmit = function(user) {
    alert('passed name ' + JSON.stringify(user))
  }
})

$(function() {
  $('#run').click(function() {
    fillElement($('[ng-model="user.name"]'), 'some user name')
    sendForm($('[ng-submit="handleSubmit(user)"]'));
  })

  function fillElement($el, text) {
    $el.val(text).trigger('input')
  }

  function sendForm($form) {
    $form.submit()
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div>
  <button id="run">Execute `input` event outside AngularJS</button>
</div>
<div ng-app="app">
  <div ng-controller="app">
    <form ng-submit="handleSubmit(user)">
      <input type="text" ng-model="user.name" />
      <input type="submit" id="submit" value="Submit" />
    </form>
  </div>
</div>
Krzysztof Safjanowski
  • 7,292
  • 3
  • 35
  • 47
  • 1
    Thank you. Although I don't understand jquery that well, or angularjs at all this gave me enough code to solve it. As long as I can get the angular element and then use trigger("input") it seemed to fix the problem. – techdog Jul 20 '15 at 04:38
3

You should use the data binding of AngularJS, you put an ng-model on the input so you need to change that value to get/set the input value, in you controller you can do:

JSFiddle

.controller('myCtrl', function ($scope) {
    $scope.addressTypes = [];
    $scope.addressTypes['onetype'] = { FirstName: 'Dave'};
});
michelem
  • 14,430
  • 5
  • 50
  • 66
  • Thank you for answering and for creating the example. I changed the description to be more clear about what I'm attempting. All I have to work with is the id of the input field and any parameters I can read using javascript and the id. – techdog Jul 19 '15 at 19:28
2

use angular.element

angular.element(document.querySelector("#shippingAddr-first-name")).val("Dave");
Vineet
  • 4,525
  • 3
  • 23
  • 42
  • I tried this from the address bar using javascript:thecode but angularjs did not seem to register the change when I click the button. – techdog Jul 19 '15 at 19:29
  • You should apply the changes. You should check this out. http://stackoverflow.com/questions/31301475/call-angularjs-from-other-scripts/31301533#31301533 – Vineet Jul 20 '15 at 09:22
  • 1
    I achieved success with this answer in conjunction with Krzysztof's answer with the following: angular.element(document.querySelector("#shippingAddr-first-name")).val("Dave").trigger("input"); – aRealPerson Mar 02 '22 at 21:39
0

It worked for me when I added .trigger('input')

const el = document.querySelector("#shippingAddr-first-name");
angular.element(el).val("Dave").trigger('input');
Wilhelm Mauch
  • 23
  • 1
  • 6