0

I need to activate a select value in a website using Knockout.js using a browser extension.

I am using jQuery but I am a bit lost with the trigger ko methods.

This is the select that I want to change the value:

<select name="subDuration" class="select number_weeks" id="sub_duration"
data-bind="
    options: options.durationOptions, 
    optionsText: 'val', 
    optionsValue: 'key', 
    optionsCaption: 'Select subscription duration', 
    value:options.duration,
    event:{
        change: options.updateDuration,
        blur: options.validateDuration
    },
    attr:{
        'aria-invalid': options.hasDurationError,
        'aria-describedby': options.hasDurationError() ? 'sub_duration_error' : false
    }
" 
data-ctp-cont="Direct Debit Payments">
</select>

$("#sub_duration").focus().trigger("change", options).val(2).blur()

When I use the cde line above it puts the value as 2 but the site validation runs on blur and does not recognise it as valid. If I select something else or then 2 again it does. It feels like the jQuery change is not triggered in the site ko js validations.

Any idea of what I am missing here? Sadly I've never worked with knockout.js...

Any help is appreciated. Thank you!

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • 1
    I'm assuming you're changing a knockout observable value with jQuery, is there a reason why you're not changing the value via knockout itself? – Sam Sep 20 '19 at 09:37
  • Hi, I am doing it via the browser extension with jquery. And the knockout.js in the site is minified. I never worked with KOJS so I'm not 100% sure how to actually do it. I imagine I have to apply bindings somehow but I am a bit lost. Let's say I wanted to do it via ko, how would I go around finding the view model and apply the value change via ko? – user4561667 Sep 20 '19 at 09:57

2 Answers2

0

It's probably easier to directly update the values in knockout's underlying viewmodel. You can also trigger the methods bound to the event handlers from there.

// The website part
ko.applyBindings({
  options: {
    duration: ko.observable(1),
    durationOptions: [
      { key: 0, val: "Option 0" },
      { key: 1, val: "Option 1" },
      { key: 2, val: "Option 2" }
    ],
    updateDuration: () => console.log("Updated duration"),
    validateDuration: () => console.log("Validated duration"),
    hasDurationError: ko.observable(false)
  }
});

// Your plugin
$("button").click(function(e) {
  const select = document.getElementById("sub_duration");
  const vm = ko.dataFor(select);
  
  // Change the value:
  vm.options.duration(2);  

  // Trigger the event handlers
  vm.options.updateDuration();
  vm.options.validateDuration();
  
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>



<select id="sub_duration" data-bind="
  options: options.durationOptions, 
  optionsText: 'val', 
  optionsValue: 'key', 
  optionsCaption: 'Select subscription duration', 
  value:options.duration,
  event:{
    change: options.updateDuration,
    blur: options.validateDuration
  },
  attr:{
    'aria-invalid': options.hasDurationError,
    'aria-describedby': options.hasDurationError() ? 'sub_duration_error' : false
  }
"></select>

<div>
Trigger your jQuery code: <button>Update value</button>
</div>

Note that updateDuration and validateDuration might require to be passed some event like object and this context to work correctly, depending on their implementations.

user3297291
  • 22,592
  • 4
  • 29
  • 45
  • Thanks, I will try that. I noticed that in console I can run window.ctp.dbg.viewModel.OptionsController.duration(1) to set duration or window.ctp.dbg.viewModel.OptionsController.duration() to view it. But when running that from my extension it fails... – user4561667 Sep 20 '19 at 11:55
  • I am getting "ko is not defined" in my plug in script... Weird... Isn't it? – user4561667 Sep 20 '19 at 12:00
  • Probably related to not having access to the global namespace from your extension. Have a look at this question + answer: https://stackoverflow.com/a/9636008/3297291 – user3297291 Sep 20 '19 at 12:02
  • Good shout... I will investigate further. Many thanks! – user4561667 Sep 20 '19 at 13:09
  • I think it's because jquery dom ready loads but ko is not ready yet. But then it is when I find it in the console. Anyone knows how to wait to ko to load? I say this because I can manipulate the window title of the background page but ko is not available to me at that point... – user4561667 Sep 20 '19 at 14:49
0

Thanks to @user3297291 I followed the solution of injecting js in the main site to be able to run the code form the content script of my extension.

Below is the solution code in my extension content script, in case anyone is facing the same issue:

function myFunc() {
  //Trigger some Knockout JS functions on the main site scope.
}

var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ myFunc +')();'));
(document.body || document.head || document.documentElement).appendChild(script);

Thanks to anyone that helped!