Jak używać knockout.js z ASP.NET MVC ViewModels?

129

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.

  1. Jak używać pomocników HTML w knockout.js
  2. Dlaczego dokument był potrzebny, aby działał (zobacz pierwszą edycję, aby uzyskać więcej informacji)

  3. 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
    
    };
    
  4. 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.

  5. 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 arraywysył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);
    }
chobo2
źródło
2
Właśnie napisałem post na blogu, aby odpowiedzieć na inne podobne pytanie: roysvork.wordpress.com/2012/12/09/… Może nie odpowiedzieć w pełni na Twoje pytanie, ale daje dobre wyobrażenie o tym, jak to wszystko może działać. Mam nadzieję, że w niedalekiej przyszłości dołączę do tego kolejny post. Zapraszam do zadawania mi jakichkolwiek pytań w komentarzach do wpisu lub tutaj, jeśli potrzebujesz więcej informacji.
poza kodem

Odpowiedzi:

180

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.editabledodaną 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: CourseIdwskazuje, że wiążesz valuewłaściwość inputkontrolki z CourseIdwłaściwością z modelu i modelu skryptu

Wynik 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ć readyzdarzenia 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ś

Ostrzeżenie 1 Kompilacja warunkowa jest wyłączona -> @ Html.Raw

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ę

Mogę mieć jakieś pętle foreach lub coś do pobrania danych z kolekcji modeli widoku ucznia.

Następnie, wysyłając formularz, użyłbym jQuery i serializacji tablicy i wysłałbym go do metody akcji kontrolera, która wiązałaby go z powrotem z Viewmodel.

Możesz zrobić to samo z KO, w poniższym przykładzie utworzę następujący wynik:

wprowadź opis obrazu tutaj

Zasadniczo tutaj masz dwie listy, utworzone przy użyciu Helpersi powiązane z KO, mają one dblClickpowiązane zdarzenie, które po uruchomieniu usuwa wybrany element z bieżącej listy i dodaje go do drugiej listy, kiedy piszesz do Controller, zawartość każdej lista jest wysyłana jako dane JSON i ponownie dołączana do modelu serwera

Brył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ę

Jupaol
źródło
Hmm bardzo pouczające. Z twojej odpowiedzi i odpowiedzi Puala myślę, że prawie uzyskałem odpowiedzi na wszystkie moje pytania z wyjątkiem tego, jak używać wtyczek, takich jak edytowalne. Mam nadzieję, że ktoś wie, jak mogę to wykorzystać.
chobo2
1
Właśnie dodałem kompatybilność z ko.editableswtyczką, możesz sprawdzić zaktualizowaną odpowiedź lub jeśli chcesz, możesz pobrać cały projekt, aby uruchomić go lokalnie
Jupaol
Sprawdzę to, kiedy będę mógł. Czy wiele musiało się zmienić, aby zadziałało? Zastanawiam się, czy dla każdej wtyczki znajdę, czy będę musiał coś w niej zmienić, a potem zachować własną wersję.
chobo2
Nie. Będziesz zaskoczony, jest prawie po wyjęciu z pudełka
Jupaol
1
Wielkie dzięki, z twojej odpowiedzi nauczyłem się kilku nowych strategii. Sława!
sky-dev,
23

Moż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ść.

Darin Dimitrov
źródło
1
Tak, przeszedłem przez interaktywny samouczek, który mają w witrynie, ale tak naprawdę nigdy nie widzę nic wspólnego z asp.net mvc. Widzę, że mają też jakąś wtyczkę mapującą, ale nie jestem pewien, jak to pasuje. W twoim przykładzie, jak byś powiązał ją z modelem knockout (w innym skrypcie). Naprawdę chcę mieć jak najmniej wbudowanego javascript (nie jest preferowane, ale myślę, że nie jest możliwe)
chobo2
2
Jaki problem próbujesz rozwiązać? Jeśli chcesz widoków MVC i jesteś zadowolony z tego, jak z nich korzystać, możesz się tam trzymać. Jeśli chcesz wiązać dane po stronie klienta i manipulować nimi, KO jest doskonałym wyborem. Możesz wygenerować swój model widoku KO z kodu MVC, jak pokazuje ta odpowiedź. Pobiera maszynę wirtualną i serializuje ją do json. Następnie na kliencie możesz zmapować wyniki do modelu widoku javascript. Następnie połącz model widoku z widokiem i gotowe. Kluczem jest to, że MVC i KO nie muszą być w żaden sposób łączone, chyba że tego chcesz. Wszystko zależy od problemu, który próbujesz rozwiązać.
John Papa
1
To normalne, że nie widzisz nic związanego z mvc asp.net. Knockout to framework po stronie klienta. Nie wie ani nie dba o to, jakiego języka serwera używasz. Te dwie ramy powinny być absolutnie oddzielone.
Darin Dimitrov
@JohnPapa - podoba mi się sposób, w jaki teraz robię, ale lubię też uczyć się nowych rzeczy (widzę, że KO może być bardzo przydatne w niektórych sytuacjach). Wiem, że KO to skrypt po stronie klienta, ale uważam je za współpracę. Obecnie generuję widoki za pomocą modeli widoków i pomocników HTML. Więc moim zdaniem KO musi z tym współpracować. Na przykład powiedz, że masz okno dialogowe edycji. Jak zaprojektowałbyś i wypełnił wartości z bazy danych w tych polach. Gdybym używał swojego sposobu, byłby to widok pomocników html, który ma viewModel. Wypełniłby viewmodel i wysłał go za pomocą metody akcji i użył go.
chobo2
1
@ chobo2, knockout to framework po stronie klienta. Używa modeli widoku na kliencie do implementacji wzorca MVC na kliencie. Serwer jest odłączony. Możesz również użyć na nim modeli widoku. To tylko 2 różne miejsca. Jeśli masz jakąś złożoną logikę, którą chcesz zaimplementować na kliencie za pomocą javascript, to knockout może to uprościć. W przeciwnym razie, szczerze, nie potrzebujesz tego.
Darin Dimitrov
2

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.

Paul Tyng
źródło
Hmm, więc myślę, że może FluentJSON nie jest dla mnie, ponieważ moje modele widoków przez większość czasu działają w obie strony (zwykle wysyłam je z powrotem przez json, a następnie wiążę to z viewmodelem w parametrze metody akcji). Czy wiesz, jak mogłem używać tych wtyczek, o których wspomniałem, takich jak edytowalne? Wreszcie, czy tracę jakąkolwiek funkcjonalność, używając mapowania i próbując użyć mojego modelu widoku, a nie go nie używać?
chobo2,
Nie korzystałem z żadnej wtyczki, więc nie jestem pewien. W przeszłości po prostu subskrybowałem każdą zmianę i utrzymywałem stos serializowanych stanów modelu widoku, do których chciałbym naciskać przy zmianie i wyskakiwać przy cofaniu ( zobacz to pytanie ).
Paul Tyng,
mapowanie nie powstrzymuje Cię przed żadną funkcjonalnością, po prostu musisz upewnić się i przestrzegać konwencji, w jaki sposób obsługuje mapowanie do iz JS, aby wszystko grało dobrze razem.
Paul Tyng,
Cóż, akceptowaną odpowiedzią na opublikowane przez Ciebie pytanie jest w zasadzie to, jaka byłaby wtyczka. To jest właśnie to, co mnie wprawia w zakłopotanie, ponieważ widzisz, że tworzą model widoku, a następnie używają swojej funkcji, którą stworzyli (ko.observableArrayWithUndo ([])). Jeśli robię mapowanie, nie wiem, jak to zrobić. Jedyne, co przychodzi mi do głowy, to napisanie własnego mapowania (co wątpię, żebym mógł to zrobić w tej chwili), które ma możliwość cofnięcia obserwacji lub odwzorowania każdej właściwości, ale w zasadzie mam zduplikowane modele widoku, jeden dla serwera i jeden dla klienta i jestem boi się, że stanie się nie do utrzymania
chobo2
Ach tak, przepraszam, że mówiłem o mojej odpowiedzi na to pytanie, przepraszam, że powinienem był bezpośrednio połączyć.
Paul Tyng,