Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controls Webpage with Knockout: Visible changes not saved

Trying to edit a website with Excel VBA. The edits appear to work, but when I use the save button, nothing is saved. Why isn't updated data, which is visible on the screen, being saved?

This code opens a web page in internet explorer, navigates where I want, fills out data, all which show 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 work and changes appear on the screen. I 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 runs. The issue is it doesn't save the changes from the three pieces above.

What I have tried

  1. Pause my code and manually click save (same issue)

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

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

From above, it appears my save click works, but although the boxes are visibly changed and filled out using the code, there is a gap between the visible and the background.

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 searched the 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:

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

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

I cannot release the website address or source code publicly.

like image 465
Bryan Davies Avatar asked Jul 03 '16 09:07

Bryan Davies


People also ask

What happens in visible binding?

When the parameter transforms into true-like value (such as boolean true,or nonnull object or array ), the binding removes yourElement. style. display value, making it visible. If this is an observable property, then the binding will update visibility every time the property changes.

What is Ko observable?

An observable is useful in various scenarios where we are displaying or editing multiple values and require repeated sections of the UI to appear and disappear as items are inserted and deleted. The main advantage of KO is that it updates our UI automatically when the view model changes.

How to enable disable button in knockout js?

canSubmit is an observable, so change its value by calling the function: self. submit = function () { // disable submit button self. canSubmit(false); // do stuff self.

How does Ko identify the template block that needs to be rendered?

Shorthand syntax: If you just supply a string value, KO will interpret this as the ID of a template to render. The data it supplies to the template will be your current model object.


2 Answers

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.

like image 159
Michael Scheffenacker Avatar answered Sep 19 '22 13:09

Michael Scheffenacker


Per your comment that:

  1. 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.

like image 21
Robin Mackenzie Avatar answered Sep 20 '22 13:09

Robin Mackenzie