Hojność
Minęło trochę czasu, a ja wciąż mam kilka nierozstrzygniętych pytań. Mam nadzieję, że przez dodanie nagrody może uzyskasz odpowiedzi na te pytania.
- Jak używać pomocników HTML w knockout.js
Dlaczego dokument był potrzebny, aby działał (zobacz pierwszą edycję, aby uzyskać więcej informacji)
Jak mogę zrobić coś takiego, jeśli używam mapowania wybicia w moich modelach widoku? Ponieważ nie mam funkcji ze względu na mapowanie.
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 };
Chcę na przykład używać wtyczek. Chcę mieć możliwość wycofania obserwowalnych, tak jakby użytkownik anulował żądanie. Chcę mieć możliwość powrotu do ostatniej wartości. Z moich badań wynika, że osiągają to ludzie tworzący wtyczki, takie jak elementy do edycji
Jak użyć czegoś takiego, jeśli używam mapowania? Naprawdę nie chcę przechodzić do metody, w której mam w widoku mapowanie ręczne, w którym mapuję każde pole MVC viewMode na pole modelu KO, tak jak chcę, jak najmniej wbudowanego javascript, a to wydaje się po prostu podwoić pracę i to jest dlaczego lubię to mapowanie.
Obawiam się, że aby ułatwić tę pracę (używając mapowania) stracę dużo mocy KO, ale z drugiej strony obawiam się, że ręczne mapowanie będzie po prostu dużo pracy i sprawi, że moje widoki będą zawierały zbyt dużo informacji i w przyszłości może stać się trudniejsze w utrzymaniu (powiedzmy, że jeśli usunę właściwość w modelu MVC, muszę ją przenieść również w modelu widoku KO)
Oryginalny post
Używam asp.net mvc 3 i patrzę na nokaut, ponieważ wygląda całkiem fajnie, ale mam trudności z ustaleniem, jak to działa z asp.net mvc, zwłaszcza widok modeli.
Dla mnie teraz robię coś takiego
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; }
}
Chciałbym mieć maszynę wirtualną, która ma kilka podstawowych właściwości, takich jak CourseName, i będzie miała kilka prostych walidacji. W razie potrzeby model Vm może również zawierać inne modele widoków.
Następnie przekazałbym to Vm do widoku, w którym użyłbym pomocników HTML, aby pomóc mi wyświetlić go użytkownikowi.
@Html.TextBoxFor(x => x.CourseName)
Mogę mieć jakieś pętle foreach lub coś do pobrania danych z kolekcji modeli widoku ucznia.
Następnie, serialize array
wysyłając formularz, użyłbym jquery i wysłałbym go do metody akcji kontrolera, która wiązałaby go z powrotem z viewmodelem.
Z knockout.js jest inaczej, ponieważ masz teraz modele widoku i ze wszystkich przykładów, które widziałem, nie używają pomocników HTML.
Jak wykorzystać te dwie funkcje MVC w knockout.js?
Znalazłem ten film i na krótko (ostatnie kilka minut filmu @ 18:48) omawia sposób korzystania z modeli widoków, zasadniczo mając wbudowany skrypt, który ma model widoku knockout.js, któremu przypisywane są wartości w modelu ViewModel.
Czy to jedyny sposób, aby to zrobić? A co z moim przykładem z kolekcją modeli widoku? Czy muszę mieć pętlę foreach czy coś, aby wyodrębnić wszystkie wartości i przypisać je do nokautu?
Jeśli chodzi o pomocników HTML, wideo nic o nich nie mówi.
To są 2 obszary, które mnie mylą, ponieważ niewiele osób wydaje się o tym mówić, a to sprawia, że jestem zdezorientowany, w jaki sposób początkowe wartości i wszystko docierają do punktu widzenia, kiedy przykład jest tylko jakimś zakodowanym przykładem wartości.
Edytować
Próbuję tego, co zasugerował Darin Dimitrov i wydaje się, że działa (musiałem jednak wprowadzić pewne zmiany w jego kodzie). Nie jestem pewien, dlaczego musiałem użyć gotowego dokumentu, ale jakoś bez niego wszystko nie było gotowe.
@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>
Musiałem owinąć go wokół dokumentu jQuery, gotowego do działania.
Otrzymuję również to ostrzeżenie. Nie wiem, o co w tym wszystkim chodzi.
Warning 1 Conditional compilation is turned off -> @Html.Raw
Mam więc punkt wyjścia, który przynajmniej się zaktualizuje, kiedy zrobię więcej zabawy i jak to działa.
Próbuję przejść przez interaktywne samouczki, ale zamiast tego używam ViewModel.
Nie wiem jeszcze, jak sobie z tym poradzić
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
}
lub
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
};
Edytuj 2
Udało mi się rozwiązać pierwszy problem. Nie mam pojęcia o drugim problemie. A jednak. Czy ktoś ma jakieś pomysły?
@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>
Kontroler
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);
}
Odpowiedzi:
Myślę, że podsumowałem wszystkie Twoje pytania, jeśli coś przeoczyłem, daj mi znać ( gdybyś mógł podsumować wszystkie pytania w jednym miejscu, byłoby miło =))
Uwaga. Zgodność z
ko.editable
dodaną wtyczkąPobierz pełny kod
Jak używać pomocników HTML w knockout.js
To jest łatwe:
@Html.TextBoxFor(model => model.CourseId, new { data_bind = "value: CourseId" })
Gdzie:
value: CourseId
wskazuje, że wiążeszvalue
właściwośćinput
kontrolki zCourseId
właściwością z modelu i modelu skryptuWynik to:
<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" />
Dlaczego dokument był potrzebny, aby działał (zobacz pierwszą edycję, aby uzyskać więcej informacji)
Nie rozumiem jeszcze, dlaczego musisz użyć
ready
zdarzenia do serializacji modelu, ale wydaje się, że jest to po prostu wymagane (nie martw się o to)Jak mogę zrobić coś takiego, jeśli używam mapowania wybicia w moich modelach widoku? Ponieważ nie mam funkcji ze względu na mapowanie.
Jeśli dobrze rozumiem, musisz dodać nową metodę do modelu KO, cóż, to łatwe łączenie modeli
Aby uzyskać więcej informacji, w sekcji -Mapowanie z różnych źródeł-
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); });
O ostrzeżeniu, które otrzymałeś
Musisz używać cudzysłowów
Zgodność z wtyczką ko.editable
Myślałem, że będzie to bardziej skomplikowane, ale okazuje się, że integracja jest naprawdę łatwa, aby umożliwić edycję modelu wystarczy dodać następujący wiersz: (pamiętaj, że w tym przypadku używam modelu mieszanego, z serwera i dodanie rozszerzenia w kliencie i edytowalne po prostu działa ... jest super):
ko.editable(g); ko.applyBindings(g);
Stąd wystarczy bawić się powiązaniami za pomocą rozszerzeń dodanych przez wtyczkę, na przykład mam przycisk, aby rozpocząć edycję moich pól w ten sposób iw tym przycisku rozpoczynam proces edycji:
this.editMode = function () { this.isInEditMode(!this.isInEditMode()); this.beginEdit(); };
Następnie mam przyciski zatwierdzenia i anulowania z następującym kodem:
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); } };
I na koniec mam jedno pole do wskazania, czy pola są w trybie edycji, czy nie, to tylko w celu powiązania właściwości enable.
this.isInEditMode = ko.observable(false);
O twoim pytaniu o tablicę
Możesz zrobić to samo z KO, w poniższym przykładzie utworzę następujący wynik:
Zasadniczo tutaj masz dwie listy, utworzone przy użyciu
Helpers
i powiązane z KO, mają onedblClick
powiązane zdarzenie, które po uruchomieniu usuwa wybrany element z bieżącej listy i dodaje go do drugiej listy, kiedy piszesz doController
, zawartość każdej lista jest wysyłana jako dane JSON i ponownie dołączana do modelu serweraBryłki:
Skrypty zewnętrzne .
Kod kontrolera
[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); }
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; } }
Strona 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> }
Skrypty
<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>
Uwaga: właśnie dodałem te linie:
@Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" }) @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })
Ponieważ kiedy przesyłam formularz moje pola są wyłączone, więc wartości nie zostały przesłane na serwer, dlatego dodałem kilka ukrytych pól, aby załatwić sprawę
źródło
ko.editables
wtyczką, możesz sprawdzić zaktualizowaną odpowiedź lub jeśli chcesz, możesz pobrać cały projekt, aby uruchomić go lokalnieMożesz serializować model widoku ASP.NET MVC do zmiennej 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>
W dokumentacji nokautu jest wiele przykładów, przez które możesz przejść.
źródło
Aby uzyskać dodatkowe obliczone właściwości po mapowaniu serwera, konieczne będzie dalsze ulepszenie modeli widoku po stronie klienta.
Na przykład:
var viewModel = ko.mapping.fromJS(model); viewModel.capitalizedName = ko.computed(function() {...}, viewModel);
Dlatego za każdym razem, gdy mapujesz z surowego formatu JSON, musisz ponownie zastosować obliczone właściwości.
Dodatkowo wtyczka mapująca zapewnia możliwość przyrostowej aktualizacji modelu widoku, w przeciwieństwie do ponownego tworzenia go za każdym razem, gdy przechodzisz tam iz powrotem (użyj dodatkowego parametru w
fromJS
):// Every time data is received from the server: ko.mapping.fromJS(data, viewModel);
I to wykonuje przyrostową aktualizację danych w modelu tylko mapowanych właściwości. Możesz przeczytać więcej na ten temat w dokumentacji mapowania
Wspomniałeś w komentarzach do odpowiedzi Darina o pakiecie FluentJSON . Jestem autorem tego, ale jego przypadek użycia jest bardziej szczegółowy niż ko.mapping. Generalnie użyłbym go tylko wtedy, gdy twoje modele widoku są jednokierunkowe (np. Serwer -> klient), a następnie dane są przesyłane z powrotem w jakimś innym formacie (lub wcale). Lub jeśli model widoku javascript musi mieć format znacznie różniący się od modelu serwera.
źródło