2014-09-04 134 views
0

背景:我很新MVC & Knockout.js,但我試圖趕上這些技術的速度。我正在使用EF6和Knockout.JS 3.2的MVC 5。連接MVC模型視圖和關聯的Knockout.js視圖模型

我有基於URL傳遞的ID,拉使用MVC一個「VoteAnswer」對象的詳細視圖:

例如,我可以到網址MYDOMAIN/VoteAnswers /細節/ 1,它會拉來自我的數據庫的信息正確(它拉取ID爲1的VoteAnswer)並顯示在我的Details視圖中。然而,我正在試圖連接我的Knockout.js「VoteAnswer」ViewModel以相同的方式運行,並遇到麻煩。

這裏是我的詳細信息視圖。(注意@ Html.DisplayFor(型號=> model.VoteAnswerId)等作品,並從我的數據庫顯示的數據

@model AM_SPA_TestSite.Models.VoteAnswer 

@{ 
    Layout = null; 
} 

<!DOCTYPE html> 

<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>Details</title> 
    <script src="~/KnockoutViewModels/VoteAnswers.js"></script> 
</head> 
<body> 
    <div> 
     <h4>VoteAnswer</h4> 
    <hr /> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td data-bind="text: id"></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td data-bind="text: isActive"></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td data-bind="text: displayText"></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td><input type="text" data-bind="value: id" /></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td><input type="text" data-bind="value: displayText" /></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td><input type="text" data-bind="value: isActive" /></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td>@Html.DisplayFor(model => model.VoteAnswerId)</td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td>@Html.DisplayFor(model => model.DisplayText)</td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td>@Html.DisplayFor(model => model.IsActive)</td> 
     </tr> 
    </table> 
</div> 

這裏是我的Knockout.Js視圖模​​型

// VoteAnswer ViewModel 
var VoteAnswerVM = { 

id: ko.observable(), 
displayText: ko.observable(), 
isActive: ko.observable(), 
SaveVoteAnswer: function() { 
    $.ajax({ 
     url: '/VoteAnswers/Create', 
     type: 'post', 
     dataType: 'json', 
     data: ko.toJSON(this), 
     contentType: 'application/json', 
     success: function (result) { 
     }, 
     error: function (err) { 
      if (err.responseText == "Creation Failed") 
      { window.location.href = '/VoteAnswers/Index/'; } 
      else { 
       alert("Status:" + err.responseText); 
       window.location.href = '/VoteAnswers/Index/';; 
      } 
     }, 
     complete: function() { 
      window.location.href = '/VoteAnswers/Index/'; 
     } 
    }); 
} 
}; 

//Go 
$(document).ready(function() { 
    //initialize and create new VoteAnswerVM by URL value here? 
    ko.applyBindings(VoteAnswerVM); 
}); 

我知道我缺少的是初始化視圖模型與1的ID,但我想MVC模式已經有數據和knockout.js應該映射到這些數據,而無需再次通過向數據庫發送請求來手動初始化。我錯過了什麼?謝謝。

EDIT添加溶液下面。我不確定我是如何解決這個問題的,但是現在是這樣。更新控制器以僅返回視圖而不查詢數據庫。 (否則我會擁有相同的數據的兩個數據庫調用。

// GET: VoteAnswers/Details/5 
    public ViewResult Details(int? id) 
    { 
     return View(); 
    } 

新增的API控制器,做查詢數據庫。

// GET: api/VoteAnswers/5 
    [ResponseType(typeof(VoteAnswer))] 
    public async Task<IHttpActionResult> GetVoteAnswer(int id) 
    { 
     VoteAnswer voteAnswer = await db.VoteAnswers.FindAsync(id); 
     if (voteAnswer == null) 
     { 
      return NotFound(); 
     } 

     return Ok(voteAnswer); 
    } 

在我看來(.cshtml文件)我引用我的淘汰賽。 JS模型視圖,示意如下:

<!DOCTYPE html> 

<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>Details</title> 
    <script src="~/KnockoutViewModels/VoteAnswers.js"></script> 
</head> 
<body> 
<div> 
    <h4>VoteAnswer</h4> 
    <hr /> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td data-bind="text: VoteAnswerId"></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td data-bind="text: IsActive"></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td data-bind="text: DisplayText"></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td><input type="text" data-bind="value: VoteAnswerId" /></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td><input type="text" data-bind="value: DisplayText" /></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td><input type="text" data-bind="value: IsActive" /></td> 
     </tr> 
    </table> 
</div> 
<div id="error"></div> 
</body> 
</html> 

更新了我的視圖模型腳本訪問基於URL ID數據庫

// VoteAnswer ViewModel 
var VoteAnswer = function() { 
var self = this; 
self.VoteAnswerId = ko.observable(); 
self.DisplayText = ko.observable(); 
self.IsActive = ko.observable(); 

self.SaveVoteAnswer = function() { 
    $.ajax({ 
     url: '/VoteAnswers/Create', 
     type: 'post', 
     dataType: 'json', 
     data: ko.toJSON(this), 
     contentType: 'application/json', 
     success: function (result) { 
     }, 
     error: function (err) { 
      if (err.responseText == "Creation Failed") 
      { window.location.href = '/VoteAnswers/Index/'; } 
      else { 
       alert("Status:" + err.responseText); 
       window.location.href = '/VoteAnswers/Index/';; 
      } 
     }, 
     complete: function() { 
      window.location.href = '/VoteAnswers/Index/'; 
     } 
    }); 
} 
self.load = function (id) { 
    if (id != 0) { 
     $.ajax({ 
      url: '/api/VoteAnswers/' + id, 
      type: 'get', 
      data: ko.toJSON(this), 
      contentType: 'application/json', 
      success: function(data) { 
       self.VoteAnswerId = ko.observable(data.voteAnswerId); 
       self.DisplayText = ko.observable(data.displayText); 
       self.IsActive = ko.observable(data.isActive); 
       ko.applyBindings(self); 
      }, 
      error: function(err) { 
       if (err.responseText == "Creation Failed") { 
        window.location.href = '/VoteAnswers/Index/'; 
       } else { 
        $("#error").text("Status:" + err.responseText); 
        //window.location.href = '/VoteAnswers/Index/';; 
       } 
      }, 
      complete: function() { 
       //window.location.href = '/VoteAnswers/Index/'; 
      } 
     }); 
    } else { 
     window.location.href = '/VoteAnswers/Index/'; 
    } 
} 
}; 
function GetURLParameter() { 
var sPageUrl = window.location.href; 
var indexOfLastSlash = sPageUrl.lastIndexOf("/"); 

if (indexOfLastSlash > 0 && sPageUrl.length - 1 != indexOfLastSlash) 
    return sPageUrl.substring(indexOfLastSlash + 1); 
else 
    return 0; 
} 
//Go 
$(document).ready(function() { 
    //initialize and create new VoteAnswerVM by URL value here? 
    var viewModel = new VoteAnswer(); 
    viewModel.load(GetURLParameter()); 
}); 
+0

ViewModel javascript是在單獨的JS文件中,還是直接內置於剃鬚刀頁面? – 2014-09-04 22:05:09

+0

你嘗試過'ko.applyBindings(new VoteAnswerVM());'這樣的事情 – 2014-09-05 06:41:59

+0

@Robert - 是的,它是一個單獨的JS文件,我確認它正在加載。我還驗證了,如果手動傳入參數,viewModel和knockout代碼將起作用。例如,如果我初始化爲:'ko.applyBindings(new VoteAnswerVM(1,「My Text」,true));'並將功能更改爲除這些屬性外。 – cmartin 2014-09-05 12:03:24

回答

0

我希望我能正確理解你,如果我的回答對你的問題都是錯誤的,請讓我知道我出錯的地方。

首先要認識到的是,如果將KO可觀察元素綁定到輸入字段,則淘汰將不會查看該輸入字段的初始值並將其存儲在可觀察元素中。它會做相反的事情:它會查看observable中的當前值並將其存儲在輸入字段的值中。在你的情況下,observable的初始化沒有值,在JavaScript中的意思是值undefined。因此,如果你將你的觀測值綁定到你已經用Razor/MVC視圖模型填充的字段,你將立即用存儲在你的觀測值中的空值覆蓋這些值。

有一種方法可以通過Razor將您的數據填充到Knockout模型中,但它涉及內聯JavaScript並且由於多種原因(我將根據請求詳細闡述)是一種不好的做法。

這樣做的最好方法是將視圖與數據分開:不要將MVC視圖模型注入到視圖中,而是創建一個單獨的端點來返回JSON並在其中返回數據(此端點將接收ID參數而不是視圖)。 JSON端點從JavaScript調用,可用於使用正確的值填充模型。

上行鏈路:關注點分離,爲更敏感的前端啓用視圖緩存的可能性,無需使用剃刀語法,或者更糟糕的是,將其與內聯JS結合使用。您的所有數據綁定到UI都將通過Knockout進行。我自己瞭解到這一點,因爲我們也開始使用剃鬚刀,但從長遠來看,這個解決方案對於一個大型項目來說是不可行的。我們從不後悔切換到始終從單獨的JSON端點獲取數據。

如果你不確定如何做到這一點,我可以寫一些僞代碼來說明這個想法。

+0

從來沒有通過這條線,但我希望看到你的'僞代碼',如果你能告訴我們。 – 2014-09-05 07:06:15

+0

感謝您的迴應漢斯 - 所以基本上你通過自己的JSON調用進行初始化。在我的場景中,URL來自路由配置文件/ VoteAnswers/Details/1(「{controller}/{action}/{id}」)。你是說只是手動解析URL來獲取ID來初始化knockout viewModel? – cmartin 2014-09-05 11:59:49

+0

這是一種方法,解析前端的URL,以便知道將哪個ID與請求一起發送以獲取數據。從您的代碼看來,您正在創建一個SPA,在這種情況下,通常會爲您完成URL解析,因此在ID部分可能會更容易。使用ID對JSON端點執行獲取請求,該端點返回現在是MVC視圖模型的模型,只轉換爲JSON(.NET具有JSON轉換器)。 – 2014-09-05 13:22:06

0

這可能是可能的解決方案

1) data_bind被誤用

<td><input type="text" data-bind="value: isActive" /></td> // which is wrong 

<td><input type="text" data_bind="value: isActive" /></td> //data_bind is wrongly used 

2) 如果仍然存在問題,你可以嘗試這句法

@Html.DisplayFor(model => model.IsActive, new { data_bind = "value:IsActive" }); 

如果您發現仍有遺漏,請提供一些de尾巴信息。