Kendo ListView:Add/Create 在刷新浏览器会话之前不会反映出来

Kendo ListView: Add/Create not reflected until browser session is refreshed

我正在使用 MVC 和 Kendo (UI v2018.3.1017) ListView 来管理我称为 'MaximoList' 的列表。为了使列表对多个控制器可用,我利用了 MVC 提供的“TempData”对象。在创建(和销毁)时,我看到 MaximoList 对象正确反映了更改。但是为了反映 UI 中的变化,我必须刷新 (F5) (Chrome) 浏览器。

对控件的 CSHTML 引用相当简单:

    <div class="col-md-3">
      <div class="form-group">
        <label style="white-space: nowrap;">Maximo Id's</label>
        <a id="btnAddMaximoEntry" onclick=window.allIsos.addMaximoEntry() href="#" title='Add Maximo' style='margin-left: 8px'><i class='fa fa-upload'></i></a>
        <div class="" id="divMaximoList">
        </div>
      </div>
    </div>

控件的JS定义是:

function addMaximoListView(isoId, outageId) {
  maximoListDataSource = new kendo.data.DataSource({
    transport: {
      read: { url: "/request/GetMaximoList", type: "GET", dataType: "json", cache: false },
      create: { url: "/request/AddMaximoEntry", type: "Post", dataType: "json", cache: false },
      destroy: { url: "/request/DeleteMaximoEntry", type: "POST", dataType: "json", cache: false },
      update: {},
      parameterMap: function (data, type) {
        if (type == "read") { return { IsoId: isoId, OutageId: outageId }; }
        if (type == "create") { return { MaximoId: data.MaximoId }; }
        if (type == "destroy") { return { id: data.Id }; }
      },
      sortable: true,
      pageable: true,
    },
    requestEnd: function (e) {
      console.log(e.type);
      console.log(e.response);
    },
    columns: [
      { field: "Id", Title: "Id", width: "0px" },
      { field: "MaximoId", Title: "MaximoId", width: "50px" },
      { command: ["edit", "destroy"], title: "&nbsp;", width: "250px" }
    ],
    autoSync: false,
    schema: {
      model: {
        id: "Id",
        fields: {
          Id: { editable: false },
          MaximoId: { editable: true, type: "string" },
        }
      },
    },
  });
  var getTemplate = ""
    + "<div>"
    + "    <a onclick=window.allIsos.deleteMaximo(#:Id#) href='\#'><i class='fa fa-trash'></i></a>"
    + '#:MaximoId#</div>';
  var editTemplate = ""
    + "<div>"
    + "  <input type='text' data-bind='value: MaximoId' name='MaximoId' required='required' />"
    + "  <div>"
    + "    <a class='k-button k-update-button' href='\#'><span class='k-icon k-i-check'></span></a>"
    + "    <a class='k-button k-cancel-button' href='\#'><span class='k-icon k-i-cancel'></span></a>"
    + "  </div>"
    + "</div>";
  $("#divMaximoList").kendoListView({
    template: kendo.template(getTemplate),
    editTemplate: kendo.template(editTemplate),
    dataSource: maximoListDataSource,
    pageSize: 5,
    dataBound: function (e) {
      console.log("ListView is bound and ready to render.");
    }
  });
};

将项目添加到列表的 JS 定义是:

var addMaximoEntry = function () {
  var listView = $("#divMaximoList").data("kendoListView");
  listView.add();
};

正在重新启动调试本地应用程序,这里是公开来自 API/Database:

的测试数据的控件

我点击了我的添加按钮,在接受条目之前,请注意我的新 MaximoId 的输入值 'NotWorking':

接受输入后,请注意控件添加了一个新条目,但它是原始项目之一的幽灵:

但是在刷新页面 (F5) 后,请注意当 GET 触发从服务器端 TempData 对象拉取时,实际的 'NotWorking' 项目已被服务器正确接收,并且更新的 TempData 对象被传递给UI(包括新条目):

Create/Add 的 API 方法如下 - 我返回到 UI,更新后的 MaximoList 对象的 JSON 表示并使用它更新对象,刷新到 TempData["MaximoList"]:

[HttpPost, Route("AddMaximoEntry")]
public ActionResult AddMaximoEntry(string MaximoId)
{
  var ignoreCase = StringComparison.InvariantCultureIgnoreCase;
  try
  {
    List<MaximoEntry> maximoList = TempData["MaximoList"] != null ? TempData["MaximoList"] as List<MaximoEntry> : new List<MaximoEntry>();

    var maximoId = MaximoId;// unnecessary variable, but used for testing against multiple method confgurations

    var maximoEntry = new MaximoEntry();
    maximoEntry.MaximoId = maximoId;

    if (maximoList.Where(s => s.MaximoId.Equals(maximoEntry.MaximoId, ignoreCase)).FirstOrDefault() == null)
      maximoList.Add(maximoEntry);

    TempData["MaximoList"] = maximoList as List<MaximoEntry>;
    return Json(maximoList, JsonRequestBehavior.AllowGet);
  }
  catch (Exception ex)
  {
    var msg = $"Excepton: {ex.Message}. Inner Expection: {(ex.InnerException == null ? "N/A" : ex.InnerException.ToString())}";
    return new JsonResult
    {
      Data = new
      {
        id = 0,
        Success = false,
        Msg = "Something went wrong while adding to the Maximo List. Please contact system support!"
      }
    };
  }
}

而 API GET 如下:

[HttpGet, Route("GetMaximoList")]
public ActionResult GetMaximoList(int IsoId, int OutageId)
{
  try
  {
    List<MaximoEntry> maximoList = ODataGetMaximoList(IsoId, OutageId);
    return Json(maximoList, JsonRequestBehavior.AllowGet);
  }
  catch (Exception ex)
  {
    var msg = $"Excepton: {ex.Message}. Inner Expection: {(ex.InnerException == null ? "N/A" : ex.InnerException.ToString())}";
    return new JsonResult
    {
      Data = new
      {
        id = 0,
        Success = false,
        Msg = "Something went wrong while getting Maximo List. Please contact system support!"
      }
    };
  }
}

最初,ADD(CREATE) 的 API 方法传回了 Maximo 条目的整个列表(JSON 数组)。

解决方案是仅传回最初传递给 ADD(CREATE) 方法的 model 对象的更新版本,包括新分配的“id”值。新创建的条目将有一个负索引值,稍后我将在 MERGE 语句中的数据库级别使用它(即主键的负值不应该存在于数据库中table)来同步数据库。

如果还不清楚,我正在使用 TempData["MaximoList"] 对象来维护跨 API 控制器的 Kendo ListView 控件的状态。将控件状态保存到 DB(利用 TempData 对象)将发生在更通用的控制器方法中,该方法不管理 TempData 对象的状态(adds/deletes/etc。)。

我还重新设计了 DESTROY(删除)组件以仅在成功的 API 调用时从 (UI) 列表中删除已删除的项目,并通过删除一个清理了 JS不需要反映 TempData 对象状态的控件的不必要集数。

我的 API ADD 方法现在是:

[HttpPost, Route("AddMaximoEntry")]
public JsonResult AddMaximoEntry(MaximoEntry model)
{
  var ignoreCase = StringComparison.InvariantCultureIgnoreCase;
  try
  {
    List<MaximoEntry> maximoList = TempData["MaximoList"] != null ? TempData["MaximoList"] as List<MaximoEntry> : new List<MaximoEntry>();

    var minId = maximoList.Where(w => w.id <= 0);
    model.id = minId.Count() == 0 ? -1 : minId.Select(s => s.id).Min() - 1;

    if (maximoList.Where(s => s.maximoId.Equals(model.maximoId, ignoreCase)).FirstOrDefault() == null)
      maximoList.Add(model);

    TempData["MaximoList"] = maximoList as List<MaximoEntry>;
    return Json(model, JsonRequestBehavior.AllowGet);
  }
  catch (Exception ex)
  {
    var msg = $"Excepton: {ex.Message}. Inner Expection: {(ex.InnerException == null ? "N/A" : ex.InnerException.ToString())}";
    return new JsonResult
    {
      Data = new
      {
        Success = false,
        Msg = "Something went wrong while adding to the Maximo List. Please contact system support!"
      }
    };
  }
}

我还更新了我的 DELETE 方法,作为 HTTPDELETE 方法,并返回一个状态指示器来提醒调用 JS 操作结果:

[HttpDelete, Route("DeleteMaximoEntry")]
public JsonResult DeleteMaximoEntry(int id)
{
  try
  {
    List<MaximoEntry> maximoList = TempData["MaximoList"] != null ? TempData["MaximoList"] as List<MaximoEntry> : new List<MaximoEntry>();

    var itemToRemove = maximoList.SingleOrDefault(r => r.id == id);
    if (itemToRemove != null)
      maximoList.Remove(itemToRemove);

    TempData["MaximoList"] = maximoList as List<MaximoEntry>;

    return Json(new { success = true, responseText = $"id {id} deleted from TempData object" });
  }
  catch (Exception ex)
  {
    var msg = $"Excepton: {ex.Message}. Inner Expection: {(ex.InnerException == null ? "N/A" : ex.InnerException.ToString())}";
    return new JsonResult
    {
      Data = new
      {
        Success = false,
        Msg = $"Something went wrong while deleteing Maximo Entry (id {id}). Please contact system support!"
      }
    };
  }
}

SELECT (READ) 和 CREATE(Add) 的 JS 控件定义是:

function addMaximoListView(isoId, outageId) {
  maximoListDataSource = new kendo.data.DataSource({
    transport: {
      batch: false,//default is false
      read: { dataType: "json", cache: false, type: "GET", url: "/request/GetMaximoList" },
      create: { dataType: "json", cache: false, type: "POST", url: "/request/AddMaximoEntry" },
      parameterMap: function (data, type) {
        if (type == "read") { return { IsoId: isoId, OutageId: outageId }; }
        if (type == "create") { return { model: data }; }
      },
    },
    requestEnd: function (e) {
      console.log(e.type);
      console.log(e.response);
    },
    autoSync: false,
    serverFiltering: true,
    schema: {
      model: {
        id: "id",
        fields: {
          maximoId: { editable: true, type: "string" },
        }
      },
    },
  });
  var getTemplate = ""
    + "<div>"
    + "  <a onclick=window.allIsos.deleteMaximo(#:id#) href='\#'><i class='fa fa-trash'></i></a>"
    + "  #:maximoId#"
    + "</div > ";
  var editTemplate = ""
    + "<div>"
    + "  <input type='text' data-bind='value: maximoId' name='MaximoId' required='required' />"
    + "  <div>"
    + "    <a class='k-button k-update-button' href='\#'><span class='k-icon k-i-check'></span></a>"
    + "    <a class='k-button k-cancel-button' href='\#'><span class='k-icon k-i-cancel'></span></a>"
    + "  </div>"
    + "</div>";

  $("#divMaximoList").kendoListView({
    template: kendo.template(getTemplate),
    editTemplate: kendo.template(editTemplate),
    dataSource: maximoListDataSource,
    dataBound: function (e) {
      console.log("ListView is bound and ready to render.");
    }
  });
};

对于DESTROY(DELETE),我添加了这些JS方法(希望将这些元素嵌入到控件定义中):

var deleteMaximo = function (id) {
  executeDeleteMaximo(id, "Are you sure you want to delete this Maximo entry?");
}

function executeDeleteMaximo(id, confirmMsg) {
  common.LoadingGif("#mainView", false);
  common.ConfirmDialog(true, confirmMsg,
    function (reason) {
      if (reason == "NO") {
        common.LoadingGif("#mainView", true);
        return false;
      }
      $.ajax({
        type: "DELETE",
        data: { id: id },
        url: "/request/DeleteMaximoEntry",
        success: function (response) {
          var data = response;
          if (data.success) {
            var myDataSource = $("#divMaximoList").data().kendoListView.dataSource;
            var item = myDataSource.get(id);//need a check before attempting remove
            myDataSource.remove(item);

          }
          common.LoadingGif("#mainView", true);
          window.scrollTo(0, 0);
        },
        error: function (jqXHR, textStatus, errorThrown) {
          var e = errorThrown;
        }
      });
    });
}