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