0

Hi I have looked around online for hours trying to solve this but I couldn't find a solution.

I am building the solution using ASP.NET MVC and Knockout. The controller returns the options for the user to select from to the view which converts it into JSON and uses the mapping plugin to map it to a knockout View Model. It populates correctly because when I do this: ko.toJSON($data) to test it, the correct data is returned but the view won't update the list when an item is added to it.

My guess is that the properties aren't observables but I'm not sure how to fix that.

This is an example of the JSON Returned:

[{"Key":1,"Value":"company"}]

This is my javascript:

function ProjectWorkedOn() {
    var self = this;
    self.client = ko.observable();
    self.job = ko.observable();
    self.project = ko.observable();
    self.workType = ko.observable();
}

function createTimesheetViewModel() {
    var self = this;

    //list of options
    self.UserClients = ko.observableArray();
    self.UserProjects = ko.observableArray();
    self.UserJobs = ko.observableArray();
    self.UserWorkTypes = ko.observableArray();

    //keep track of selected options
    self.selectedClient = ko.observable();
    self.selectedProject = ko.observable();
    self.selectedJob = ko.observable();
    self.selectedWorkType = ko.observable();

    //list to add choices in
    self.ListProjectsWorkedOn = ko.observableArray();


    self.addProjectWorkedOn = function () {
        var project = new ProjectWorkedOn();
        project.client = self.selectedClient;
        project.job = self.selectedJob;
        project.project = self.selectedProject;
        project.workType = self.selectedWorkType;
        self.ListProjectsWorkedOn.push(project)
    }
    self.removeProjectWorkedOn = function (projectWorkedOn) {
        self.ListProjectsWorkedOn.remove(projectWorkedOn)
    }

}



$(function () {
    var CreateTimesheetViewModel = new createTimesheetViewModel();

    CreateTimesheetViewModel.UserClients = ko.mapping.fromJSON('@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.UserClients))');
    CreateTimesheetViewModel.UserProjects = ko.mapping.fromJSON('@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.UserProjects))');
    CreateTimesheetViewModel.UserJobs = ko.mapping.fromJSON('@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.UserJobs))');
    CreateTimesheetViewModel.UserWorkTypes = ko.mapping.fromJSON('@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.UserWorkTypes))');

    ko.applyBindings(CreateTimesheetViewModel, document.getElementById("CreateTimesheet"));
});

This is the part of the view that adds items to the list:

<table>
    <tr>
        <td>
            Projects Worked On:
        </td>
        <td>
            Client: <select data-bind="options: UserClients, optionsText: 'Value', value: selectedClient"></select>
        </td>
        <td>
            Project: <select data-bind="options: UserProjects, optionsText: 'Value', value: selectedProject"></select>
        </td>
        <td>
            Job: <select data-bind="options: UserJobs, optionsText: 'Value', value: selectedJob"></select>
        </td>
        <td>
            Service: <select data-bind="options: UserWorkTypes, optionsText: 'Value', value: selectedWorkType"></select>
        </td>
        <td>
            <button data-bind="click: addProjectWorkedOn">Add Project</button>
        </td>
    </tr>
</table>

and this is what displays them:

<table>
    <tr>
        <td>Client</td>
        <td>Project</td>
        <td>Job</td>
        <td>Service</td>
        <td></td>
    </tr>
    <tbody data-bind="foreach: ListProjectsWorkedOn()">
        <tr>
            <td data-bind="text: client.Value"></td>
            <td data-bind="text: project.Value"></td>
            <td data-bind="text: job.Value"></td>
            <td data-bind="text: workType.Value"></td>
            <td><button data-bind="click: $parent.removeProjectWorkedOn">Remove</button></td>
        </tr>
    </tbody>
</table>

Thanks in advance!

EDIT:

I worked it out

to add project set the attributes like this: project.clientValue(self.selectedClient().Value);

then to reference them in view call it as a function: clientValue()

credit goes to Chris Patt for explaining that observables should be called as functions when you want to get their value

1 Answer 1

1

Because you're setting the ProjectWorkedOn observables and getting the selected values incorrectly. To set an observable you pass the value as a parameter as you would for a function and to get the value of an observable, you call it like a function. (They are in fact functions.):

self.addProjectWorkedOn = function () {
    var project = new ProjectWorkedOn();
    project.client(self.selectedClient());
    project.job(self.selectedJob());
    project.project(self.selectedProject());
    project.workType(self.selectedWorkType());
    self.ListProjectsWorkedOn.push(project)
}

EDIT

I was thinking you literally wanted the selected values, not the actual whole JSON object. What you're attempting simply isn't possible. The value of the select has to be an integer or string -- it can't be a full object. Usually you would handle something like this by first simply returning a value/text list to populate the selects, where the value would be something like the id. Then, upon selection, you would issue an AJAX request to retrieve the object with that id.

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

4 Comments

thanks, I tried that but it still isn't displaying the values. The list is showing and it is increasing in size but each of the values it is binded to are empty.
strange thing is that I can add and remove but it just won't display the data
I just changed it to a key value pair but either way it seems like it should work because the list was being populated as I was adding new items. Only problem was it just wouldn't update the part of the view that was binded to it.
You were adding new ProjectWorkOns to the observableArray sure, but no meaningful data was going into the object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.