amdram
amdram

Reputation: 25

Refresh <table> after update using knockout asp .net mvc

I am trying to refresh a containing rows of roles after an update from the view. UI uses Knockout and backend is ASP NET MVC. The problem I face is the is not refreshed with the updated information when I call GetRoles(). However, it works when I call self.Roles(data.RolesList) instead. Below is my implementation.

View: Contains a TABLE. When TR is clicked, the inputs are populated with values of selected row.

@model ExpensesOrganiser4.ViewModels.RoleIndexViewModel

@section scripts{
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/jqueryui")
@Styles.Render("~/Content/themes/base/css")
<script src="~/Scripts/knockout-2.1.0.js" type="text/javascript"></script>
<script src="~/Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
<script src="~/Scripts/Application/role.js" type="text/javascript"></script>
<script type="text/javascript">
    var kovm = new RoleVM(@Html.Raw(Json.Encode(Model)));
    ko.applyBindings(kovm);
</script>
@*<script type="text/javascript">
    RoleVM = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
</script>*@
}

<h2>Index</h2>

<p>
    <a data-bind="click: DisplayNew">Create New</a>
</p>

<!-- //TABLE containing rows of roles -->
<table border="1">
    <thead>
    <tr>
        <th>@Html.DisplayNameFor(model => model.Rvm.RoleID)</th>
        <th>@Html.DisplayNameFor(model => model.Rvm.RoleName)</th>
        <th>@Html.DisplayNameFor(model => model.Rvm.Description)</th>
        <th>@Html.DisplayNameFor(model => model.Rvm.ApplicationName)</th>
        <th>@Html.DisplayNameFor(model => model.Rvm.ApplicationDescription)</th>
        <th data-bind="visible: false"></th>
    </tr>
    </thead>

    <tbody data-bind="foreach: Roles">
    <tr data-bind="click: $root.GetSelectedRole" id="updtr">
        <td><span data-bind="text: RoleID"></span></td>
        <td><span data-bind="text: RoleName"></span></td>
        <td><span data-bind="text: Description"></span></td>
        <td><span data-bind="text: ApplicationName"></span></td>
        <td><span data-bind="text: ApplicationDescription"></span></td>
        <td data-bind="visible: false"><span data-bind="text: ApplicationID"></span></td>
    </tr>
    </tbody>
</table>
<!-- //END TABLE containing rows of roles -->

<!-- INPUTS populated when <TR> is clicked --->
<table data-bind="visible: ReadOnlyMode">
    <tr>
        <td><label for="RoleID">Role ID:</label></td>
        <td><input data-bind="value: RoleID" type="text" id="RoleID" /></td>
    </tr>
    <tr>
         <td><label for="RoleName">Role Name:</label></td>
         <td><input data-bind="value: RoleName" type="text" id="RoleName" /></td>
    </tr>
    <tr>
        <td><label for="Description">Role Description:</label></td>
        <td><input data-bind="value: Description" type="text" id="Description" /></td>
    </tr>
    <tr>
    <td><label for="ApplicationName">Application:</label></td>
    <!--<td>@Html.DropDownList("cboApplicationName", Model.ApplicationsSelectList, new Dictionary<string, object> {{ "data-bind", "value: ApplicationID"}})</td>-->
    <td><select data-bind="options: ApplicationsList, optionsText: 'AppName', optionsValue: 'AppID', value: ApplicationID" id="cboApplications"></select></td>
</tr>
<tr>
    <td><label for="ApplicationDescription">Application Description:</label></td>
    <td><input data-bind="value: SelectedApplication" type="text" id="ApplicationDescription" /></td>
</tr>
</table>

<button data-bind="visible: DisplayUpdateRoleButton, click: UpdateRole" id="btnUpdate">Update</button>
<button data-bind="visible: DisplayDeleteRoleButton" id="btnDelete">Delete</button>


<div id="dialog-confirm" title="Success">Success.</div>  

Definition for Knockout: Refresh method function GetRoles(). thrownerror is SyntaxError: Unexpected token <

var self; //declare self outside the view model logic. 

var RoleVM = function (data) {

self = this;

ko.mapping.fromJS(data, {}, self);

self.EditFields = ko.observable(false);
self.ReadOnlyMode = ko.observable(true);
self.DisplayEditRoleButton = ko.observable(true);
self.DisplayUpdateRoleButton = ko.observable(false);
self.DisplayDeleteRoleButton = ko.observable(false);
self.Roles = ko.observableArray(data.RolesList);
self.ApplicationsList = ko.observableArray(data.ApplicationsList);

self.RoleID = ko.observable();
self.RoleName = ko.observable();
self.Description = ko.observable();
self.ApplicationName = ko.observable();
self.ApplicationDescription = ko.observable();
self.ApplicationID = ko.observable();
self.SelectedApplication = ko.observable("");
self.ApplicationID.subscribe(function (value) {
    self.SelectedApplication(self.ApplicationsList()[value - 1].AppDesc);
});


self.GetSelectedRole = function (role) {
    self.RoleID(role.RoleID);
    self.RoleName(role.RoleName);
    self.Description(role.Description);
    self.ApplicationName(role.ApplicationName);
    self.ApplicationDescription(role.ApplicationDescription);
    self.ApplicationID(role.ApplicationID);
    self.DisplayUpdateRoleButton(true);
    self.DisplayDeleteRoleButton(true);
};

self.UpdateRole = function () {
    UpdateSelectedRole();
};

self.DisplayNew = function () {
    self.RoleID("");
    self.RoleName("");
    self.Description("");
    self.DisplayDeleteRoleButton(false);
};
};

//HERE. This is not working
function GetRoles() {
debugger;
$.ajax({
    cache: false,
    type: "GET",
    url: "/Roles",
    dataType: "JSON",
    success: function (vm) {
        debugger;
        console.log(vm);
        self.Roles(vm.RolesList);   //put response in self.Roles Observable array
    },
    error: function (xhr, ajaxOptions, thrownError) {
        alert("Failed to retrieve roles. " + "xhr.status: " + xhr.status + "thrownError: " + thrownError);
        //console.log(JSON.stringify(xhr));
        console.log(xhr.responseText);
        //console.log("AJAX error: " + ajaxOptions + ' : ' + thrownError);
    }
});
}

function UpdatedRoleInfo() {
this.RoleID;
this.RoleName;
this.RoleDescription;
this.ApplicationID;
}

function UpdateSelectedRole() {
var updatedRoleInfo = new UpdatedRoleInfo();

updatedRoleInfo.RoleID = self.RoleID();
updatedRoleInfo.RoleName = self.RoleName();
updatedRoleInfo.Description = self.Description();
updatedRoleInfo.ApplicationID = self.ApplicationID();
//updatedRoleInfo.ApplicationID = $("#cboApplications").val();

var url = "/Roles/Edit";

$.ajax({
    cache: false,
    type: "POST",
    url: "/Roles/Edit",
    data: updatedRoleInfo,
    success: function (data, textStatus) {
        UpdateRoleComplete(data);
        GetRoles();                    //Fails when I call this.
        //self.Roles(data.RolesList);  //Works when I call this.
    },
    error: function (xhr, ajaxOptions, thrownError) {
        alert("Failed to update. " + "xhr.status: " + xhr.status + "xhr.responseText: " + xhr.responseText);
    }
});
}

function UpdateRoleComplete(result) {
self.EditFields(false);
self.ReadOnlyMode(true);
self.DisplayEditRoleButton(true);
self.DisplayUpdateRoleButton(true);
//self.OriginalRoleName(result.ViewModel.RoleName);
//self.OriginalDescription(result.ViewModel.Description);

$("#dialog-confirm").dialog("open");
}


$("#dialog-confirm").dialog({
autoOpen: false,
resizable: false,
modal: true,
draggable: true,
width: 300,
buttons: {
    "OK": function () {
        $(this).dialog("close");
    }
}
});

MVC:

 public ActionResult Index()
 {
        RoleIndexViewModel viewModel = new RoleIndexViewModel();
        Roles r = new Roles();

        viewModel = r.CreateRoleIndexViewModel();

        return View(viewModel);
 }

[HttpPost]
public ActionResult Edit(RoleViewModel rvm)
{
        RoleIndexViewModel viewModel = new RoleIndexViewModel();
        Roles r = new Roles();

        if (ModelState.IsValid)
        {
            using (db)
            {
                tblRole role = db.tblRoles.Single(t => t.RoleID == rvm.RoleID);
                role.RoleName = rvm.RoleName;
                role.Description = rvm.Description;
                role.ApplicationID = rvm.ApplicationID;

                db.ObjectStateManager.ChangeObjectState(role, EntityState.Modified);
                db.SaveChanges();
            }

            viewModel = r.CreateRoleIndexViewModel();
        }

        return Json(viewModel, JsonRequestBehavior.AllowGet);
}

Is the problem caused by nested AJAX calls, ie, in UpdateSelectedRole() and within it GetRoles()?

Thanks

Upvotes: 0

Views: 816

Answers (2)

Rahul
Rahul

Reputation: 5774

Because self.Roles is not global / in the function you are trying to set.

Can you make following changes and see if it's working?

Change

function UpdateSelectedRole() {
var updatedRoleInfo = new UpdatedRoleInfo();

updatedRoleInfo.RoleID = self.RoleID();
updatedRoleInfo.RoleName = self.RoleName();
updatedRoleInfo.Description = self.Description();
updatedRoleInfo.ApplicationID = self.ApplicationID();
//updatedRoleInfo.ApplicationID = $("#cboApplications").val();

var url = "/Roles/Edit";

$.ajax({
    cache: false,
    type: "POST",
    url: "/Roles/Edit",
    data: updatedRoleInfo,
    success: function (data, textStatus) {
        UpdateRoleComplete(data);
        GetRoles().success(function(data){
             self.Roles(data.RolesList);
        });

        //self.Roles(data.RolesList);  //Works when I call this.
    },
    error: function (xhr, ajaxOptions, thrownError) {
        alert("Failed to update. " + "xhr.status: " + xhr.status + "xhr.responseText: " + xhr.responseText);
    }
});
}

function GetRoles() {
return $.ajax({
    cache: false,
    type: "GET",
    url: "/Roles",
    dataType: "JSON"
});
}

GetRoles returns promise which I resolved in UpdateSelectedRole and populated self.Roles

Upvotes: 0

Muhammad Raheel
Muhammad Raheel

Reputation: 19882

In your ajax call first clear the roles array then pass new result in it like this

self.Roles([]);
self.Roles(vm.RolesList);

Also change your GetRoles function

self.GetRoles = () {
    // your code here
}

Upvotes: 1

Related Questions