5

I'm trying to fill a Angular 4+ form using console (devtools).

This is what I'm doing now:

function fillForm(){
    let el = document.querySelector('input[ng-reflect-name="my_input"]');
    let elProbe = ng.probe(el);
    elProbe._debugContext.component.value = 'new-value';
}

Some references I'm trying to use (if it helps anyone):

2
  • Where do you call detectChanges? Angular is very lazy it wants a kick :) Commented Jul 7, 2017 at 14:24
  • The same question: How to do $('#someId').val(somevalue) using angular? Commented Apr 11, 2020 at 14:35

3 Answers 3

3

There are two options. The first one is to work with component property bound to a form and it requires manual change detection triggering. The second one is to work with the form control associated with the input and it doesn't require manual change detection.

Neither is better.

For the first option see @yurzui's answer. Here is the answer for the second option - update the form control directly without the need for change detection:

function fillForm(){
    let el = document.querySelector('input[ng-reflect-name="my_input"]');
    let elProbe = ng.probe(el);

    const NgControlClassReference = elProbe.providerTokens.find((p)=>{ 
        return p.name === 'NgControl';
    });

    let directive = elProbe.injector.get(NgControlClassReference);
    let control = directive.control;

    control.setValue('some');
}

In this case you don't need change detection because when you call setValue on the control directly it notifies valueAccessor about the change:

FormControl.prototype.setValue = function (value, options) {
  ...
  this._onChange.forEach(function (changeFn) { 
      return changeFn(_this._value, options.emitViewToModelChange !== false); });

where changeFn is a subscriber added in the setUpContorl function:

  control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor !.writeValue(newValue);

which calls writeValue on the accessor directly and it in turn writes the value into input:

export class DefaultValueAccessor implements ControlValueAccessor {
  ...

  writeValue(value: any): void {
    const normalizedValue = value == null ? '' : value;
    this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
  }

You also might find this article useful

Everything you need to know about debugging Angular applications

Sign up to request clarification or add additional context in comments.

9 Comments

Whats your context in $0 ?
@leonardomerlin, it's elProbe, I've updated the details
I choose the @yurzui as a correct answer because I dont discover what referencet to put in the place of $0 reference and nothing about the magic number "3" =/ Sorry.
@leonardomerlin, mine and yurzui's answers are different. I'm updating the form control directly and don't need to invoke change detection manually, while his solution updates the property on the parent component and requires manual trigger to change detection. The magical 3 is a reference to the NgControl class. You can choose whatever you like
plnkr.co/edit/rvXQ4xl6TEXchueasDS5?p=preview Are you sure that index will be 3?
|
2

Simple way of doing this could be running change detection cycle using core token for ApplicationRef

let el = document.querySelector('input[ng-reflect-name="my_input"]');
let elProbe = ng.probe(el);   
elProbe._debugContext.component.value = 'new-value';
elProbe.injector.get(ng.coreTokens.ApplicationRef).tick()

You can also take a look at integration tests

https://github.com/angular/angular/blob/master/packages/forms/test/template_integration_spec.ts#L28-L30

I think this way we check how our component works with NgModel and don't call direct API on inner NgModel directive.

Without calling change detection we can also the following code to simulate user action

let valueAccessor = elProbe.injector.get(elProbe.providerTokens
                       .find(x =>x.name === 'DefaultValueAccessor'));

el.value = 'some';
valueAccessor.onChange('some');

But it looks very strange

5 Comments

Works like a charm! Thank you!
you don't need change detection if you update the control directly
@Maximus Angular works in this way so i don't think that it is not correct
@yurzui, yeah, but for this particular case the change detection is indeed not required. See my edited answer
You can also take a look at integration tests - it's a good reference, but the test is written to test ngModel - 'should support ngModel for standalone fields' and formControl is a perfectly fine class to work with. Anyway, it'd be great to see how the form is represented in html
0

Copy/paste spin-off from the accepted answer to fill multiple fields at once:

[
  { selector: "#initials", val: "JJA" },
  { selector: "#lastName", val: "Steenbruggehoek" },
  { selector: "#email", val: "[email protected]" },
].forEach(item => {
    const el = document.querySelector(item.selector);
    if (!el) { console.warn(item.selector, 'not found'); return; }
    const probe = ng.probe(el);
    const ref = probe.providerTokens.find(p => p.name === 'NgControl');
    const directive = probe.injector.get(ref);
    directive.control.setValue(item.val);
});

As the other answer, only works for debug builds.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.