Cet article présente mes idées de mise en œuvre récentes pour des fonctions de cascade similaires dans les cascades provinciales et municipales. Afin de séparer les responsabilités, les performances et le comportement autant que possible, cette fonction est divisée en 2 composants et une seule liste liée est utilisée pour implémenter la logique de cascade clé. La section suivante a un effet de démonstration. Bien qu'il s'agisse d'une fonction très courante, la logique d'implémentation de cet article est claire et le code est facile à comprendre. Il est séparé de la sémantique des cascades provinciales et municipales, et prend en compte la séparation des performances et du comportement. J'espère que le contenu de cet article pourra apporter une valeur de référence à votre travail. Bienvenue pour le lire et le corriger.
Fonctionnement de cascade en cascade
Cascadetype. Persister une opération de persistance en cascade (sauvegarde)
Cascadetype. Fusionner l'opération de mise à jour en cascade (Mergy)
Cascadetype. Rafraîchir le fonctionnement de rafraîchissement en cascade, interrogez uniquement les opérations
Cascadetype. Supprimer l'opération de suppression de cascade
Cascadetype. Toutes les opérations en cascade ci-dessus
Que ce soit pour retarder le chargement de la fraw-fetch, la valeur par défaut est que le premier côté se charge immédiatement, et plus le côté est le chargement de retard
Entretien des relations cartographiées
mappedby = "parentid" signifie que l'attribut Parentid dans la classe d'enfants est utilisé pour maintenir la relation. Ce nom doit être exactement le même que le nom d'attribut Parentid dans la classe des enfants.
Il convient également de noter que le type de collection dans la classe parent doit être répertorié ou défini, et ne peut pas être défini sur ArrayList, sinon une erreur sera signalée
Effet de démonstration (téléchargement de code, Remarque: Cet effet nécessite que HTTP s'exécute. De plus, les données dans l'effet sont des données simulées et ne sont pas réellement renvoyées par l'arrière-plan, donc les données excessives des provinces, des villes et des comtés que vous voyez sont les mêmes):
Remarque: Cet article utilise la mise en œuvre technique de plusieurs blogs connexes précédents. Si vous en avez besoin, vous pouvez cliquer sur le lien ci-dessous pour en savoir plus:
1) Explication détaillée de la mise en œuvre de l'héritage de JavaScript: Fournissez une classe.js pour définir la relation d'héritage entre la classe JavaScript et la classe de construction;
2) Compétences JQuery pour réaliser n'importe quel composant Support de gestion d'événements de type DOM: Fournissez un eventbase.js pour fournir n'importe quelle instance de composant avec des fonctions de gestion d'événements de type DOM;
3) Encapsulation secondaire des composants proxy de cache AJAX et AJAX de JQuery: AjaxCache: fournit ajax.js et ajaxcache.js, simplifie les appels ajax de jQuery et le proxy de cache pour les demandes du client.
Apprenons d'abord plus sur les exigences de cette fonction.
1. Analyse fonctionnelle
Cette fonction est illustrée par un composant en cascade contenant trois éléments en cascade:
1) Chaque élément en cascade peut nécessiter une option pour être utilisé comme invite d'entrée:
Dans ce cas, une option vide peut être sélectionnée dans la liste de données de chaque élément en cascade (c'est-à-dire celui invité par l'entrée):
Il peut également ne pas être nécessaire d'utiliser comme invites d'entrée:
Dans ce cas, seule l'option de données peut être sélectionnée dans la liste de données de chaque élément en cascade, et aucune option vide ne peut être sélectionnée:
2) Si la page actuelle est interrogé à partir de la base de données et que les champs correspondant au composant en cascade ont des valeurs, la valeur interrogée est repris sur le composant en cascade:
Si le champ correspondant trouvé dans la requête n'a aucune valeur, les deux situations décrites dans les exigences du point 1) sont affichées.
3) Chaque élément en cascade forme une seule relation de liste liée dans la structure de données. La liste de données de ce dernier élément en cascade est liée aux données sélectionnées par l'élément en cascade précédent.
4) Compte tenu des problèmes de performances, la liste de données de chaque élément en cascade s'affiche de manière asynchrone par AJAX.
5) Une fois l'initialisation du composant en cascade terminé, la liste du premier élément en cascade est automatiquement chargée.
6) Lorsque l'élément en cascade actuel change, effacez la liste des données de tous les éléments en cascade qui sont directement ou indirectement associés. Dans le même temps, si la valeur après l'élément en cascade précédent n'est pas vide, la liste de données de l'élément en cascade suivant qui est directement associé sera automatiquement chargé. Lors de la compensation de la liste des données des éléments en cascade, soyez prudent: si les éléments en cascade doivent afficher l'option d'invite d'entrée, l'option doit être conservée lors de la compensation.
7) Nous devons pleinement considérer les problèmes de performances et éviter le chargement en double.
8) Compte tenu de la question de la soumission de formulaire, lorsqu'un élément en cascade du composant en cascade change, la valeur sélectionnée par le composant en cascade doit être reflétée dans un champ de texte caché, afin de faciliter la soumission de la valeur du composant en cascade à l'arrière-plan via le champ de texte.
La fonction est à peu près comme ci-dessus.
2. Idées de mise en œuvre
1) Structure de données
Ce qui est différent des autres composants, c'est qu'il a certaines dépendances avec les données en arrière-plan. La structure de données que je considère est mieux mise en œuvre:
{"id": 1, "texte": "beijing", "code": 110000, "Parentid": 0}, {"id": 2, "texte": "Hebei Province", "Code": 220000, "Parentid": 0}, {"id": 3, "Text": "Henan Province", "Code": 330000, "PALERS": 0}L'ID est l'identifiant unique des données, et la relation d'association entre les données est construite par Parentid. Le texte et le code sont tous des domaines commerciaux ordinaires. Si nous suivons cette structure de données, il sera très simple d'interroger l'interface de la liste de données en cascade:
// Vérifiez la liste du premier élément Cascade / API / Cascade? Parentid = 0 // Vérifiez la liste du deuxième élément Cascade en fonction de la valeur sélectionnée par le premier élément Cascade / API / Cascade? ParentId = 1 // Vérifiez la liste de la troisième cascade élément basé sur la valeur sélectionnée par le deuxième élément de cascade / API / CASCADE? ParentID = 4
Cette structure est également facile à gérer pour l'arrière-plan. Bien qu'ils soient structurellement une structure de table en forme d'arbre, les requêtes sont toutes à la seule couche, elles sont donc faciles à mettre en œuvre.
Il peut également être vu à partir de la démonstration de requête précédente que cette structure peut nous aider à unifier l'interface et les paramètres de la requête de données en une seule, ce qui est une chose très pratique pour le développement des composants. Après avoir obtenu cette structure de données à partir de l'arrière-plan, nous analysons chaque donnée dans une option, telle que <option value = "beijing" data-param-value = "1"> beijing </opoption>. Cela peut non seulement compléter l'affichage déroulant de la liste de données, mais également collecter la valeur sélectionnée de l'élément de cascade actuel via la fonction de l'élément SELECT Form. Enfin, lorsque l'élément en cascade change, nous pouvons également obtenir l'option sélectionnée et utiliser la valeur de la valeur de données paramètre sur les données comme paramètre Parentid pour charger la liste de l'élément Cascade suivant. C'est également l'idée de la requête et de l'analyse des données de composants en cascade.
Cependant, ce qui doit être pris en considération ici est la question de la flexibilité. Dans les projets réels, les structures de données des composants en cascade peuvent être définies en fonction d'une relation d'association similaire telle que ID Parentid, mais leurs champs ne sont pas nécessairement appelés code texte Parentid ID et sont probablement d'autres domaines. En d'autres termes: lors de l'analyse des données dans une option, le champ utilisé pour le texte et la valeur de l'option, et la valeur du champ utilisé pour l'attribut de valeur de données de données-Param sont incertaines; Le nom de paramètre Parentid utilisé lors de la requête des données ne peut pas être mort. Parfois, si le personnel backend écrit d'abord l'interface de requête et utilise un autre nom, vous ne pouvez pas demander à quelqu'un de modifier son nom de paramètre, car il doit être compilé et déployé, ce qui est plus gênant que le frontal; La valeur de Parentid = 0 ne peut pas être fixe, car le Parentid de la première couche de données dans le projet réel peut être vide ou -1. Ces choses doivent être conçues comme des options. D'une part, la valeur par défaut est fournie et le paramètre externe est également réservé en fonction de la situation réelle. Par exemple, dans la mise en œuvre finale de cet article, cette option est définie comme ceci:
TextField: 'Text', // Le nom du champ dans les données renvoyées à afficher dans l'élément <option>
ValueField: 'Text', // Le nom du champ dans les données renvoyées à définir sur la valeur de l'élément <option>
Paramfield: 'id', // Lorsque vous appelez l'interface de requête de données, le nom de champ correspondant aux données à passer à l'arrière-plan
paramname: 'parentid', // Lors de l'appel de l'interface de requête de données, le nom du paramètre des données est passé après l'URL
DefaultParam: '', // Lorsque vous interrogez le premier élément de cascade, la valeur transmise à l'arrière-plan est généralement 0, '', ou -1, etc., indiquant que les données de la couche supérieure doivent être interrogées
2) Structure HTML
Selon l'article 1 de l'analyse fonctionnelle précédente, il existe deux types de structures HTML initiales de composants en cascade:
<ul id = "liceNeSelocation-View"> <li> <lelect> <option value = ""> Veuillez sélectionner une province </ option> </ select> </li> <li> <lelect> <option valeur = ""> Veuillez sélectionner une ville </ option> </lect> </li> <li> <li> <lelect> <option Value = ""> Veuillez sélectionner un district et un comté </ option> </lect> </li> </ ul>
ou
<ul id = "CompanyLocation-View"> <li> <lelect> </ select> </li> <li> <lect> </lect> </li> <li> <li> <lect> </lect> </li> </ul>
La seule différence entre ces deux structures est de savoir si l'option utilisée comme invites d'entrée est configurée. Il convient également de noter que si cette option vide est nécessaire, l'attribut de valeur doit être défini sur vide, sinon cette option vide soumettra les informations de l'invite d'option à l'arrière-plan lors de la soumission du formulaire.
La chose la plus importante à propos de ces deux structures est l'élément sélectionné, qui n'a rien à voir avec UL et Li. UL et Li sont utilisés pour l'interface utilisateur; L'élément sélectionné n'a pas de sémantique, et il n'est pas nécessaire d'identifier quelle province, qui est une ville, et qui est un district ou un comté. Fonctionnellement, un Select représente un élément en cascade. Peu importe où ces sélections sont définies. Nous avons juste besoin de dire au composant en cascade dont les éléments sélectionnés par ses éléments en cascade sont composés. La seule chose qui doit être racontée au composant est la séquence de ces éléments sélectionnés, mais cela est généralement contrôlé par l'ordre par défaut des éléments dans le HTML. Cette structure peut nous aider à séparer autant que possible les fonctions des composants du comportement.
3) Séparation des responsabilités et l'utilisation de listes liées uniques
On peut voir à partir de la partie précédente que si ce composant en cascade est divisé en fonction des responsabilités, il peut être divisé en deux composants principaux, l'un est responsable de la gestion des fonctions globales et des éléments en cascade interne (CascadeView), et l'autre est responsable de l'implémentation fonctionnelle des éléments Cascade (CascadeItem). De plus, afin d'implémenter plus facilement la logique en cascade, nous n'avons qu'à connecter tous les éléments en cascade via une liste liée. Grâce au mode de publication-subscription, cet dernier élément en cascade s'abonne au message que l'élément en cascade précédent a changé; Lorsque l'élément en cascade actuel change, un message est publié pour informer l'élément en cascade ultérieur pour traiter la logique pertinente; Grâce au rôle de la liste liée, ce message peut être passé jusqu'au dernier élément en cascade. Si vous le décrivez sur une image, ce sera à peu près comme ceci:
Tout ce que nous devons faire est de contrôler la libération et la livraison de bonnes nouvelles.
4) Soumission de formulaire
Afin de soumettre facilement la valeur du composant en cascade à l'arrière-plan, l'ensemble du composant en cascade peut être traité dans son ensemble, et un événement onchangé est fourni à l'extérieur, à travers lequel l'événement externe peut obtenir les valeurs de tous les éléments en cascade. Puisqu'il y a plusieurs cascades, lors de la publication de l'événement inchangé, cet événement ne peut être déclenché que lorsqu'une cascade change.
5) Cache ajax
Dans ce composant, nous devons considérer deux niveaux de cache Ajax. Le premier est au niveau des composants. Par exemple, j'ai changé le premier élément de cascade à Pékin. À l'heure actuelle, le deuxième élément Cascade a chargé les données de Pékin. Ensuite, j'ai changé le premier élément de cascade de Pékin à Hebei puis à Pékin. À l'heure actuelle, le deuxième élément Cascade affiche toujours la liste de données associée de Pékin. Si nous avons mis ses données en cache lorsque la liste a été chargée pour la première fois, nous n'avons pas besoin de lancer une demande AJAX cette fois; La seconde est la demande ajax. Au niveau, s'il y a plusieurs composants en cascade sur la page, je change d'abord le premier élément en cascade du premier composant en cascade à Pékin, et le navigateur initie une demande Ajax pour charger des données. Lorsque je change le premier élément en cascade du deuxième composant en cascade à Pékin, le navigateur enverra une autre demande pour charger des données. Si je cache les données renvoyées par la première demande AJAX du premier composant d'abord, lorsque le deuxième composant utilise les mêmes paramètres pour demander la même interface, il utilisera directement le cache précédent pour renvoyer le résultat, ce qui peut également réduire la demande AJAX. Le deuxième niveau du cache ajax dépend de la "Encapsulation secondaire ci-dessus de JQuery Ajax et Ajax Cache Component Cache: AjaxCache". Pour le composant, il implémente uniquement le premier niveau de cache en interne, mais il n'a pas besoin de considérer le deuxième niveau de cache, car la mise en œuvre du cache du deuxième niveau est transparente, et il ne sait pas que le composant AJAX qu'il utilise a la fonction de cache.
3. Détails de la mise en œuvre
La mise en œuvre finale comprend trois composants, CascadeView, CascadeItem et CascadePublicDefaults. Les deux premiers sont le cœur du composant, et le dernier est juste utilisé pour définir certaines options. Sa fonction est décrite en détail dans les commentaires de CascadeItem. De plus, il existe des commentaires très détaillés dans le code suivant qui expliquent le rôle de certains codes clés. En regardant le code en fonction des exigences précédentes, il devrait être relativement facile à comprendre. J'utilisais du texte pour expliquer certains détails de mise en œuvre, mais plus tard, j'ai progressivement senti que cette méthode était un peu ingrat. Premièrement, la langue au niveau des détails n'était pas facile à organiser. Parfois, je n’ai pas exprimé mon sens. Je voulais évidemment expliquer quelque chose clairement, mais cela s'est avéré encore plus confus. Au moins, je se sentirais de cette façon quand j'ai lu ce que j'ai écrit. Deuxièmement, les développeurs eux-mêmes ont la capacité de lire le code source, et la plupart des développeurs actifs sont prêts à comprendre les idées de mise en œuvre en réfléchissant au code des autres; J'ai donc utilisé l'annotation pour expliquer les détails de l'implémentation à la place :)
CascadepublicDefaults:
Définir (fonction () {return {url: '', // interface de requête de données textfield: 'text', // return data in the field nom valuefield: 'text', // return data in the field nom qui devrait être affiché dans l'élément <option>, paramfield: `` texte '', // return les données dans le nom de champ qui devrait être défini sur la valeur de l'interface <option>, paramfield: 'id', // des données à transmettre à l'arrière-plan sont paramname: `` Parentid ', //, lors de l'appel de l'interface de requête de données, le nom du paramètre des données qui passait après l'URL est par défaut:' ', // Lorsque vous interrogez le premier élément de cascade, la valeur transmise à l'arrière-plan est généralement de 0,' ', ou -1, etc. Invite d'entrée, telle que: veuillez sélectionner une province), si elle est vraie, la première option de par défaut OptionResolveajax ne sera pas effacée lors du rechargement de l'élément Cascade: fonction (res) {return res;} // parce que l'article en cascade enverra une demande asynchrone lors du chargement des données, ce rappel est utilisé pour analyser la réponse retournée par la demande asynchrone}});Cascadeview:
Définir (fonction (require, exportts, module) {var $ = require ('jQuery'); var class = require ('mod / class'); var eventbase = required ('mod / eventbase'); var publicDefaults = require ('mod / cascadePublicDefaults'); var cascadeItem = exiger ('mod / cascadeitem'); / *** la fonction de la fonction de la fonction de la fonction de la fonction de la fonction de la fonction de la fonction dans le dans dans la cascadetem); Le composant CascadeItem * / var par défaut = $ .Extend ({}, publicDefaults, {$ Elements: Undefined, // Array of Cascaded Item JQ objets, l'ordre des éléments dans les données représente l'ordre de CASCADED VALADED-SACARATOR: ',', // Le séparateur utilisé pour obtenir les valeurs de tous les éléments Cascaded. City, District, Chaoyang District Values: '', // La chaîne séparée par Value STACK-SEPARATOR représente la valeur de chaque sélection au début en cascade. this.base (); $ elements.each (function (i) {var $ el = $ (this); // instancie le composant CascadeItem et pointez la propriété prenem de chaque instance vers l'instance précédente // Définissez la première propriété prenem sur undefinedvar cascadeItem = new CasCadeItem ($ el, $. 1], valeur: $ .trim (valeurs [i])})); items.push (CascadeItem); // chaque modification de l'instance Cascade déclenchera l'événement de changement du composant CascadeView // extérieur peut gérer la logique commerciale dans ce rappel / par exemple, définir les valeurs de tous {that.trigger ('changé.CascadeView', that.getValue ());});});}); // Initialisation complète automatiquement le premier Cascading Items.Length && éléments [0] .Load ();}, getOptions: function (options) {return $ .Extend ({}, this.getDefaults (), options); () {return par défaut;}, getItemOptions: function () {var opts = {}, _options = this.options; for (var i in publicdefaults) {if (publicdefaults.hasownproperty (i) && i in _Options) {opts [i] = _Options [i];}} Les éléments en cascade, qui sont une chaîne séparée par ValueSeparator // La valeur d'un élément en cascade vide ne renverra pas GetValue: function () {var valeur = []; this.items.ForEach (fonction (item) {var val = $ .trim (item.getValue ()); val! = '' && value.push (val);}); value.join (this.options.valuesparator);}}, prolonger: eventBase}); return CascadeView;});CascadeItem:
définir (fonction (require, exports, module) {var $ = require ('jQuery'); var class = require ('mod / class'); var eventbase = require ('mod / eventbase'); var publicDefauts = reques = Nouveau ajaxcache (); / *** Il y a une partie de l'option définie dans PublicDefault, car le composant CascadeItem ne sera pas utilisé directement par le composant CascadeView en externe, donc certaines options doivent devenir publiques. PublicDefaults est transmis à CascadeItem * / var DefaultSS = $ .Extend ({}, publicDefaults, {previtem: Undefined, // pointer la valeur de l'élément Cascade précédent: '' // Valeur affichée au début}); Var CascadeItem = Class ({{instanceMembers: {init: function ($ el, options) {// Calline Innit Cassol Ceci. $ el.on ('change', function () de proxyage de l'élément sélectionné {that.trigger ('changé.CascadeItem');}); // lorsque la valeur d'un élément en cascade change, le traitement est pour effacer et recharger les données selon les besoins. lui-même, le contenu doit être effacé que.Hascontent && that.clear (); // s'il n'est pas le premier élément de cascade et que l'élément Cascade précédent ne sélectionne pas une option valide, il ne sera pas traité si (that.previtem && $ .trim (that.previtem.getValue ()) == ') return; $ .trim (this.options.value); valeur! == '' && this.one ('render.cascadeitem', function () {// Définissez la valeur initiale qui. $ el.val (value.split (',')); // notifier la cascade subséquente pour nettoyer et recharger les données que.triggerS ('changée.CaCaScadeItem'); (Options) {return $ .Extend ({}, this.getDefaults (), Options);}, getDefaults: function () {return par défaut;}, clear: function () {var $ el = this. $ el; $ el.val (''); if (this.options.keeptoption) {// conserver le premier L'option $ el.children (). Filter (': gt (0)'). retire ();} else {// effacer tous les éléments de cascade ultérieurs pour effacer et recharger les données this.trigger ('changé.CascCaDetem'); this.options, paramvalue, that = this, datakey; // datakey est le nom de clé utilisé lorsque le cache de cache // puisque les données du premier élément en cascade sont les données de niveau supérieur, une clé fixe et unique est utilisée lors de la cache: root // le nom de la clé utilisé lorsqu'il est mis en cache par d'autres éléments de cascade) {paramvale) opts.defaultParam; datakey = 'root';} else {paramValue = this.previtem.getParamValue (); datakey = paramvalue;} // vérifiez d'abord s'il y a des données chargées dans le cache de données, et s'il y a, il sera affiché directement pour éviter ajaxif (datakey dans ce. else {var params = {}; params [opts.paramname] = paramvalue; ajax.get (opts.url, params) .done (fonction (res) {// résolveajax Ce rappel est utilisé pour analyser les données renvoyées par ajax à l'external // il a besoin de retourner une données de données var data = opts.ResolVeajax (res); {that.cache [datakey] = data; that.render (data);}});}}, reender: function (data) {var html = [], opts = this.options; data.ForEach (fonction (item) {html.push (['<option value = "', item [opts.valufield]," "Data-Param-value =", ". Valeur correspondante de Paramfield sur la propriété Data-Param-Value de l'élément d'option [Opts.Paramfield], '">', élément [opts.textfield], '</opoption>']. JOIN (''));}); // ajouter dynamiquement sous la forme d'ajout pour éviter d'affecter la première option // à la fin, définir la valeur pour vider ceci. $. this.hascontent = true; // signifie qu'il existe un contenu.trigger ('render.cascadeItem');}, getValue: function () {return this. $ el.val ();}, getParamValue: function () {return this. $ el.find ('option: selected'). CascadeItem;});4. Instructions de démonstration
Démontrer la structure du code:
Ce qui est encadré, c'est la partie pertinente de la démonstration. html / regist.html est la page qui démontre l'effet, et js / app / regist.js est l'entrée de l'effet de démonstration JS:
Define (fonction (require, exports, module) {var $ = require ('jQuery'); var cascadeView = require ('mod / cascadeView'); fonction publicSetCaSCaView (fieldName, opts) {this.cascadeView ' '../API/Cascade.json', onchanged: this.onchanged, Values: Opts.values, keepFirstOption: this.keepFirstoption, resolveajax: function (res) {if.code == 200) {return res.data; $ ('input [name = "liceNeSelocation"]'), keepFirstoption: true, setCascadeView: publicsetCascadeView, onchanged: function (e, value) {location_views.licensEllocation. $ input.val (valeur); PublicSetCascadeView, onchanged: fonction (e, valeur) {location_views.companylocation. $ input.val (valeur);}}}; {valeurs: location_views.companyLocation. $ input.val ()});});Faites attention à la fonction de la variable location_views dans le code ci-dessus, car il existe plusieurs composants en cascade sur la page. Cette variable est réellement gérée de la même manière à travers le modèle de politique. Si vous ne faites pas cela, il est facile de générer du code en double; Ce formulaire est également plus propice à la séparation et à l'encapsulation d'une logique métier dans le fichier d'entrée, comme l'endroit où la logique métier est traitée.
5. Autres
C'est probablement le dernier blog écrit par la société maintenant. Vous devez aller travailler dans une nouvelle unité en deux jours. Je ne sais pas si vous avez autant de temps libre pour enregistrer vos idées de travail habituelles, mais au moins, vous avez développé l'habitude d'écrire des blogs, et vous vous entraînerez du temps si vous n'avez pas le temps à l'avenir. L'objectif de cette année est principalement d'élargir les connaissances et d'améliorer la qualité du code. Les blogs suivants seront plus dans la catégorie du développement des composants. J'espère que vous pourrez continuer à faire attention au site Web de Wulin.com à l'avenir!