5

I'm having a hidden field in a knockout template that its value gets updated with jquery. The problem is when trying to pass this value to the server with ajax, I get null value in the controller. But the html source code shows that the value of the hidden field is updated. If I replaced the hidden field with a textbox, it would work fine only when I enter text manually.

jQuery

        function getFileDetail(fileID, fileName) {
        $('#hdnFileName' + fileID).val(fileName);
        $('#lblFileName' + fileID).text(fileName);
    }

Here is the html knockout template:

    <script type="text/html" id="fileTemplate">
        <div data-role="fieldcontain">
            <a href="#" data-bind="click: function () { openFileUpload('file', ID) }"><label data-bind="text: 'File Upload ' + ID, attr: { id: 'lblFileName' + ID }"></label></a><input type="button" value="Remove" data-bind="click: removeFile" /> 
        </div>
        <input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID, value: fileName }" />
    </script>

ViewModel

function FileViewModel() {
        var self = this;
        self.ID = ko.observable();
        self.fileName = ko.observable();
        self.removeFile = function (file) { };
        self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);

        self.addNewFile = function () {
            var newFile = new FileViewModel();
            newFile.ID = self.Files().length + 1;
            newFile.fileName = "";
            newFile.removeFile = function (file) { self.Files.remove(file); };
            self.Files.push(newFile);
            //$("input[name='hdnFileName'").trigger("change");
        }
    }
function ViewModel() {
        var self = this;
        self.fileViewModel = new FileViewModel();
        self.submitForm = function () {

            $.ajax({
                type: "POST",
                url: "<%= Url.Action("MeetingPresenter")%>",
                data: "{Files:" + ko.utils.stringifyJson(self.fileViewModel.Files) + "}",
                contentType: "application/json",
                success: function (data) {},
            });
        };
    }
Wedad Shurrab
  • 95
  • 1
  • 1
  • 7

4 Answers4

3

Your model property ID is an observable, so you need to 'unwrap' to get the value from it when you are concatenating, like this:

<input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID(), value: fileName }" />

and this:

<label data-bind="text: 'File Upload ' + ID(), attr: { id: 'lblFileName' + ID() }"></label>
David Tansey
  • 5,813
  • 4
  • 35
  • 51
0

It looks to me that setting a field's value via the DOM does not interact with knockout. If you are setting its value using .value, the observable will not be updated. You should be updating the observable.

I wrote a little Fiddle to demonstrate. Every 2 seconds, it sets the input's value via the DOM, but the bound observable only changes when you type something.

http://jsfiddle.net/qcv01h2e/

var viewModel = (function () {
    return {
        fv: ko.observable().extend({notify:'always'})
    };
}());

ko.applyBindings(viewModel);
setInterval(function () {
    console.debug("Set it");
    var f = document.getElementById('field');
    f.value = "Hi";
    console.debug("fv is", viewModel.fv());
}, 2000);
Roy J
  • 42,522
  • 10
  • 78
  • 102
0

If you are using knockout.js you don't neede to modify the DOM, you can just update the ViewModel and the DOM will be updated according

function getFileDetail(fileID, fileName) {
    viewModel.fileViewModel.update(fileID, fileName);
}

Add the update function in FileViewModel

function FileViewModel() {
    // rest of the code

    self.update = function(fileID, fileName) {
        var file = ko.utils.arrayFirst(self.Files(), function(file) {
            return file.ID == fileID;
        });

        file.fileName(fileName); // this will change and the UI will be updated according
    };
}

Note: Please notice that you have a default item in Files that will not be changed with update function because properties are not observable

self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);

You can solve this by making them observable (i.e. ID: observable(1)) or you can create a new FileViewModel().

Note: The viewModel must be accesible in the function (i.e. global instance), otherwise will be undefined.

adricadar
  • 9,971
  • 5
  • 33
  • 46
  • I have added the update function to the FileViewModel, but for some reason file is having the value of null. Am I missing something? – Wedad Shurrab Jun 01 '15 at 18:09
  • @WS84 The idea there is to find the element that match the condition. You can find other ways to find the element that match that condition. Also you can put a brakepoin to make sure that `fileID` contains what you want and the array have that element. – adricadar Jun 01 '15 at 19:19
  • It is working perfect now! I just had to create a global instance of the ViewModel(). Thanks! – Wedad Shurrab Jun 02 '15 at 00:07
0

I came across a similar issue where I need to set a value without user input. Before doing the click update function I do the required model update. If you have mode operations better to introduce a function in the model.

<input data-bind="click: function(){ isEnabled(true); update() }" />

What I actually did was,

<input data-bind="click: function(){ isEnabled(!isEnabled()); update() }" />

Keep in mind that asynchronous nature of javascript.

Dhanuka777
  • 8,331
  • 7
  • 70
  • 126