Come utilizzare knockout.js con ASP.NET MVC ViewModels?




asp.net-mvc asp.net-mvc-3 (2)

È possibile serializzare il modello di visualizzazione ASP.NET MVC in una variabile 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>

Ci sono molti esempi nella documentazione a eliminazione diretta che potresti seguire.

generosità

È passato un po 'di tempo e ho ancora un paio di domande in sospeso. Spero che aggiungendo una taglia, queste domande avranno una risposta.

  1. Come si usano helper HTML con knockout.js
  2. Perché il documento era pronto per farlo funzionare (vedi prima modifica per maggiori informazioni)

  3. Come faccio a fare qualcosa di simile se sto usando la mappatura ad eliminazione diretta con i miei modelli di visualizzazione? Dato che non ho una funzione a causa della mappatura.

    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. Voglio usare i plugin, per esempio, voglio essere in grado di rollback osservabili come se un utente annulla una richiesta che voglio essere in grado di tornare all'ultimo valore. Dalla mia ricerca questo sembra essere ottenuto da persone che creano plugin come editables

    Come faccio a usare qualcosa del genere se sto usando la mappatura? Non voglio davvero andare a un metodo in cui ho a mio avviso la mappatura manuale se mappassi ogni campo MVC viewMode su un campo di modello KO come voglio il meno javascript in linea possibile e sembra solo raddoppiare il lavoro e questo è perché mi piace quella mappatura.

  5. Sono preoccupato che per semplificare questo lavoro (usando la mappatura) perderò un sacco di potenza KO, ma d'altra parte sono preoccupato che la mappatura manuale sarà molto impegnativa e renderà le mie viste contenere troppe informazioni e potrebbe diventare in futuro più difficile da mantenere (diciamo che se rimuovo una proprietà nel modello MVC devo spostarlo anche nel viewmodel KO)

Post originale

Sto usando asp.net mvc 3 e guardo ad eliminazione diretta perché sembra piuttosto interessante, ma mi sto divertendo a capire come funziona con asp.net mvc in particolare per visualizzare i modelli.

Per ora, faccio qualcosa del genere

 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; }

}

Avrei un Vm che ha alcune proprietà di base come CourseName e avrà una semplice validazione su di esso. Il modello Vm potrebbe contenere anche altri modelli di visualizzazione, se necessario.

Quindi passerei questo Vm alla vista dove userei gli helper html per aiutarmi a mostrarlo all'utente.

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

Potrei avere alcuni cicli foreach o qualcosa per ottenere i dati dalla collezione di modelli di visualizzazione degli studenti.

Poi, quando invio il modulo, userei jquery e serialize array e lo invierò a un metodo di azione del controller che lo legherebbe al viewmodel.

Con knockout.js è tutto diverso dato che ora hai i viewmodels e da tutti gli esempi che ho visto non usano gli helper html.

Come usi queste 2 funzionalità di MVC con knockout.js?

Ho trovato questo video e in breve (ultimi pochi minuti del video @ 18:48) entra in un modo di usare viewmodels avendo fondamentalmente uno script inline che ha il viewmodel knockout.js a cui vengono assegnati i valori nel ViewModel.

È questo l'unico modo per farlo? Che ne dite nel mio esempio di avere una collezione di modellini di visi? Devo avere un ciclo foreach o qualcosa per estrarre tutti i valori e assegnarlo a knockout?

Per quanto riguarda gli helper HTML il video non dice nulla su di loro.

Queste sono le 2 aree che mi confondono, poiché non molte persone sembrano parlarne e questo mi lascia confuso da come i valori iniziali e tutto stiano arrivando alla vista quando mai l'esempio è solo un esempio di valore hard-coded.

modificare

Sto provando ciò che ha suggerito Darin Dimitrov e questo sembra funzionare (ho dovuto apportare alcune modifiche al suo codice). Non sono sicuro del motivo per cui ho dovuto usare il documento pronto ma in qualche modo tutto non era pronto senza di esso.

@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>

Ho dovuto avvolgerlo intorno a un documento jquery pronto per farlo funzionare.

Ho anche ricevuto questo avvertimento. Non sono sicuro di cosa si tratta.

Warning 1   Conditional compilation is turned off   -> @Html.Raw

Quindi ho un punto di partenza, suppongo che almeno si aggiornerà quando ho fatto un po 'di più a giocare e come funziona.

Sto provando a passare attraverso le esercitazioni interattive, ma uso invece un ViewModel.

Non sono sicuro di come affrontare ancora queste parti

function AppViewModel() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");
}

o

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
    };

Modifica 2

Sono stato in grado di capire il primo problema. Nessun indizio sul secondo problema. Eppure però. Qualcuno ha qualche idea?

 @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>

controllore

  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);
    }

Penso di aver riassunto tutte le tue domande, se ho perso qualcosa per favore fammi sapere ( Se potessi riassumere tutte le tue domande in un posto sarebbe bello =))

Nota. ko.editable la compatibilità con il plug-in ko.editable

Download il codice completo

Come si usano helper HTML con knockout.js

Questo è facile:

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

Dove:

  • value: CourseId indica che si sta vincolando la proprietà value del controllo di input con la proprietà CourseId dal modello e il modello di script

Il risultato è:

<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" />

Perché il documento era pronto per farlo funzionare (vedi prima modifica per maggiori informazioni)

Non capisco ancora perché è necessario utilizzare l'evento ready per serializzare il modello, ma sembra che sia semplicemente richiesto (non preoccuparsi però)

Come faccio a fare qualcosa di simile se sto usando la mappatura ad eliminazione diretta con i miei modelli di visualizzazione? Dato che non ho una funzione a causa della mappatura.

Se ho capito bene, devi aggiungere un nuovo metodo al modello KO, beh questo è facile da unire ai modelli

Per maggiori informazioni, nella sezione -Mapping da diverse fonti-

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);
});

Informazioni sull'avviso che stavi ricevendo

Avviso 1 La compilazione condizionale è disattivata -> @ Html.Raw

Devi usare le virgolette

Compatibilità con il plug-in ko.editable

Ho pensato che sarebbe stato più complesso, ma si scopre che l'integrazione è davvero semplice, per rendere modificabile il tuo modello basta aggiungere la seguente riga: (ricorda che in questo caso sto usando un modello misto, da server e aggiungendo l'estensione nel client e il modificabile funziona semplicemente ... è fantastico):

    ko.editable(g);
    ko.applyBindings(g);

Da qui hai solo bisogno di giocare con i tuoi binding usando le estensioni aggiunte dal plug-in, ad esempio, ho un pulsante per iniziare a modificare i miei campi come questo e in questo pulsante avvio il processo di modifica:

    this.editMode = function () {
        this.isInEditMode(!this.isInEditMode());
        this.beginEdit();
    };

Poi ho commesso e annullato i pulsanti con il seguente codice:

    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);
        }
    };

E infine, ho un campo per indicare se i campi sono in modalità di modifica o no, questo è solo per associare la proprietà enable.

this.isInEditMode = ko.observable(false);

Informazioni sulla tua domanda matrice

Potrei avere alcuni cicli foreach o qualcosa per ottenere i dati dalla collezione di modelli di visualizzazione degli studenti.

Poi, quando invio il modulo, userei jquery e serialize array e lo invierò a un metodo di azione del controller che lo legherebbe al viewmodel.

Puoi fare lo stesso con KO, nel seguente esempio creerò il seguente output:

Fondamentalmente qui, hai due elenchi, creati usando Helpers e rilegati con KO, hanno un evento dblClick che, una volta dblClick , rimuove l'elemento selezionato dall'elenco corrente e lo aggiunge all'altro elenco, quando si registra sul Controller , il il contenuto di ciascun elenco viene inviato come dati JSON e riattaccato al modello del server

Nuggets:

scripts esterni.

Codice del controller

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

Modello

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; }
}

Pagina 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

<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>

Nota: ho appena aggiunto queste righe:

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

Perché quando invio il modulo i miei campi sono disabilitati, quindi i valori non sono stati trasmessi al server, ecco perché ho aggiunto un paio di campi nascosti per fare il trucco





knockout.js