J'apprends toujours la bonne utilisation de Knockout et je me suis rapidement éloigné de toujours taper ko.observable
Lors de la configuration de mon viewmodel et au lieu de simplement définir un objet littéral et le passer à travers le plugin de cartographie avec quelque chose comme
var viewModel = ko.mapping.fromJS(data);
ou à tout le moins, quelque chose dans le sens de bourrer toutes mes données dans un attribut sur le viewModel comme ça
var viewModel = {
... events etc ... ,
"data": ko.mapping.fromJS(data)
}
Pour être honnête, la principale raison pour laquelle j'ai fait cela est d'éviter de taper ko.observable
Et ko.observableArray
De manière répétitive. J'essaie simplement de savoir si c'est une bonne approche et s'il y a des inconvénients à supprimer la déclaration var x = ko.observable()
spécifique ensemble. De plus, je fais tout cela à la charge, pas en réponse à un appel ajax, etc., ce qui d'après ce que je peux dire, a été conçu pour le plugin de mappage.
Dans votre travail avec knockout, déclarez-vous toujours les observables manuellement, un par un, ou avez-vous opté pour la méthode mapping.fromJS que j'utilise? Y a-t-il des inconvénients spécifiques à utiliser le plug-in de cartographie si souvent comme ça?
Éditer:
Dans cet article , Steve configure son viewModel en faisant
var initialData = [ { ... } , { ... } ]; // json from the serializer
var viewModel = {
gifts : ko.observableArray(initialData)
};
Normalement, j'utiliserais simplement ko.mapping.fromJS
Pour cette situation également, en particulier pour vous assurer que les objets dans le tableau sont également transformés en observables. En regardant ce qu'il a fait, mon approche semble être exagérée et ajoute un peu de surcharge inutile.
Ma suggestion serait la même qu'une autre question à laquelle je viens de répondre à https://stackoverflow.com/questions/7499133/mapping-deply-hierarchical-objects-to-custom-classes-using-knockout-mapping-plug .
Votre raisonnement pour utiliser le plug-in de mappage est raisonnable et celui que j'utilise. Pourquoi taper plus de code que nécessaire?
Dans mon expérience avec KO (tous les 4 mois), j'ai constaté que moins je fais manuellement et laisse les routines KO faire leur travail, mieux mes applications semblent fonctionner. Ma suggestion est d'essayer d'abord l'approche la plus simple. Si cela ne répond pas à vos besoins, regardez comment l'approche simple fait sa "chose" et déterminez ce qui doit changer pour répondre à vos besoins.
Après avoir utilisé Knockout pendant un peu plus longtemps, j'ai remarqué que le plug-in de mappage a des options supplémentaires qui vous donnent un contrôle beaucoup plus fin sur le processus de mappage.
Il existe plusieurs façons d'accomplir cela, et je vais en passer en revue certaines, mais le résultat final est que vous vous retrouvez avec un résultat plus léger du plugin de cartographie car tout n'est pas observable.
Fondamentalement, vous laissez tout ce que vous pensez ne pas changer, en tant que propriété normale et ne faites que des éléments observables à partir des éléments spécifiques que vous souhaitez observer.
mapping
omettre certaines propriétésVous pouvez faire en sorte que le plug-in de mappage omet complètement les propriétés du résultat final en spécifiant des choses comme ignore
ou include
. Les deux accomplissent la même chose, juste de manière opposée.
Remarque: les exemples proviennent de la documentation du plugin de cartographie knockout.js , commentaires ajoutés par moi
include
L'extrait de code suivant supprimera toutes les propriétés de l'objet source autres que celles transmises via l'argument include
.
// specify the specific properties to include as observables in the end result
var mapping = {
// only include these two properties
'include': ["propertyToInclude", "alsoIncludeThis"]
}
// viewModel will now only contain the two properties listed above,
// and they will be observable
var viewModel = ko.mapping.fromJS(data, mapping);
ignore
Si vous souhaitez uniquement omettre certaines propriétés de l'objet source, utilisez l'argument ignore
comme indiqué ci-dessous. Il rendra observables toutes les propriétés de l'objet source à l'exception des propriétés spécifiées.
// specify the specific properties to omit from the result,
// all others will be made observable
var mapping = {
// only ignore these two properties
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
// viewModel will now omit the two properties listed above,
// everything else will be included and they will be an observable
var viewModel = ko.mapping.fromJS(data, mapping);
Si vous devez inclure des propriétés mais que vous ne pensez pas qu'elles devront être rendues observables (pour une raison quelconque), le plug-in de mappage a quelque chose qui peut vous aider.
copy
Si vous souhaitez que le plug-in de mappage copie simplement les propriétés simples et ne les rende pas observables, utilisez cet argument, comme indiqué ci-dessous.
// tell the mapping plugin to handle all other properties normally,
// but to simply copy this property instead of making it observable
var mapping = {
'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);
Si vous voulez avoir un contrôle à 100% sur ce qui est créé dans le processus de mappage, y compris la possibilité de mettre des fermetures et des abonnements dans vos objets, alors vous voulez utiliser l'option "créer".
Voici un exemple où je mappais des données d'un appel ajax à un objet avec une propriété results
. Je ne voulais rien d'observable et je voulais juste une propriété générée simple qui serait faite des autres propriétés simples sur l'objet. Peut-être pas l'exemple le plus convaincant mais il démontre la fonctionnalité.
var searchMappingConfig = {
// specific configuration for mapping the results property
"results": {
// specific function to use to create the items in the results array
"create": function (options) {
// return a new function so we can have the proper scope/value for "this", below
return new function () {
// instead of mapping like we normally would: ko.mapping.fromJS(options.data, {}, this);
// map via extend, this will just copy the properties from the returned json element to "this"
// we'll do this for a more light weight vm since every last property will just be a plain old property instead of observable
$.extend(this, options.data);
// all this to add a vehicle title to each item
this.vehicleTitle = this.Year + "<br />" + this.Make + " " + this.Model;
}, this);
};
}
}
}
Une autre situation est si vous voulez des fermetures et des abonnements dans votre résultat. Cet exemple est trop long pour être inclus dans son intégralité mais pour une hiérarchie de marque/modèle de véhicule. Je voulais que tous les modèles (enfants) pour une marque donnée (parent) ne soient pas activés si le modèle n'était pas activé et je voulais que cela se fasse avec un abonnement.
// here we are specifying the way that items in the make array are created,
// since makes has a child array (Models), we will specify the way that
// items are created for that as well
var makesModelsMappingConfig = {
// function that has the configuration for creating makes
"create": function (options) {
// return a new function so we can have the proper
// scope/value for "this", below
return new function () {
// Note: we have a parent / child relationship here, makes have models. In the
// UI we are selecting makes and then using that to allow the user to select
// models. Because of this, there is going to be some special logic in here
// so that all the child models under a given make, will automatically
// unselect if the user unselects the parent make.
// make the selected property a private variable so it can be closure'd over
var makeIsSelected = ko.protectedComputed(false);
// expose our property so we can bind in the UI
this.isSelected = makeIsSelected;
// ... misc other properties and events ...
// now that we've described/configured how to create the makes,
// describe/configure how to create the models under the makes
ko.mapping.fromJS(options.data, {
// specific configuration for the "Models" property
"Models": {
// function that has the configuration for creating items
// under the Models property
"create": function (model) {
// we'll create the isSelected as a local variable so
// that we can flip it in the subscription below,
// otherwise we wouldnt have access to flip it
var isSelected = ko.protectedComputed(false);
// subscribe to the parents "IsSelected" property so
// the models can select/unselect themselves
parentIsSelected.current.subscribe(function (value) {
// set the protected computed to the same
// value as its parent, note that this
// is just protected, not the actual value
isSelected(value);
});
// this object literal is what makes up each item
// in the Models observable array
return {
// here we're returning our local variable so
// we can easily modify it in our subscription
"isSelected": isSelected,
// ... misc properties to expose
// under the item in the Model array ...
};
}
}
}, this);
};
}
};
Dans l'ensemble, ce que j'ai trouvé, c'est que vous avez rarement besoin de 100% d'un objet que vous transmettez au plug-in et que vous en avez rarement besoin pour être observable. Découvrez les options de configuration du mappage et créez toutes sortes d'objets complexes et simples. L'idée est de n'obtenir que tout ce dont vous avez besoin, ni plus ni moins.
Allen, ma récente expérience d'apprentissage avec Knockout.js a été similaire à la vôtre. Nous travaillons avec un graphe d'objets hiérarchique profond du serveur et j'ai défini des fonctions de modèle de vue instanciables explicites qui en préservent la structure de base.
J'ai commencé par définir chaque propriété explicitement comme observable sur le modèle de vue pertinent, mais cela est rapidement devenu incontrôlable. De plus, une des principales raisons de passer à l'utilisation du plug-in de mappage est que nous devons faire des publications Ajax fréquentes du graphique sur le serveur où il est fusionné avec la version persistante, puis validé sur le serveur de telle sorte que de nombreuses propriétés peuvent le changement et les collections doivent être modifiés, et une nouvelle instance est retournée en tant que résultat Ajax où elle doit être fusionnée avec la représentation du client. Cela est devenu très difficile, et le plugin de cartographie a beaucoup aidé en permettant la spécification d'identifiants pour résoudre les ajouts/suppressions/mises à jour et pour remapper un graphique mis à jour sur l'original.
Il a également aidé à la création du graphique d'origine grâce à l'utilisation de l'option "créer" pour les modèles de sous-vues. Dans chaque constructeur de modèle de vue, je reçois une référence au modèle de vue parent ainsi que les données avec lesquelles construire le modèle de vue enfant, puis je crée d'autres options de mappage pour créer des petits-enfants à partir des données enfant transmises.
Le seul (léger) inconvénient que j'ai récemment trouvé, comme détaillé dans cette question , est que lorsque vous faites ko.mapping.toJSON, il ne se connecte à aucun remplacement toJSON que vous avez pu définir sur les prototypes de votre afficher les modèles afin d'exclure des propriétés de la sérialisation. J'ai pu contourner cela en spécifiant les options d'ignorer dans le démappage, comme recommandé par Ryan Niemeyer dans ce post.
Donc en résumé, je vais certainement rester avec le plugin de cartographie. Règles Knockout.js.
Un module complémentaire plus simple mais complet pourrait être knockout-data-projections
Actuellement, il ne gère pas les mappages js vers viewmodel, mais il gère assez bien le mappage de modèle vers JS.