asp.net-mvc - 如何使用ASP.NET MVC ViewModels的knockout.js?





asp.net-mvc-3 (4)


為了在服務器映射之後獲得額外的計算屬性,您需要在客戶端進一步增強視圖模型。

例如:

var viewModel = ko.mapping.fromJS(model);

viewModel.capitalizedName = ko.computed(function() {...}, viewModel);

因此,每次從原始JSON映射時,都需要重新應用計算的屬性。

此外,映射插件提供增量更新視圖模型的功能,而不是每次來回重新創建視圖模型(在fromJS使用其他參數):

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

然後,對您映射的屬性模型執行增量數據更新。 你可以在映射文檔中閱讀更多關於它的信息

你在Darin的回答中提到了FluentJSON包。 我是那個的作者,但它的用例比ko.mapping更具體。 如果您的視圖模型是單向的(即服務器 - >客戶端),然後以某種不同格式(或根本不使用)發布數據,我通常只會使用它。 或者,如果您的javascript viewmodel需要與服務器模型的格式完全不同。

賞金

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

  1. 你如何使用knockout.js的html helper
  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
    
    };
    
  4. 我希望使用插件作為實例,我希望能夠回滾observables,就像用戶取消請求一樣,我希望能夠返回到最後一個值。 從我的研究來看,這似乎是通過製作editables插件等人來實現的

    如果我使用映射,我該如何使用類似的東西? 我真的不想去一個方法,在我看來,手動映射是我將每個MVC viewMode字段映射到一個KO模型字段,因為我想盡可能少用內聯javascript,而且看起來像是工作的兩倍,這就是為什麼我喜歡那個映射。

  5. 我擔心為了使這項工作變得簡單(通過使用映射),我將失去很多KO的力量,但另一方面,我擔心手工映射只是很多工作,並會使我的觀點包含太多的信息,可能會在未來變得更難以維護(比如說,如果我在MVC模型中刪除了一個屬性,我必須在KO視圖模型中移動它)

原始帖子

我正在使用asp.net mvc 3,並且我正在尋找knockout,因為它看起來很酷,但我很難弄清楚它是如何與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; }

}

我會有一個Vm,它具有一些像CourseName這樣的基本屬性,並且它會有一些簡單的驗證。 如果需要,Vm模型也可能包含其他視圖模型。

然後我會將這個Vm傳遞給View,我將使用HTML幫助器來幫助我將它顯示給用戶。

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

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

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

隨著knockout.js,它是所有不同,因為你現在有它的視圖模型,並從我看到他們不使用HTML助手的所有例子。

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

我發現這個視頻並簡要地介紹了視頻模型(最後幾分鐘的視頻@ 18:48),它基本上有一個內置腳本,它具有knockout.js視圖模型,該視圖模型被分配ViewModel中的值。

這是唯一的方法嗎? 在我的例子中有一個視圖模型的集合呢? 我必須有一個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);
    }



你可以將你的ASP.NET MVC視圖模型序列化為一個javascript變量:

@model CourseVM
<script type="text/javascript">
    var model = @Html.Raw(Json.Encode(Model));
    // go ahead and use the model javascript variable to bind with ko
</script>

淘汰賽文檔中有很多例子可以通過。




我想我已經總結了所有的問題,如果我錯過了一些事情,請讓我知道( 如果你可以在一個地方總結你所有的問題都會很好 =))

注意。 ko.editable插件的兼容性增加了

Download完整的代碼

你如何使用knockout.js的html helper

這很容易:

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

哪裡:

  • value: CourseId表示您將綁定input控件的CourseId屬性與來自模型和腳本模型的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(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做同樣的事情,在下面的例子中,我將創建以下輸出:

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

掘金:

外部scripts

控制器代碼

    [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" })

因為當我提交表單時,我的字段被禁用,所以這些值不會被傳送到服務器,這就是為什麼我添加了一些隱藏字段來執行該操作的原因




Html:

@using (Html.BeginForm("StoreMyCompany", "MyCompany", FormMethod.Post, new { id = "formMyCompany", enctype = "multipart/form-data" }))
{
   <div class="form-group">
      @Html.LabelFor(model => model.modelMyCompany.Logo, htmlAttributes: new { @class = "control-label col-md-3" })
      <div class="col-md-6">
        <input type="file" name="Logo" id="fileUpload" accept=".png,.jpg,.jpeg,.gif,.tif" />
      </div>
    </div>

    <br />
    <div class="form-group">
          <div class="col-md-offset-3 col-md-6">
              <input type="submit" value="Save" class="btn btn-success" />
          </div>
     </div>
}  

Code Behind:

public ActionResult StoreMyCompany([Bind(Exclude = "Logo")]MyCompanyVM model)
{
    try
    {        
        byte[] imageData = null;
        if (Request.Files.Count > 0)
        {
            HttpPostedFileBase objFiles = Request.Files["Logo"];

            using (var binaryReader = new BinaryReader(objFiles.InputStream))
            {
                imageData = binaryReader.ReadBytes(objFiles.ContentLength);
            }
        }

        if (imageData != null && imageData.Length > 0)
        {
           //Your code
        }

        dbo.SaveChanges();

        return RedirectToAction("MyCompany", "Home");

    }
    catch (Exception ex)
    {
        Utility.LogError(ex);
    }

    return View();
}




asp.net-mvc asp.net-mvc-3 knockout.js