2
votes

I use mvc4 and entityframework in combination with knockout. I also use knockout mapping. But I run into a problem wich I cannot get solved.

What I want to achieve:

  • User clicks on delete button to delete row from table. [Works]
  • Call the delete action method on the mvc controller to actualy delete the data from the database. [Works]
  • Send back new Json object containing the data of the new changed table. [Works]
  • Load the new data in my knockout viewmodel and update the UI using ko.mapping.fromJS() and ko.applyBindings() [Does not work and I am realy lost here]

It looks like I totaly lost the reference to my list of Experiences wich I use to databind and foreach my table with. And I think it has to do with the way I call my deletemethod from within the foreach loop.

It also looks like the javascript 'trick' with var self = this; doesnt work to get my reference to the correct data.

Here follows my code I use for now to give you all a better idea of the situation. If more information is needed please let me know.

These are my mvc viewmodels:

public class ExperienceOverviewModel
{
        public ExperienceModel selectedExperience { get; set; }
        public List<ExperienceModel> Experiences { get; set; }
}

public class ExperienceModel
{
        public int ExperienceId { get; set; }
        public DateTime DateFrom { get; set; }
        public DateTime DateUntil { get; set; }
        public string Description { get; set; }
        public string Employer { get; set; }
        public decimal Hours { get; set; }
        public int PersonId { get; set; }
        public bool? Secondment { get; set; }
        public string Title { get; set; }
}

This is my table where I foreach databind the experiences using knockout:

</table>
    <thead>
        </thead> 
    <tbody data-bind="foreach: Experiences()">
            <tr>
                <td data-bind="text: Employer"></td>
                <td data-bind="text: Description"></td>
                <td data-bind="text: DateFrom"></td>
                <td data-bind="text: DateUntil"></td>
                <td data-bind="text: Secondment"></td>
                <td>
                    <a href="#" data-bind="click: function(data, event){ $root.EditExperienceModal(data); }"><i class="icon-edit"></i></a>
                </td>
                <td>
                    <a href="#" data-bind="click: function(data, event){ $root.ConfirmDeleteExperienceModal(data);}"><i class="icon-remove"></i></a>
                </td>
            </tr>
        </tbody>
    </table>

My knockout viewmodel where I want to call ko.mapping.fromJS() and ko.applyBindings()... Here it goes fubar....

    function ViewModel() {
            this.DeleteExperience = function (experience) {

                $.ajax({
                    type: "post",
                    contentType: "application/json",
                    url: "/Experienced/Delete/" + this.selectedExperience.ExperienceId(),
                    data: ko.toJSON(self.selectedExperience),
                    error: function (xhr, status, error) {
                    },
                    success: function (response) {

                        ko.mapping.fromJS(response, ?? this.Experiences ??);  <---- ???
                        ko.applyBindings(?????);                              <---- ???

                    }
                });
            }
        }

        $(function () {
            var jsonModel = '@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model, new Newtonsoft.Json.Converters.IsoDateTimeConverter()))';
            var mvcModel = ko.mapping.fromJSON(jsonModel);

            var myViewModel = new ViewModel();
            g = ko.mapping.fromJS(myViewModel, mvcModel);
            ko.applyBindings(g);
        });

* -- UPDATED -- *

To clarify more here is what I have simplyfied: When I get the results back from the AJAX call I get the following error:

Object # has no method 'Experiences'

<table class="table table-striped">
    <tbody data-bind="foreach: Experiences()">
        <tr>
            <td data-bind="text: Employer"></td>
            <td data-bind="text: Description"></td>
            <td data-bind="text: DateFrom"></td>
            <td data-bind="text: DateUntill"></td>
            <td data-bind="text: Secondment"></td>
            <td>
               <a href="#" data-bind="click: $root.DeleteExperience"><i class="icon-remove"></i></a>

            </td>
        </tr>
    </tbody>
</table>
<script type="text/javascript">

    function ViewModel() {
        var self = this;

        self.DeleteExperience = function (experience) {
            $.ajax({
                type: "post",
                contentType: "application/json",
                url: "/Experienced/Delete/" + experience.ExperienceId(),
                data: ko.toJSON(experience),
                error: function (xhr, status, error) {
                    console.log(error);
                },
                success: function (response) {
                    $('#confirmDeleteModal').modal('hide');
                    self.UpdateExperienceList(response);
                }
            });
        }

        self.UpdateExperienceList = function (data) {
            self.Experiences(data);       <---- ?????
        }
    }

    $(function () {
        var jsonModel = '@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model, new Newtonsoft.Json.Converters.IsoDateTimeConverter()))';
        var mvcModel = ko.mapping.fromJSON(jsonModel);
        var myViewModel = new ViewModel();
        g = ko.mapping.fromJS(myViewModel, mvcModel);
        ko.applyBindings(g);
    });
</script>
2
Can you post the code that defines this.Experiences? You almost certainly won't be able to use this. here though - the this object inside your success callback is not the same as it is within your ViewModel function...Steve Greatrex
Thanks, but I have still the same problem, I updated my question to make the problem clearer.user1853542
Where are you instantiating self.Experiences?Steve Greatrex
self.Experiences comes from my mvc4 viewmodel. it gets mapped to knockout. at this line var jsonModel = '@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model, new Newtonsoft.Json.Converters.IsoDateTimeConverter()))';user1853542

2 Answers

0
votes

In your self.UpdateExperienceList function, try the following:

self.UpdateExperienceList = function(data) {
    ko.mapping.fromJSON(data, {}, self);
};

This will update the properties on the self object based on the data from the server.

Your initial creation of the view model also looks wrong to me. I would have though you wanted something more like the below:

var myViewModel = ko.mapping.fromJSON(jsonModel, {}, new ViewModel());
0
votes

I don't think you need to call ko.applyBindings(?????); again. You should define explicitly your view model:

function MyModel() {
    var self = this;

    self.Data = ko.observableArray([{"Name": "Lionel Messi", "Occupation": "Football player"}, {"Name": "Jason Mraz", "Occupation": "Singer"}, {"Name": "Nicolas Cage", "Occupation": "Film Actor"}]);

    self.update = function(data) {
        self.Data(data);
    }
}

$(function(){
    var model = new MyModel();
    ko.applyBindings(model);
});

As you see I define self.update, you just invoke this function and update your data and in case you want to remove a row from table, you just splice that row out of self.Data()