Home » excel » javascript – Excel VBA controls Webpage with Knockout; appears to work; but nothing is saved

javascript – Excel VBA controls Webpage with Knockout; appears to work; but nothing is saved

Posted by: admin March 7, 2020 Leave a comment

Questions:

Summary:

Basically trying to edit a website with Excel VBA. The edits appear to work, but when I use the save button, nothing is saved. I know the save button works, explained below. So why isn’t my updated data, which is visible on the screen being saved?

The story:

I have this code I have been working on for awhile with Excel VBA. It opens a webpage in internet explorer, navigates where I want, fills out a bunch of data, all which show up on the screen, using various methods, such as:

For Each objElement In objElementColl
ExtractedName = objElement.outerHTML

If InStr(ExtractedName, "NewPermit") > 0 Then
objElement.Checked = True

and

Set DropDown = objHTML.getElementById("ProjectFile-AccreditedCertifierId")
DropDown.selectedIndex = 1

or

objHTML.getElementsByName(ElementName)(0).Value = ValueCheck

All of which work very well, and changes on the screen. I then click save by using:

Set objElementColl = objHTML.getElementsByClassName("btn")

For Each objElement In objElementColl
    ExtractedName = objElement.outerHTML

    If InStr(ExtractedName, "click: save, enable:") > 0 Then
        objElement.Click
        ExtractedName = 1
        Exit For
    End If
Next

Which again works fine. The issue is that it doesn’t actually save my changes from the code from the 3pieces above. What i have tried is

a) Pause my code and manually click save (same issue)

b) Pause my code, manually change a checkbox and run the code to save (does save the manual change, but not the coded ones

c) Pause the code and manually change a box and manually save (only manually changed box is saved)

So, from above, it appear my save click works, but for some reason, although the boxes are visibly changed and filled out using the code, there is a gap between the visible and the background.

Anyway, some HTML source code. Is what chrome shows me when Inspecting an element I am changing:

    <fieldset>
        <legend>Proposal</legend>

        <div class="col-xs-12 col-sm-8 col-md-6">
            <div class="row">
                <div class="col-xs-2 form-group">
                    <label for="ProjectFile_ProposalLot">Lot</label><input class="form-control" data-bind="textInput: ProjectFile().ProposalLot" maxlength="100" name="ProjectFile-ProposalLot" type="text" />
                </div>
                    <div class="col-xs-2 form-group" data-bind="visible: ProjectFile().StateId() != 7 && ProjectFile().StateId() != 5">
                        <label data-bind="text: ProjectFile().ProposalDpLabel()"></label>
                        <input class="form-control" data-bind="textInput: ProjectFile().ProposalDp" maxlength="100" name="ProjectFile-ProposalDp" type="text" />
                        </div>

I have also searched the entire source code for the page… I believe this might be important, but I am not a HTML coder. I have shortened it a bit

var ProjectFileEditViewModel=(function(){__extends(ProjectFileEditViewModel,ViewModel.Model);function ProjectFileEditViewModel(){ProjectFileEditViewModel.__super__.constructor.apply(this,arguments);};ProjectFileEditViewModel.prototype.fields=function(){return {"Id":new ViewModel.NumberField(0),"StateId":new ViewModel.NumberField(0),"DefaultOfficeAddressId":new ViewModel.ObservableField(),"Name":new ViewModel.ObservableField(),"ExistingApprovalDate":new ViewModel.DateField("DD/MM/YYYY"),"ProjectClosed":new ViewModel.ObservableField(),"ProposalAddress":new ViewModel.ObservableChildField(exports.AddressViewModel,this),"Zoning":new ViewModel.ObservableField(),"ProposalLot":new return ProjectFileEditViewModel;})();if(exports.ProjectFileEditViewModel==null)exports.ProjectFileEditViewModel=ProjectFileEditViewModel;

There is also this… again much longer:

Buildaform.model=new Buildaform.ProjectPageViewModel({ ... ,"ProposalLot":null .... }

I think this last one has something to do with it, and I do not know if I can change it.

For the bad news, I cannot release the website address or source code publicly. But please PM me, and I can work something out.

How to&Answers:

Per your comment that:

b) Pause my code, manually change a checkbox and run the code to save
(does save the manual change, but not the coded ones

It seems that the problem is with the code setting form controls and not with the code clicking the save button.

This seems to be a problem not related to VBA but with the behaviour of knockout – see this SO post. The pertinent comment is:

Your problem is that ko subscribes on the click event inside the checked binding:

The questioner in that post is having a similar problem to you – they are trying to check a checkbox (to change the view) but it is not updating either the viewmodel, or the underlying model itself. Knockout is a MVVM framework.

The give-away in your question is that your manual changes commit because you perform a click-and-change when performing the action via point-and-click in the browser, but your programmatic method only does the change to the form control, but not the click first.

So, how to solve this via VBA automation through IE?

Based on the solution in the post I referenced above, plus the method here I will hazard the code below as a possible solution, but please note it is untested …

Basically you need to ‘click’ on the form element you want to change – and then update the control value. Hopefully the ‘clicking’ bit will mean that the knockout viewmodel updates per the ‘change’, and from there, the model data will be written to the database (or whatever):

Your checkbox example:

If InStr(ExtractedName, "NewPermit") > 0 Then
// hopefully this will get knockout to apply the required binding before your change the value
objElement.Click
objElement.Checked = True

Your dropdown example:

Set DropDown = objHTML.getElementById("ProjectFile-AccreditedCertifierId")
// hopefully this will get knockout to apply the required binding before your change the value
Dropdown.Click
DropDown.selectedIndex = 1

Hope that helps – quite the 3-pipe problem! Good luck.

Answer:

As the regarding web site can not be shared, I can come up with a just set of hints to try out:

If the web site would implement a simple (pure) HTML form to send the POST request, your solution would be fine. But looking at the HTML you shared

<label data-bind="text: ProjectFile().ProposalDpLabel()"></label>

the data-bind is already suggesting that the data is getting collected/sent by a library. (E.g. Knockout is using that attribute). This library might now collect the data somewhere, and it might get triggered by a “click” or a “key” event in JavaScript. The collected information can then be stored in a hidden DOM element as suggested by GCSDC or directly in a JavaScript variable.

What I would suggest now is to find out which JavaScript framework is used on this page by inspecting the HTML source. At some point there should be a

<script src="<fancy js framework>.js"></script>

tag in the HTML, which should give you the name of the framework. (There can actually be multiple tags of this kind, including custom JavaScript files. These tags do not have to be at the beginning of the HTML document, and can be scattered all over it, so you might have to search for script in the HTML document. One of them should be the main framework, which is sending the request. If you are not sure which one it would be, you have to google all of them and find out.)

Then, research how the the POST (maybe Ajax) request is sent in the JavaScript code on this page, with help from the documentation of the Framework. And then, send the request by executing custom JavaScript from VBA on this page; how this could be done is shown in this post.

Alternatively, you could try to trigger a click (or key) event on the form inputs to make the framework believe you actually typed it in; how this could be done is shown in this post, but this might not work in all cases.