[解決済み] ASP.NET MVC ViewModelsでknockout.jsを使用するには?
質問
懸賞金
しばらく経ちますが、まだいくつかの未解決の質問があります。懸賞金を追加することで、これらの質問に答えられるようになることを期待しています。
- どのように knockout.js で html ヘルパーを使用するのですか?
-
なぜdocument readyが必要だったのか(詳しくは最初の編集をご覧ください)
-
私のビューモデルでノックアウトマッピングを使用している場合、このようなことを行うにはどうすればよいですか?私はマッピングのために関数を持っていないように。
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 };
-
私はプラグインを使いたいと思っています。例えば、ユーザーがリクエストをキャンセルした場合、最後の値に戻ることができるように、observableをロールバックできるようにしたいのです。調べてみると、以下のようなプラグインを作ることで実現しているようです。 editables
マッピングを使用している場合、そのようなものをどのように使用するのですか?私はインラインのJavaScriptをできるだけ少なくしたいので、各MVC viewModeフィールドをKOモデルフィールドにマッピングするような手動マッピングをビューに持つ方法に行きたくありませんし、それは単に二重作業のように思えます。
-
私は、この作業を簡単にするために (マッピングを使用することによって) 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; }
}
CourseNameのような基本的なプロパティを持つVmがあり、その上にいくつかの簡単な検証を行います。Vmモデルは、必要に応じて他のビューモデルを含むかもしれません。
このVmをViewに渡し、htmlヘルパーを使用してユーザーに表示することができます。
@Html.TextBoxFor(x => x.CourseName)
Student View Modelsのコレクションからデータを取り出すために、foreachループか何かを用意するかもしれませんね。
それから、フォームを送信するときに、jqueryと
serialize array
を使用して、それをコントローラのアクションメソッドに送り、ビューモデルにバインドします。
knockout.jsでは、ビューモデルが用意されており、私が見たすべての例では、htmlヘルパーは使用されていなかったので、すべてが異なっています。
MVCのこの2つの機能をknockout.jsでどのように使うのでしょうか?
私が見つけたのは このビデオ で、簡単に(ビデオの最後の数分 @ 18:48)ビューモデルを使用する方法について説明しています。基本的に、ビューモデルの値を割り当てる knockout.js ビューモデルを持つインラインスクリプトを持つことで、ビューモデルを使用します。
これは唯一の方法なのでしょうか?私の例では、ビューモデルのコレクションを持つのはどうでしょうか?すべての値を抽出し、ノックアウトに割り当てるために、foreachループか何かを持たなければならないのでしょうか?
htmlヘルパーについては、ビデオには何も書かれていません。
これらの2つの領域は、多くの人がそれについて話していないように見えるので、私を混乱させ、初期値やすべてがどのようにビューに到達しているのか、これまでの例が単にハードコードされた値の例である場合、私を混乱させたままにします。
編集
私はDarin Dimitrovが提案したことを試しており、これは動作するようです(私は彼のコードにいくつかの変更を加えなければなりませんでしたが)。なぜ document ready を使用しなければならないのかわかりませんが、なぜかそれなしではすべてが準備できていませんでした。
@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 document readyに巻きつけて動作させる必要がありました。
こんな警告も出ます。どういうことなのかよくわかりません。
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
最初の問題は把握できました。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);
}
どのように解決するのですか?
質問はすべてまとめたつもりですが、もし何か見落としていたら教えてください ( あなたが1つの場所ですべてのあなたの質問を要約することができれば、それは素晴らしいことです。 =))
注意 との互換性は
ko.editable
プラグインが追加されました。
ダウンロード 完全なコード
knockout.jsでhtmlヘルパーを使用する方法
これは簡単です。
@Html.TextBoxFor(model => model.CourseId, new { data_bind = "value: CourseId" })
どこで
-
value: CourseId
はvalue
プロパティをinput
コントロールに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);
}
};
最後に、フィールドが編集モードかどうかを示すフィールドを1つ持っていますが、これはenableプロパティをバインドするためだけです。
this.isInEditMode = ko.observable(false);
配列に関する質問について
Student View Modelsのコレクションからデータを取り出すために、foreachループか何かを用意するかもしれませんね。
そして、フォームを送信するときに、jqueryを使用して配列をシリアライズし、それをコントローラのアクションメソッドに送信して、ビューモデルにバインドし直します。
KOでも同じことができます。次の例では、次のような出力を作成します。
基本的にここでは、2つのリストを
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" })
フォームを送信するとき、私のフィールドは無効になっているので、値はサーバーに送信されませんでした、そのため、トリックを行うためにいくつかの隠しフィールドを追加しました。
関連
-
[解決済み] System.Web.HttpException (0x80004005)。リクエストの最大長を超えました
-
[解決済み] Automapper のタイプマップ構成がない、またはサポートされていないマッピング - エラー
-
[解決済み] ファイルアップロード ASP.NET MVC 3.0
-
[解決済み] ASP.NET MVC Frameworkで複数のサブミットボタンを処理する方法は?
-
[解決済み] ASP.NET MVCでenumからドロップダウンリストを作成するにはどうすればよいですか?
-
[解決済み] ASP.NET MVCでビューをコンパイルする
-
[解決済み] ELMAHをASP.NET MVCの[HandleError]属性で動作させる方法は?
-
[解決済み】ASP.NET MVC 3 razor ViewStart ファイルで異なるレイアウトを指定するには?
-
[解決済み】ASP.NET mvcとIISでURLのドットが原因で404になる
-
[解決済み] ASP.NET MVC 1でHttpContextBaseからHttpContextオブジェクトを取得するにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] glyphicons-halflings-regular.woff2 not foundに関するエラーを削除する方法
-
[解決済み] Razor View throwing "The name 'model' does not exist in the current context".
-
[解決済み] MVC 3 - 辞書に渡されたモデル項目の型は 'System.Collections.Generic.List`1 です。
-
[解決済み] MVCにおけるViewModelとは?
-
[解決済み】ASP.NET MVCのフォームでチェックボックスを処理するにはどうすればよいですか?
-
[解決済み】cshtmlテンプレートで関数を作成する方法は?
-
[解決済み] MVCとRazorにおけるHtml.TextboxForとHtml.EditorForの相違点
-
[解決済み] mvc 4 で部分ビューにパラメータを渡すにはどうすればよいですか?
-
[解決済み] ASP.NET MVC: UrlHelperを使用するコントローラのユニットテスト
-
[解決済み] 型名または名前空間名 'DbContext' が見つかりませんでした [閉鎖]。