Asp.net-Mvc

如何將 knockout.js 與 ASP.NET MVC ViewModels 一起使用?

  • June 15, 2012

賞金

已經有一段時間了,我還有幾個懸而未決的問題。我希望通過增加賞金也許這些問題會得到解答。

  1. 你如何使用帶有 knockout.js 的 html 助手
  2. 為什麼需要準備好文件才能使其工作(有關更多資訊,請參閱第一次編輯)
  3. 如果我在視圖模型中使用剔除映射,我該怎麼做?由於映射,我沒有功能。
function AppViewModel() {

   // ... leave firstName, lastName, and fullName unchanged here ...

   this.capitalizeLastName = function() {

   var currentVal = this.lastName();        // Read the current value

   this.lastName(currentVal.toUpperCase()); // Write back a modified value

};
  1. 我想使用外掛,例如我希望能夠回滾 observables,就好像使用者取消了我希望能夠返回到最後一個值的請求一樣。根據我的研究,這似乎是通過製作可編輯外掛等外掛來實現的

如果我使用映射,我該如何使用類似的東西?我真的不想使用在我的視圖中手動映射的方法,即我將每個 MVC viewMode 欄位映射到一個 KO 模型欄位,因為我想要盡可能少的內聯 javascript,這似乎是工作的兩倍,那就是為什麼我喜歡那個映射。 5. 我擔心為了使這項工作變得容易(通過使用映射)我會失去很多 KO 權力,但另一方面我擔心手動映射只會做很多工作並且會使我的視圖包含太多資訊和將來可能會變得更難維護(例如,如果我在 MVC 模型中刪除一個屬性,我也必須在 KO 視圖模型中移動它)


原帖 我正在使用 asp.net mvc 3,我正在研究淘汰賽,因為它看起來很酷,但我很難弄清楚它如何與 asp.net mvc 一起工作,尤其是視圖模型。

對我來說,我現在做這樣的事情

public class CourseVM
   {
       public int CourseId { get; set; }
       [Required(ErrorMessage = "Course name is required")]
       [StringLength(40, ErrorMessage = "Course name cannot be this long.")]
       public string CourseName{ get; set; }


       public List<StudentVm> StudentViewModels { get; set; }

}

我將擁有一個具有一些基本屬性(如 CourseName)的 Vm,並且在它之上會進行一些簡單的驗證。如果需要,Vm 模型中也可能包含其他視圖模型。

然後,如果我會使用 html 助手幫助我將它顯示給使用者,我會將這個 Vm 傳遞給視圖。

@Html.TextBoxFor(x => x.CourseName)

我可能有一些 foreach 循環或其他東西來從學生視圖模型的集合中獲取數據。

然後,當我送出表單時,我會使用 jquery 並將serialize array其發送到控制器操作方法,該方法會將其綁定回視圖模型。

使用 knockout.js,一切都不同了,因為您現在獲得了它的視圖模型,並且從我看到的所有範例中它們不使用 html 幫助器。

你如何將 MVC 的這兩個特性與 knockout.js 一起使用?

我找到了這個影片,它簡要地(影片的最後幾分鐘 @ 18:48)通過基本上擁有一個內聯腳本來使用視圖模型,該腳本具有被分配 ViewModel 中的值的 knockout.js 視圖模型。

這是唯一的方法嗎?在我的範例中,其中包含一組視圖模型怎麼樣?我是否必須有一個 foreach 循環或其他東西來提取所有值並將其分配給淘汰賽?

至於 html 助手,影片對他們隻字未提。

這些是讓我感到困惑的兩個領域,因為似乎沒有多少人談論它,並且當範例只是一些硬編碼的值範例時,它讓我對初始值和所有內容如何進入視圖感到困惑。


編輯 我正在嘗試 Darin Dimitrov 的建議,這似乎可行(儘管我不得不對他的程式碼進行一些更改)。不知道為什麼我必須使用準備好的文件,但不知何故,如果沒有它,一切都沒有準備好。

@model MvcApplication1.Models.Test

@{
   Layout = null;
}

<!DOCTYPE html>

<html>
<head>
   <title>Index</title>
   <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script>
   <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script>
   <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
  <script type="text/javascript">

  $(function()
  {
     var model = @Html.Raw(Json.Encode(Model));


// Activates knockout.js
ko.applyBindings(model);
  });

</script>

</head>
<body>
   <div>
       <p>First name: <strong data-bind="text: FirstName"></strong></p>
       <p>Last name: <strong data-bind="text: LastName"></strong></p>
       @Model.FirstName , @Model.LastName
   </div>
</body>
</html>

我必須將它包裝在一個 jquery 文件周圍,以使其工作。

我也收到此警告。不知道這到底是怎麼回事。

Warning 1   Conditional compilation is turned off   -> @Html.Raw

所以我有一個起點,我想至少會在我做更多的遊戲以及它是如何工作的時候更新。

我正在嘗試閱讀互動式教程,但改用 ViewModel。

不知道如何處理這些部分

function AppViewModel() {
   this.firstName = ko.observable("Bert");
   this.lastName = ko.observable("Bertington");
}

要麼

function AppViewModel() {
   // ... leave firstName, lastName, and fullName unchanged here ...

   this.capitalizeLastName = function() {
       var currentVal = this.lastName();        // Read the current value
       this.lastName(currentVal.toUpperCase()); // Write back a modified value
   };

編輯 2 我能夠弄清楚第一個問題。對第二個問題毫無頭緒。然而雖然。有人有什麼想法嗎?

@model MvcApplication1.Models.Test

   @{
       Layout = null;
   }

   <!DOCTYPE html>

   <html>
   <head>
       <title>Index</title>
       <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script>
       <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script>
       <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
      <script type="text/javascript">

      $(function()
      {
       var model = @Html.Raw(Json.Encode(Model));
       var viewModel = ko.mapping.fromJS(model);
       ko.applyBindings(viewModel);

      });

   </script>

   </head>
   <body>
       <div>
           @*grab values from the view model directly*@
           <p>First name: <strong data-bind="text: FirstName"></strong></p>
           <p>Last name: <strong data-bind="text: LastName"></strong></p>

           @*grab values from my second view model that I made*@
           <p>SomeOtherValue <strong data-bind="text: Test2.SomeOtherValue"></strong></p>
           <p>Another <strong data-bind="text: Test2.Another"></strong></p>

           @*allow changes to all the values that should be then sync the above values.*@
           <p>First name: <input data-bind="value: FirstName" /></p>
           <p>Last name: <input data-bind="value: LastName" /></p>
           <p>SomeOtherValue <input data-bind="value: Test2.SomeOtherValue" /></p>
           <p>Another <input data-bind="value: Test2.Another" /></p>

          @* seeing if I can do it with p tags and see if they all update.*@
           <p data-bind="foreach: Test3">
               <strong data-bind="text: Test3Value"></strong> 
           </p>

    @*took my 3rd view model that is in a collection and output all values as a textbox*@       
   <table>
       <thead><tr>
           <th>Test3</th>
       </tr></thead>
         <tbody data-bind="foreach: Test3">
           <tr>
               <td>    
                   <strong data-bind="text: Test3Value"></strong> 
<input type="text" data-bind="value: Test3Value"/>
               </td>
           </tr>    
       </tbody>
   </table>

控制器

 public ActionResult Index()
   {
             Test2 test2 = new Test2
       {
           Another = "test",
           SomeOtherValue = "test2"
       };

       Test vm = new Test
       {
           FirstName = "Bob",
           LastName = "N/A",
            Test2 = test2,

       };
       for (int i = 0; i < 10; i++)
       {
           Test3 test3 = new Test3
           {
               Test3Value = i.ToString()
           };

            vm.Test3.Add(test3);
       }

       return View(vm);
   }

我想我已經總結了你所有的問題,如果我遺漏了什麼,請告訴我(如果你能在一個地方總結你所有的問題會很好=))

筆記。ko.editable添加了與外掛的兼容性

下載完整程式碼

你如何使用帶有 knockout.js 的 html 助手

這很容易:

@Html.TextBoxFor(model => model.CourseId, new { data_bind = "value: CourseId" })

在哪裡:

  • value: CourseId表示您正在valueinput控制項的CourseId屬性與模型和腳本模型中的屬性綁定

結果是:

<input data-bind="value: CourseId" data-val="true" data-val-number="The field CourseId must be a number." data-val-required="The CourseId field is required." id="CourseId" name="CourseId" type="text" value="12" />

為什麼需要準備好文件才能使其工作(有關更多資訊,請參閱第一次編輯)

我還不明白為什麼你需要使用ready事件來序列化模型,但似乎它只是必需的(雖然不用擔心)

如果我在視圖模型中使用剔除映射,我該怎麼做?由於映射,我沒有功能。

如果我理解正確,您需要向 KO 模型附加一個新方法,這很容易合併模型

有關更多資訊,請在 - 來自不同來源的映射部分 -

function viewModel() {
   this.addStudent = function () {
       alert("de");
   };
};

$(function () {
   var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
   var mvcModel = ko.mapping.fromJSON(jsonModel);

   var myViewModel = new viewModel();
   var g = ko.mapping.fromJS(myViewModel, mvcModel);

   ko.applyBindings(g);
});

關於您收到的警告

警告 1 條件編譯已關閉 -> @Html.Raw

您需要使用引號

與 ko.editable 外掛的兼容性

我認為它會更複雜,但事實證明集成非常簡單,為了使您的模型可編輯,只需添加以下行:(請記住,在這種情況下,我使用的是混合模型,來自伺服器和在客戶端中添加副檔名,並且可編輯的只是工作……這很棒):

   ko.editable(g);
   ko.applyBindings(g);

從這裡你只需要使用外掛添加的擴展來玩你的綁定,例如,我有一個按鈕來開始像這樣編輯我的欄位,在這個按鈕中我開始編輯過程:

   this.editMode = function () {
       this.isInEditMode(!this.isInEditMode());
       this.beginEdit();
   };

然後我有送出和取消按鈕,程式碼如下:

   this.executeCommit = function () {
       this.commit();
       this.isInEditMode(false);
   };
   this.executeRollback = function () {
       if (this.hasChanges()) {
           if (confirm("Are you sure you want to discard the changes?")) {
               this.rollback();
               this.isInEditMode(false);
           }
       }
       else {
           this.rollback();
           this.isInEditMode(false);
       }
   };

最後,我有一個欄位來指示欄位是否處於編輯模式,這只是綁定啟用屬性。

this.isInEditMode = ko.observable(false);

關於您的陣列問題

我可能有一些 foreach 循環或其他東西來從學生視圖模型的集合中獲取數據。

然後,當我送出表單時,我會使用 jquery 和序列化數組並將其發送到控制器操作方法,該方法會將其綁定回視圖模型。

您可以對 KO 執行相同操作,在以下範例中,我將創建以下輸出:

在此處輸入圖像描述

基本上在這裡,您有兩個列表,使用 KO 創建Helpers並與 KO 綁定,它們綁定了一個dblClick事件,當觸發時,從目前列表中刪除所選項目並將其添加到另一個列表,當您發佈到時Controller,每個列表的內容列表作為 JSON 數據發送並重新附加到伺服器模型

掘金:

外部腳本

控制器程式碼

   [HttpGet]
   public ActionResult Index()
   {
       var m = new CourseVM { CourseId = 12, CourseName = ".Net" };

       m.StudentViewModels.Add(new StudentVm { ID = 545, Name = "Name from server", Lastname = "last name from server" });

       return View(m);
   }

   [HttpPost]
   public ActionResult Index(CourseVM model)
   {
       if (!string.IsNullOrWhiteSpace(model.StudentsSerialized))
       {
           model.StudentViewModels = JsonConvert.DeserializeObject<List<StudentVm>(model.StudentsSerialized);
           model.StudentsSerialized = string.Empty;
       }

       if (!string.IsNullOrWhiteSpace(model.SelectedStudentsSerialized))
       {
           model.SelectedStudents = JsonConvert.DeserializeObject<List<StudentVm>(model.SelectedStudentsSerialized);
           model.SelectedStudentsSerialized = string.Empty;
       }

       return View(model);
   }

模型

public class CourseVM
{
   public CourseVM()
   {
       this.StudentViewModels = new List<StudentVm>();
       this.SelectedStudents = new List<StudentVm>();
   }

   public int CourseId { get; set; }

   [Required(ErrorMessage = "Course name is required")]
   [StringLength(100, ErrorMessage = "Course name cannot be this long.")]
   public string CourseName { get; set; }

   public List<StudentVm> StudentViewModels { get; set; }
   public List<StudentVm> SelectedStudents { get; set; }

   public string StudentsSerialized { get; set; }
   public string SelectedStudentsSerialized { get; set; }
}

public class StudentVm
{
   public int ID { get; set; }
   public string Name { get; set; }
   public string Lastname { get; set; }
}

CSHTML 頁面

@using (Html.BeginForm())
{
   @Html.ValidationSummary(true)
   <fieldset>
       <legend>CourseVM</legend>

       <div>
           <div class="editor-label">
               @Html.LabelFor(model => model.CourseId)
           </div>
           <div class="editor-field">
               @Html.TextBoxFor(model => model.CourseId, new { data_bind = "enable: isInEditMode, value: CourseId" })
               @Html.ValidationMessageFor(model => model.CourseId)
           </div>

           <div class="editor-label">
               @Html.LabelFor(model => model.CourseName)
           </div>
           <div class="editor-field">
               @Html.TextBoxFor(model => model.CourseName, new { data_bind = "enable: isInEditMode, value: CourseName" })
               @Html.ValidationMessageFor(model => model.CourseName)
           </div>
           <div class="editor-label">
               @Html.LabelFor(model => model.StudentViewModels);
           </div>
           <div class="editor-field">

               @Html.ListBoxFor(
                   model => model.StudentViewModels,
                   new SelectList(this.Model.StudentViewModels, "ID", "Name"),
                   new
                   {
                       style = "width: 37%;",
                       data_bind = "enable: isInEditMode, options: StudentViewModels, optionsText: 'Name', value: leftStudentSelected, event: { dblclick: moveFromLeftToRight }"
                   }
               )
               @Html.ListBoxFor(
                   model => model.SelectedStudents,
                   new SelectList(this.Model.SelectedStudents, "ID", "Name"),
                   new
                   {
                       style = "width: 37%;",
                       data_bind = "enable: isInEditMode, options: SelectedStudents, optionsText: 'Name', value: rightStudentSelected, event: { dblclick: moveFromRightToLeft }"
                   }
               )
           </div>

           @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
           @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })
           @Html.HiddenFor(model => model.StudentsSerialized, new { data_bind = "value: StudentsSerialized" })
           @Html.HiddenFor(model => model.SelectedStudentsSerialized, new { data_bind = "value: SelectedStudentsSerialized" })
       </div>

       <p>
           <input type="submit" value="Save" data-bind="enable: !isInEditMode()" /> 
           <button data-bind="enable: !isInEditMode(), click: editMode">Edit mode</button><br />
           <div>
               <button data-bind="enable: isInEditMode, click: addStudent">Add Student</button>
               <button data-bind="enable: hasChanges, click: executeCommit">Commit</button>
               <button data-bind="enable: isInEditMode, click: executeRollback">Cancel</button>
           </div>
       </p>
   </fieldset>
}

腳本

<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ko.editables.js")" type="text/javascript"></script>

<script type="text/javascript">
   var g = null;
   function ViewModel() {
       this.addStudent = function () {
           this.StudentViewModels.push(new Student(25, "my name" + new Date(), "my last name"));
           this.serializeLists();
       };
       this.serializeLists = function () {
           this.StudentsSerialized(ko.toJSON(this.StudentViewModels));
           this.SelectedStudentsSerialized(ko.toJSON(this.SelectedStudents));
       };
       this.leftStudentSelected = ko.observable();
       this.rightStudentSelected = ko.observable();
       this.moveFromLeftToRight = function () {
           this.SelectedStudents.push(this.leftStudentSelected());
           this.StudentViewModels.remove(this.leftStudentSelected());
           this.serializeLists();
       };
       this.moveFromRightToLeft = function () {
           this.StudentViewModels.push(this.rightStudentSelected());
           this.SelectedStudents.remove(this.rightStudentSelected());
           this.serializeLists();
       };
       this.isInEditMode = ko.observable(false);
       this.executeCommit = function () {
           this.commit();
           this.isInEditMode(false);
       };
       this.executeRollback = function () {
           if (this.hasChanges()) {
               if (confirm("Are you sure you want to discard the changes?")) {
                   this.rollback();
                   this.isInEditMode(false);
               }
           }
           else {
               this.rollback();
               this.isInEditMode(false);
           }
       };
       this.editMode = function () {
           this.isInEditMode(!this.isInEditMode());
           this.beginEdit();
       };
   }

   function Student(id, name, lastName) {
       this.ID = id;
       this.Name = name;
       this.LastName = lastName;
   }

   $(function () {
       var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
       var mvcModel = ko.mapping.fromJSON(jsonModel);

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

       g.StudentsSerialized(ko.toJSON(g.StudentViewModels));
       g.SelectedStudentsSerialized(ko.toJSON(g.SelectedStudents));

       ko.editable(g);
       ko.applyBindings(g);
   });
</script>

注意:我剛剛添加了這些行:

       @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
       @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })

因為當我送出表單時,我的欄位被禁用,所以值沒有傳輸到伺服器,這就是為什麼我添加了幾個隱藏欄位來解決問題

引用自:https://stackoverflow.com/questions/11055336