2014-09-04 3 views
0

Contexte: Je suis assez nouveau à MVC & Knockout.js mais j'essaie de se mettre au courant de ces technologies. J'utilise MVC 5 avec EF6 et Knockout.JS 3.2.Connexion MVC Model View avec Knockout.js associé ViewModel

J'ai une vue de détail qui tire un objet « VoteAnswer » en utilisant MVC basé sur l'ID passé dans l'URL:

Par exemple, je peux aller à l'url MyDomain/VoteAnswers/Détails/1 et il va tirer les informations de ma base de données correctement (Il tire un VoteAnswer avec l'ID de 1) et afficher dans ma vue Détails. Cependant, j'essaie de brancher mon ViewModel "VoteAnswer" de Knockout.js pour fonctionner de la même manière et j'ai des problèmes.

Voici mon Détails Vue:. (Notez le @ Html.DisplayFor (modèle => model.VoteAnswerId) etc travaux et affiche les données de ma base de données

@model AM_SPA_TestSite.Models.VoteAnswer 

@{ 
    Layout = null; 
} 

<!DOCTYPE html> 

<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>Details</title> 
    <script src="~/KnockoutViewModels/VoteAnswers.js"></script> 
</head> 
<body> 
    <div> 
     <h4>VoteAnswer</h4> 
    <hr /> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td data-bind="text: id"></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td data-bind="text: isActive"></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td data-bind="text: displayText"></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td><input type="text" data-bind="value: id" /></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td><input type="text" data-bind="value: displayText" /></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td><input type="text" data-bind="value: isActive" /></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td>@Html.DisplayFor(model => model.VoteAnswerId)</td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td>@Html.DisplayFor(model => model.DisplayText)</td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td>@Html.DisplayFor(model => model.IsActive)</td> 
     </tr> 
    </table> 
</div> 

Voici mon Knockout.Js ViewModel

// VoteAnswer ViewModel 
var VoteAnswerVM = { 

id: ko.observable(), 
displayText: ko.observable(), 
isActive: ko.observable(), 
SaveVoteAnswer: function() { 
    $.ajax({ 
     url: '/VoteAnswers/Create', 
     type: 'post', 
     dataType: 'json', 
     data: ko.toJSON(this), 
     contentType: 'application/json', 
     success: function (result) { 
     }, 
     error: function (err) { 
      if (err.responseText == "Creation Failed") 
      { window.location.href = '/VoteAnswers/Index/'; } 
      else { 
       alert("Status:" + err.responseText); 
       window.location.href = '/VoteAnswers/Index/';; 
      } 
     }, 
     complete: function() { 
      window.location.href = '/VoteAnswers/Index/'; 
     } 
    }); 
} 
}; 

//Go 
$(document).ready(function() { 
    //initialize and create new VoteAnswerVM by URL value here? 
    ko.applyBindings(VoteAnswerVM); 
}); 

Je sais ce que je suis absent initialise la ViewModel avec l'ID de 1, mais je pensais le modèle MVC a déjà les données et la knockout.js DEVRAIT correspondre à ces données sans initialiser manuellement en envoyant à nouveau une requête à la base de données. Qu'est-ce que je rate? Merci.

EDIT: Ajoutésolutionci-dessous. Je ne suis pas sûr que je sois fixé sur cette approche mais c'est ici. Mise à jour du contrôleur pour retourner uniquement une vue et ne pas interroger la base de données. (Sinon je l'aurais deux appels de base de données pour les mêmes données.

// GET: VoteAnswers/Details/5 
    public ViewResult Details(int? id) 
    { 
     return View(); 
    } 

Ajouté un contrôleur API qui fait interroger la base de données.

// GET: api/VoteAnswers/5 
    [ResponseType(typeof(VoteAnswer))] 
    public async Task<IHttpActionResult> GetVoteAnswer(int id) 
    { 
     VoteAnswer voteAnswer = await db.VoteAnswers.FindAsync(id); 
     if (voteAnswer == null) 
     { 
      return NotFound(); 
     } 

     return Ok(voteAnswer); 
    } 

À mon avis (fichier .cshtml) Je référence mon knock-out. js MODELVIEW, View est ci-dessous.

<!DOCTYPE html> 

<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>Details</title> 
    <script src="~/KnockoutViewModels/VoteAnswers.js"></script> 
</head> 
<body> 
<div> 
    <h4>VoteAnswer</h4> 
    <hr /> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td data-bind="text: VoteAnswerId"></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td data-bind="text: IsActive"></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td data-bind="text: DisplayText"></td> 
     </tr> 
    </table> 
    <table> 
     <tr> 
      <td>Id</td> 
      <td><input type="text" data-bind="value: VoteAnswerId" /></td> 
     </tr> 
     <tr> 
      <td>Display Text</td> 
      <td><input type="text" data-bind="value: DisplayText" /></td> 
     </tr> 
     <tr> 
      <td>IsActive</td> 
      <td><input type="text" data-bind="value: IsActive" /></td> 
     </tr> 
    </table> 
</div> 
<div id="error"></div> 
</body> 
</html> 

mis à jour mon script ViewModel pour accéder à la base de données basée sur l'ID URL

// VoteAnswer ViewModel 
var VoteAnswer = function() { 
var self = this; 
self.VoteAnswerId = ko.observable(); 
self.DisplayText = ko.observable(); 
self.IsActive = ko.observable(); 

self.SaveVoteAnswer = function() { 
    $.ajax({ 
     url: '/VoteAnswers/Create', 
     type: 'post', 
     dataType: 'json', 
     data: ko.toJSON(this), 
     contentType: 'application/json', 
     success: function (result) { 
     }, 
     error: function (err) { 
      if (err.responseText == "Creation Failed") 
      { window.location.href = '/VoteAnswers/Index/'; } 
      else { 
       alert("Status:" + err.responseText); 
       window.location.href = '/VoteAnswers/Index/';; 
      } 
     }, 
     complete: function() { 
      window.location.href = '/VoteAnswers/Index/'; 
     } 
    }); 
} 
self.load = function (id) { 
    if (id != 0) { 
     $.ajax({ 
      url: '/api/VoteAnswers/' + id, 
      type: 'get', 
      data: ko.toJSON(this), 
      contentType: 'application/json', 
      success: function(data) { 
       self.VoteAnswerId = ko.observable(data.voteAnswerId); 
       self.DisplayText = ko.observable(data.displayText); 
       self.IsActive = ko.observable(data.isActive); 
       ko.applyBindings(self); 
      }, 
      error: function(err) { 
       if (err.responseText == "Creation Failed") { 
        window.location.href = '/VoteAnswers/Index/'; 
       } else { 
        $("#error").text("Status:" + err.responseText); 
        //window.location.href = '/VoteAnswers/Index/';; 
       } 
      }, 
      complete: function() { 
       //window.location.href = '/VoteAnswers/Index/'; 
      } 
     }); 
    } else { 
     window.location.href = '/VoteAnswers/Index/'; 
    } 
} 
}; 
function GetURLParameter() { 
var sPageUrl = window.location.href; 
var indexOfLastSlash = sPageUrl.lastIndexOf("/"); 

if (indexOfLastSlash > 0 && sPageUrl.length - 1 != indexOfLastSlash) 
    return sPageUrl.substring(indexOfLastSlash + 1); 
else 
    return 0; 
} 
//Go 
$(document).ready(function() { 
    //initialize and create new VoteAnswerVM by URL value here? 
    var viewModel = new VoteAnswer(); 
    viewModel.load(GetURLParameter()); 
}); 
+0

Le ViewModel est-il javascript dans un fichier JS séparé, ou intégré directement dans la page du rasoir? –

+0

avez-vous essayé 'ko.applyBindings (nouveau VoteAnswerVM());' quelque chose comme ça –

+0

@Robert - oui c'est un fichier JS distinct, j'ai vérifié qu'il est en cours de chargement. J'ai également vérifié que les codes viewModel et knockout fonctionnent si je passe manuellement les paramètres. Par exemple si j'ai initialisé comme: 'ko.applyBindings (nouveau VoteAnswerVM (1, "Mon texte", vrai));' et changé la fonction à l'exception de ces propriétés. – cmartin

Répondre

0

J'espère que je vous ai bien compris, si ma réponse est erronée pour votre question, laissez-moi savoir où je me suis trompé. Première chose à réaliser est, si vous liez un KO observable à un champ de saisie, knock-out ne regardera pas la valeur initiale de ce champ d'entrée et le stocker dans l'observable. Il fera le contraire: il regardera la valeur courante dans l'observable et la stockera dans la valeur du champ d'entrée. Dans votre cas, les observables sont initialisés sans valeur, ce qui en JavaScript signifie la valeur undefined. Donc, si vous liez vos observables aux champs que vous avez remplis avec Razor/MVC viewmodel, vous écraserez immédiatement ces valeurs avec les valeurs vides stockées dans vos observables.

Il existe un moyen de remplir votre modèle Knockout avec vos données via Razor, mais il s'agit d'un code JavaScript intégré et c'est une mauvaise pratique pour un certain nombre de raisons (j'aborderai cela sur demande).Le meilleur moyen de le faire est de séparer vos vues de vos données: n'injectez pas le viewmodel MVC dans la vue, mais créez un point de terminaison séparé qui renvoie JSON et y renvoie les données (ce noeud final recevra l'ID) paramètre au lieu de la vue). Le point de terminaison JSON est appelé à partir de JavaScript et peut être utilisé pour remplir votre modèle avec les valeurs correctes. Upsides: séparation des préoccupations, possibilité d'activer la mise en cache des vues pour un frontend plus réactif, pas besoin d'utiliser la syntaxe du rasoir ou, pire encore, de le combiner avec JS en ligne. Toute votre liaison de données à l'interface utilisateur se fera par Knockout. J'ai appris cela moi-même parce que nous avons aussi commencé à utiliser le rasoir, mais à long terme, cette solution n'était pas réalisable pour un grand projet. Nous n'avons jamais regretté le passage à toujours obtenir les données à partir de points de terminaison JSON distincts.

Si vous ne savez pas comment faire cela, je peux écrire un pseudo-code pour illustrer l'idée.

+0

jamais été à travers cette ligne, mais j'aimerais voir votre pseudocode si vous pouvez nous montrer. –

+0

Merci pour la réponse Hans - Donc, fondamentalement, vous initialisez via votre propre appel JSON. Dans mon scénario où l'URL est/VoteAnswers/Details/1 ("{controller}/{action}/{id}" du fichier de configuration de route). Voulez-vous juste analyser manuellement l'URL pour saisir l'ID pour initialiser le knockout viewModel? – cmartin

+0

C'est une façon, analyser l'URL sur le frontend afin que vous sachiez quel ID à envoyer avec votre demande pour obtenir les données. Votre code semble indiquer que vous créez un SPA. Dans ce cas, l'analyse de l'URL est déjà effectuée pour vous, il est donc plus facile de détecter la partie ID. Utilisez l'ID pour effectuer une demande d'obtention à un point de terminaison JSON qui renvoie le modèle qui est maintenant votre modèle de vue MVC, converti uniquement en JSON (.NET a des convertisseurs JSON en abondance). –

0

Ceux-ci peuvent être les solutions possibles

1.) data_bind est utilisé à tort

<td><input type="text" data-bind="value: isActive" /></td> // which is wrong 

<td><input type="text" data_bind="value: isActive" /></td> //data_bind is wrongly used 

2.) Si vous encore problème existe peut essayer cette syntaxe

@Html.DisplayFor(model => model.IsActive, new { data_bind = "value:IsActive" }); 

Si vous trouvez encore quelque chose qui manque, veuillez fournir un peu de info de queue.

Questions connexes