128 votes

Comment utiliser knockout.js avec ASP.NET MVC Viewmodel?

Bounty

Il a été un certain temps et j'ai encore quelques questions en suspens. J'espère que d'ici l'ajout d'une prime peut-être que ces questions obtiennent une réponse.

  1. Comment utilisez-vous html helpers avec knockout.js
  2. Pourquoi était-document prêt nécessaire pour le faire fonctionner(voir la première édition pour plus d'informations)

  3. Comment puis-je faire quelque chose comme cela si je suis en utilisant le knock-out de la cartographie avec mon point de vue, les modèles? Comme je n'ai pas de fonction en raison de la cartographie.

    fonction 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. Je veux utiliser les plugins, par exemple je veux être en mesure de restaurer des observables que si un utilisateur annule une requête que je veux être en mesure de revenir à la dernière valeur. D'après mes recherches cela semble être réalisée par des personnes pour faire des plugins comme editables

    Comment puis-je utiliser quelque chose comme ça si je suis à l'aide de la cartographie? Je ne veux vraiment pas aller à une méthode où j'ai, à mon avis, manuel de la cartographie je carte chaque MVC viewMode champ à un KO champ de modèle que je veux que le peu de javascript en ligne que possible et qui semble juste comme le double du travail et c'est pourquoi j'aime que la cartographie.

  5. Je crains que pour faire ce travail facile (à l'aide de la cartographie) je vais perdre beaucoup de KO de puissance, mais d'un autre côté, je suis inquiète de ce que le manuel de la cartographie sera juste beaucoup de travail et fait mon point de vue contenir trop d'informations et pourrait devenir, à l'avenir plus difficile à maintenir(dites si je supprimer une propriété dans le modèle MVC, j'ai pour la déplacer également dans le KO viewmodel)


Post Original

Je suis à l'aide de asp.net mvc 3 et je cherche dans knock-out comme il a l'air assez cool, mais je vais avoir du mal à comprendre comment cela fonctionne avec asp.net mvc en particulier les modèles de vue.

Pour moi, maintenant, je fais quelque chose comme ça

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

}

J'aurais une machine virtuelle qui a certaines propriétés de base comme CourseName et il aura quelques validation simple sur le dessus de cela. La Vm modèle peut contenir d'autres modèles de vue dans les si nécessaire.

Je ne puis passer cette Vm au point de Vue, je voudrais utiliser html aides pour m'aider à afficher à l'utilisateur.

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

Je pourrais avoir quelques boucles foreach ou quelque chose pour obtenir les données de la collecte des Étudiants Modèles.

Puis, quand je soumettre le formulaire, je voudrais utiliser jquery et serialize array et l'envoyer à l'action d'un contrôleur méthode qui permettrait de lier de nouveau à la viewmodel.

Avec knockout.js c'est tous différents, comme vous l'avez maintenant obtenu viewmodel pour elle et par tous les exemples que j'ai vu qu'ils n'utilisent pas html helpers.

Comment utilisez-vous ces 2 caractéristiques de la MVC avec knockout.js?

J'ai trouvé cette vidéo et il a brièvement(quelques dernières minutes de la vidéo @ 18:48) va dans un moyen d'utiliser le viewmodel par gros avoir un script en ligne qui a l'knockout.js viewmodel qui sera affectée les valeurs dans le ViewModel.

Est-ce la seule façon de le faire? Dans mon exemple, avec le fait d'avoir une collection des viewmodels-t-il? Dois-je avoir une boucle foreach ou quelque chose pour extraire toutes les valeurs et l'affecter dans knock-out?

Comme pour le html helpers la vidéo ne dit rien sur eux.

Ce sont les 2 domaines qui confond le diable hors de moi que pas beaucoup de gens semblent parler et il me laisse confus de la façon dont les valeurs initiales et tout est d'arriver à la vue quand jamais exemple est juste une valeur codée en dur exemple.


Modifier

Je suis en train d'essayer ce Darin Dimitrov a proposé et cela semble fonctionner(j'ai dû faire quelques modifications à son code). Je ne sais pas pourquoi j'ai eu à utiliser le document de prêt, mais de toute façon tout n'était pas prêt sans elle.

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

J'ai eu les enrouler autour de jquery document prêt à le faire fonctionner.

Je reçois aussi cet avertissement. Pas sûr de ce qu'il est tout au sujet.

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

J'ai donc un point de départ, je suppose, au moins, sera mise à jour quand j'ai fait un peu plus de jeu autour et comment cela fonctionne.

Je suis en train de passer à travers les tutoriels interactifs, mais l'utilisation de l'un ViewModel à la place.

Pas sûr de la façon de s'attaquer à ces pièces encore

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

ou

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


Edit 2

J'ai été en mesure de comprendre le premier problème. Aucune idée sur le deuxième problème. Pourtant, bien qu'. Quelqu'un a des idées?

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

Contrôleur

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

179voto

Jupaol Points 12825

Je pense que j'ai summired à toutes vos questions, si j'ai raté quelque chose pls laissez-moi savoir (Si vous pouviez résumer toutes vos questions en un seul endroit serait sympa =))

Remarque. La compatibilité avec l' ko.editable plug-in ajouté

Télécharger le code complet

http://sdrv.ms/NebpFQ

Comment utilisez-vous html helpers avec knockout.js

C'est facile:

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

Où:

  • value: CourseId indique que vous êtes de liaison de l' value de la propriété de l' input contrôle avec l' CourseId de la propriété à partir de votre modèle et de votre script modèle

Le résultat est:

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

Pourquoi était-document prêt nécessaire pour le faire fonctionner(voir la première édition pour plus d'informations)

Je ne comprends pas encore pourquoi vous avez besoin d'utiliser l' ready événement pour sérialiser le modèle, mais il semble que c'est tout simplement nécessaire (ne vous inquiétez Pas à ce sujet)

Comment puis-je faire quelque chose comme cela si je suis en utilisant le knock-out de la cartographie avec mon point de vue, les modèles? Comme je n'ai pas de fonction en raison de la cartographie.

Si je comprends bien, vous devez ajouter une nouvelle méthode à l'KO modèle, eh bien, c'est facile la fusion de modèles

Pour plus d'info, dans la section de Cartographie à partir de différentes sources-

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

Sur l'avertissement que vous ont été receiveing

Avertissement 1 compilation Conditionnelle est éteint -> @Html.Raw

Vous devez utiliser des guillemets

La compatibilité avec le ko.modifiable plug-in

Je pensais que ça allait être plus complexe, mais il s'avère que l'intégration est vraiment facile, afin de faire de votre modèle modifiable, il suffit d'ajouter la ligne suivante: (rappelez-vous que dans ce cas je suis à l'aide d'un modèle mixte, depuis le serveur, et l'ajout de l'extension dans le client et le modifiable fonctionne tout simplement... c'est génial):

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

De là, vous avez juste besoin de jouer avec vos fixations à l'aide des extensions ajoutées par le plug-in, par exemple, j'ai un bouton pour commencer le montage de mon champs de ce genre et de ce bouton que j'ai commencer le processus de validation:

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

Puis j'ai valider et annuler les boutons avec le code suivant:

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

Et enfin, j'ai un champ pour indiquer si les champs sont en mode édition ou pas, c'est juste pour lier la propriété activer.

this.isInEditMode = ko.observable(false);

À propos de votre tableau de question

Je pourrais avoir quelques boucles foreach ou quelque chose pour obtenir les données de la collecte des Étudiants Modèles.

Puis, quand je soumettre le formulaire, je voudrais utiliser jquery et sérialiser tableau et de l'envoyer à l'action d'un contrôleur méthode qui permettrait de lier de nouveau à la viewmodel.

Vous pouvez faire de même avec KO, dans l'exemple suivant, je vais créer la sortie suivante:

enter image description here

Fondamentalement, ici, vous avez deux listes, créé à l'aide d' Helpers et lié avec un KO, ils ont un dblClick événement lié qu'à la cuisson, retirer l'élément sélectionné de la liste actuelle et l'ajouter à la liste, lorsque vous postez à l' Controller, le contenu de chaque liste est envoyé sous la forme de données JSON et re-joint le modèle de serveur

Pépites:

Des scripts externes:

Contrôleur de code

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

Modèle

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 page

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

Les Scripts

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

Note: je viens d'ajouter ces lignes:

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

Parce que quand je soumettre le formulaire de mes champs sont désactivés, les valeurs n'ont pas été transmises au serveur, c'est pourquoi j'ai ajouté un couple de champs cachés de faire le tour

23voto

Darin Dimitrov Points 528142

Vous pourriez sérialiser vos ASP.NET MVC modèle de vue dans une variable 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>

Il y a beaucoup d'exemples dans le knock-out de la documentation que vous pourriez passer par.

2voto

Paul Tyng Points 5203

Pour obtenir le supplément de propriétés calculées après mappage de serveur, vous aurez besoin pour rendre votre viewmodel sur le côté client.

Par exemple:

var viewModel = ko.mapping.fromJS(model);

viewModel.capitalizedName = ko.computed(function() {...}, viewModel);

Donc, chaque fois que vous la carte à partir de matières JSON, vous devrez réappliquer les propriétés calculées.

En outre, la cartographie plugin offre la possibilité de mettre à jour un viewmodel, par opposition à recréer chaque fois que vous aller et venir (utiliser un paramètre supplémentaire en fromJS):

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

Et qui exécute une mise à jour incrémentielle des données sur votre modèle de seulement des propriétés qui sont mappés. Vous pouvez en lire plus à ce sujet dans la cartographie de la documentation

Vous avez mentionné dans les commentaires sur Darin répondre à la FluentJSON paquet. Je suis l'auteur, mais son cas est plus spécifique que ko.la cartographie. Je voudrais généralement à n'utiliser que si votre viewmodel sont une façon (c'est à dire. serveur -> client), et les données est affecté dans un autre format (ou pas du tout). Ou si l'option javascript de votre viewmodel doit être considérablement différents format de votre modèle de serveur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X