Este artículo presenta mis ideas de implementación recientes para funciones en cascada similares en cascadas provinciales y municipales. Para separar las responsabilidades, el rendimiento y el comportamiento tanto como sea posible, esta función se divide en 2 componentes y se utiliza una sola lista vinculada para implementar la lógica de Cascade clave. La siguiente sección tiene un efecto de demostración. Aunque esta es una función muy común, la lógica de implementación de este artículo es clara y el código es fácil de entender. Está separado de la semántica de las cascadas provinciales y municipales, y tiene en cuenta la separación del rendimiento y el comportamiento. Espero que el contenido de este artículo pueda aportar algún valor de referencia a su trabajo. Bienvenido a leerlo y corregirlo.
Operación en cascada en cascada
Cascadetype. Persistir en cascada Persistencia (Guardar) Operación
Cascadetype. Operación de actualización de Cascade de fusiones (poder)
Cascadetype. Actualice la operación de actualización en cascada, solo consulta y obtenga operaciones
Cascadetype. Retire la operación de eliminación de cascade
Cascadetype. Todas las operaciones en cascada
Si se debe retrasar la carga de la búsqueda de rastreo, el valor predeterminado es que el primer lado se carga inmediatamente y cuanto más se está retrasando la carga
Mantenimiento de la relación mapeada
MAPPEDBY = "ParentId" significa que el atributo ParentId en la clase de los niños se usa para mantener la relación. Este nombre debe ser exactamente el mismo que el nombre del atributo ParentId en la clase de niños.
Además, debe tenerse en cuenta que el tipo de colección en la clase principal debe estar en lista o establecido, y no se puede establecer en ArrayList, de lo contrario se informará un error.
Efecto de demostración (descarga del código, Nota: Este efecto requiere que HTTP se ejecute. Además, los datos en el efecto son datos simulados y en realidad no son devueltos por los antecedentes, por lo que los datos desplegables de provincias, ciudades y condados que ve son los mismos):
Nota: Este artículo utiliza la implementación técnica de los varios blogs relacionados anteriores. Si lo necesita, puede hacer clic en el siguiente enlace para conocerlo:
1) Explicación detallada de la implementación de herencia de JavaScript: proporcionar una clase.js para definir la relación de herencia entre la clase JavaScript y la clase de construcción;
2) habilidades de jQuery para hacer que cualquier componente sea compatible con la gestión de eventos similares a DOM: proporcione un eventBase.js para proporcionar cualquier instancia de componente con funciones de gestión de eventos similares a DOM;
3) Encapsulación secundaria de los componentes proxy de jQuery AJAX y Ajax Cache: AjaxCache: proporciona Ajax.js y Ajaxcache.js, simplifica las llamadas de Ajax de jQuery y cache proxy para las solicitudes del cliente.
Primero aprendamos más sobre los requisitos de esta función.
1. Análisis funcional
Esta función se ilustra mediante un componente en cascada que contiene tres elementos en cascada:
1) Cada elemento en cascada puede requerir que se use una opción como un mensaje de entrada:
En este caso, se puede seleccionar una opción vacía en la lista de datos de cada elemento en cascada (es decir, la solicitada por la entrada):
Es posible que no sea necesario usar como indicaciones de entrada:
En este caso, solo se puede seleccionar la opción de datos en la lista de datos de cada elemento en cascada, y no se puede seleccionar ninguna opción vacía:
2) Si la página actual se consulta desde la base de datos y los campos correspondientes al componente en cascada tienen valores, entonces el valor consultado se hace eco en el componente en cascada:
Si el campo correspondiente que se encuentra en la consulta no tiene valor, se muestran las dos situaciones descritas en los requisitos del punto 1).
3) Cada elemento en cascada forma una relación de lista vinculada única en la estructura de datos. La lista de datos del último elemento en cascada está relacionada con los datos seleccionados por el elemento en cascada anterior.
4) Teniendo en cuenta los problemas de rendimiento, la lista de datos de cada elemento en cascada se muestra asincrónicamente por AJAX.
5) Después de completar la inicialización del componente en cascada, la lista del primer elemento en cascada se carga automáticamente.
6) Cuando cambia el elemento en cascada actual, borre la lista de datos de todos los elementos en cascada que están asociados directa o indirectamente. Al mismo tiempo, si el valor después del elemento en cascada anterior no está vacía, la lista de datos del siguiente elemento en cascada que está directamente asociado con él se cargará automáticamente. Al borrar la lista de datos de los elementos en cascada, tenga cuidado: si los elementos en cascada deben mostrar la opción de solicitación de entrada, la opción debe retenirse al borrar.
7) Debemos considerar completamente los problemas de rendimiento y evitar la carga duplicada.
8) Teniendo en cuenta la emisión de formulario, cuando cualquier elemento en cascada del componente en cascada cambia, el valor seleccionado por el componente en cascada debe reflejarse en un campo de texto oculto, para facilitar la presentación del valor del componente en cascada al fondo a través del campo de texto.
La función es más o menos como arriba.
2. Ideas de implementación
1) Estructura de datos
Lo que es diferente de otros componentes es que tiene algunas dependencias con los datos en segundo plano. La estructura de datos que considero se implementa mejor:
{"id": 1, "Texto": "Beijing", "Código": 110000, "ParentId": 0}, {"id": 2, "Texto": "Provincia Hebei", "Código": 220000, "ParentId": 0}, {"Id": 3, "Texto": "Provincia Henan", "Código": 330000, "ID es el identificador único de los datos, y la relación de asociación entre los datos se construye a través de ParentId. El texto y el código son todos los campos comerciales ordinarios. Si seguimos esta estructura de datos, será muy simple consultar la interfaz de la lista de datos en cascada:
// verifique la lista del primer elemento de cascada/API/Cascade? Parentid = 0 // verifique la lista del segundo elemento cascade basado en el valor seleccionado por el primer elemento de cascada/API/cascade? ParentId = 1 // verifique la lista del tercer elemento cascade basado en el valor seleccionado por el segundo ítem/API/cascade de Cascade Cascade = 4/4
Esta estructura también es fácil de manejar para el fondo. Aunque son estructuralmente una estructura de mesa en forma de árbol, las consultas son todas de una sola capa, por lo que son fáciles de implementar.
También se puede ver en la demostración de consulta anterior que esta estructura puede ayudarnos a unificar la interfaz y los parámetros de la consulta de datos en uno, lo cual es algo muy conveniente para el desarrollo de componentes. Después de obtener esta estructura de datos desde el fondo, analizamos cada datos en una opción, como <opción valor = "beijing" data-param-value = "1"> beijing </ppection>. Esto no solo puede completar la visualización desplegable de la lista de datos, sino que también recopila el valor seleccionado del elemento Cascade actual a través de la función del elemento de formulario Seleccionar. Finalmente, cuando cambia el elemento Cascade, también podemos obtener la opción seleccionada y usar el valor del valor de param de datos almacenado en él como el parámetro ParentId para cargar la lista del siguiente elemento cascade. Esta es también la idea de la consulta de datos de componentes en cascada y el análisis.
Sin embargo, lo que debe considerarse aquí es el problema de la flexibilidad. En proyectos reales, las estructuras de datos de los componentes en cascada pueden definirse de acuerdo con una relación de asociación similar como ID ParentId, pero sus campos no necesariamente se denominan código de texto ID ParentId, y es probable que sean otros campos. En otras palabras: al analizar los datos en una opción, el campo utilizado para el texto y el valor de la opción, y el valor del campo utilizado para el atributo de valor de datos de datos es incierto; El nombre de parámetro ParentId usado al consultar datos no puede estar muerto. A veces, si el personal de backend escribe primero la interfaz de consulta y usa otro nombre, no puede pedirle a alguien que cambie su nombre de parámetro, porque debe ser compilado e implementado, lo cual es más problemático que la parte delantera; El valor de ParentId = 0 no se puede solucionar, porque el ParentId de la primera capa de datos en el proyecto real puede estar vacío o -1. Estas cosas deben ser diseñadas como opciones. Por un lado, se proporciona el valor predeterminado y la configuración externa también se reserva de acuerdo con la situación real. Por ejemplo, en la implementación final de este artículo, esta opción se define así:
TextField: 'Text', // El nombre de campo en los datos devueltos se mostrará en el elemento <opción>
vallefield: 'text', // El nombre de campo en los datos devueltos se establecerá en el valor del elemento <opción>
paramfield: 'id', // Al llamar a la interfaz de consulta de datos, el nombre del campo correspondiente a los datos que se pasarán a un fondo
ParamName: 'ParentId', // Al llamar a la interfaz de consulta de datos, el nombre del parámetro de los datos se pasa después de la URL
DefaultParam: '', // Al consultar el primer elemento en cascada, el valor pasado al fondo es generalmente 0, '', o -1, etc., lo que indica que los datos en la capa superior se deben consultar
2) Estructura HTML
Según el artículo 1 del análisis funcional anterior, hay dos tipos de estructuras HTML iniciales de componentes en cascada:
<ul id = "LicensElocation-view"> <li> <ecteling> <opción valor = ""> Seleccione una provincia </option> </select> </li> <li> <select> <opción valor = ""> Seleccione una ciudad </pact> </li> </li> <li> <elect> <option Value = "> Seleccione un distrito y condado </option> </select> <li> </ul>
o
<ul id = "CompanyLocation-View"> <li> <elect> </select> </li> <li> <select> </select> </li> <li> <li> <select> </select> </li> </ul>
La única diferencia entre estas dos estructuras es si la opción utilizada como indicaciones de entrada está configurada. También se debe tener en cuenta que si se necesita esta opción vacía, el atributo de valor debe establecerse en vacío; de lo contrario, esta opción vacía enviará la información de solicitud de opción al fondo al enviar el formulario.
Lo más importante de estas dos estructuras es el elemento selecto, que no tiene nada que ver con UL y Li. UL y Li se usan para la interfaz de usuario; El elemento selecto no tiene semántica, y no es necesario identificar qué provincia, que es una ciudad, y que es un distrito o condado. Funcionalmente hablando, una selección representa un elemento en cascada. No importa dónde se definan estas selecciones. Solo necesitamos decirle al componente en cascada de qué elementos seleccionados están compuestos sus elementos en cascada. Lo único que debe decirle al componente es la secuencia de estos elementos seleccionados, pero esto generalmente está controlado por el orden predeterminado de elementos en el HTML. Esta estructura puede ayudarnos a separar las funciones de los componentes del comportamiento tanto como sea posible.
3) Separación de responsabilidades y el uso de listas vinculadas únicas
Se puede ver en la parte anterior que si este componente Cascade se divide de acuerdo con las responsabilidades, se puede dividir en dos componentes centrales, uno es responsable de la gestión de las funciones generales y los elementos de cascade internos (Cascadeview), y el otro es responsable de la implementación funcional de los elementos Cascade (CascadeItem). Además, para implementar más convenientemente la lógica en cascada, solo necesitamos conectar todos los elementos en cascada a través de una lista vinculada. A través del modo Publish-Susscribe, el último elemento en cascada se suscribe al mensaje de que el elemento en cascada anterior ha cambiado; Cuando cambia el elemento en cascada actual, se publica un mensaje para notificar al elemento en cascada posterior para procesar la lógica relevante; A través del papel de la lista vinculada, este mensaje puede aprobarse hasta el último elemento en cascada. Si lo describe en una imagen, será más o menos así:
Todo lo que necesitamos hacer es controlar la liberación y la entrega de buenas noticias.
4) Presentación del formulario
Para enviar convenientemente el valor del componente en cascada al fondo, todo el componente en cascada puede tratarse en su conjunto, y se proporciona un evento en el exterior, a través del cual el evento externo puede obtener los valores de todos los elementos en cascada. Dado que hay múltiples cascadas, al publicar el evento OnChanged, este evento solo se puede activar cuando cualquier cascada cambia.
5) AJAX CACHE
En este componente, debemos considerar dos niveles de caché de Ajax. El primero está en el nivel de componente. Por ejemplo, cambié el primer elemento de cascada a Beijing. En este momento, el segundo elemento en cascada cargó los datos de Beijing. Luego cambié el primer elemento cascade de Beijing a Hebei y luego a Beijing. En este momento, el segundo elemento Cascade todavía muestra la lista de datos asociada de Beijing. Si almacenamos en caché sus datos cuando la lista se cargó por primera vez, entonces no necesitamos iniciar una solicitud AJAX esta vez; El segundo es la solicitud AJAX. En el nivel, si hay múltiples componentes en cascada en la página, primero cambio el primer elemento en cascada del primer componente en cascada a Beijing, y el navegador inicia una solicitud AJAX para cargar datos. Cuando cambio el primer elemento en cascada del segundo componente en cascada a Beijing, el navegador enviará otra solicitud para cargar datos. Si el almacenamiento en caché de los datos devueltos por la primera solicitud AJAX del primer componente primero, cuando el segundo componente usa los mismos parámetros para solicitar la misma interfaz, usará directamente el caché anterior para devolver el resultado, lo que también puede reducir la solicitud AJAX. El segundo nivel de caché de Ajax depende de la "encapsulación secundaria de jQuery Ajax y el componente proxy de caché Ajax: AjaxCache". Para el componente, solo implementa el primer nivel de caché internamente, pero no necesita considerar el segundo nivel de caché, porque la implementación del caché del segundo nivel es transparente para él, y no sabe que el componente AJAX que usa tiene la función de caché.
3. Detalles de implementación
La implementación final incluye tres componentes, Cascadeview, CascadeItem y CascadepublicDefaults. Los dos primeros son el núcleo del componente, y el último solo se usa para definir algunas opciones. Su función se describe en detalle en los comentarios de CascadeItem. Además, hay comentarios muy detallados en el siguiente código que explican el papel de algunos códigos clave. Mirando el código basado en los requisitos anteriores, debe ser relativamente fácil de entender. Solía usar texto para explicar algunos detalles de implementación, pero luego gradualmente sentí que este método era un poco ingrato. Primero, el idioma a nivel de detalles no era fácil de organizar. A veces no expresé mi significado. Obviamente quería explicar algo claramente, pero resultó estar aún más confundido. Al menos me sentiría así cuando leí lo que escribí. En segundo lugar, los propios desarrolladores tienen la capacidad de leer el código fuente, y la mayoría de los desarrolladores activos están dispuestos a comprender las ideas de implementación al pensar en el código de otras personas; Así que utilicé la anotación para explicar los detalles de implementación en su lugar :)
Cascadepublicdefaults:
Define (function () {return {url: '', // interfaz de consulta de datos textield: 'text', // return data en el nombre de campo valuefield: 'text', // return data en el nombre de campo que debe ser mostrado en el elemento <pection>, paramfield: 'text', // return data en el nombre de campo que debe estar configurado en el valor de <option> elemento, paramfield: 'id', // Clamar el nombre del campo, el nombre de la interfaz de la actualización, el nombre de la interfaz de la correspondencia, el nombre de la opción. de los datos que se pasarán al fondo es paramName: 'ParentId', // Al llamar a la interfaz de consulta de datos, el nombre del parámetro de los datos que pasa después de la URL es defaultParam: '', // Al consultar el primer elemento de Cascade, el valor pasado a los antecedentes es generalmente 0, '', o -1, etc., etc., indicando que desea que se consulte los datos de nivel superior manteniendo la opinión de nivel superior: verdadero, verdadero, //// Solicitud, tales como: Seleccione una provincia), si es verdadero, la primera opción predeterminada no se borrará al recargar el elemento cascade: function (res) {return res;} // porque el elemento cascade enviará una solicitud asíncrona al cargar datos, esta devolución de llamada se usa para analizar la respuesta devuelta por la solicitud asíncrona}});Cascadeview:
Define (función (requerir, exportaciones, módulo) {var $ = require ('jQuery'); var class = require ('mod/class'); var eventbase = require ('mod/eventbase'); var publicDefaults = require ('mod/cascadePublicDefaults'); var cascadeItem = require ('mod/mod/cascadeItem');/*** La función de la función de la función de la función de la función de la función de la función de la función de los comentarios de la función de CascadeItem. Componente de cascadeItem*/var defaults = $ .extend ({}, publicDefaults, {$ Elements: Undefined, // matriz de objetos de elemento en cascada, el orden de los elementos en los datos representa el orden de Cascaded ValueseParator: ',', // El separador utilizado para obtener los valores de todos los elementos cascados. Ciudad, Distrito, Chaoyang Valores de distrito: '', // La cadena separada por ValuAyEparator representa el valor de cada selección al principio OnChanged: $ .Noop // Este evento se activará cuando el valor de cualquier elemento CASCADED cambia}); this.base (); $ elements.each (function (i) {var $ el = $ (this); // Instancia el componente CascadeItem y apunte la propiedad previsual de cada instancia a la instancia anterior // establece la primera propiedad previsual en UndefinedVar CascadeItem = new CascadeItem ($ El, $. $ .trim (valores [i])})); items.push (cascadeItem); // Cada instancia de cascade cambia activar el evento de cambio del componente CascadeView // Las externas pueden manejar la lógica comercial en esta devolución de llamada/por ejemplo, establecer los valores de todos los elementos de Cascade a un campo oculto para el campo de formato CascadeItem.on ('cambiado. ClascadeItem', Función () ()) {that.trigger ('cambia.cascadeview', that.getValue ());});});}); // La inicialización completa la carga automáticamente cargando automáticamente los primeros items.length && elementos [0] .Load ();}, getOptions: function (opciones) {return $ .extend ({}, this.getDefaults (), opciones); () {return defaults;}, getItemOntions: function () {var opts = {}, _options = this.options; for (var i en publicDefaults) {if (publicDefaults.hasownProperty (i) && i in _Options) {Opts [i] = _Options [i];}} Los elementos en cascada, que son una cadena separada por ValueseParator // el valor de un elemento en cascada vacío no devolverá getValue: function () {var value = []; this.items.forach (function (item) {var val = $ .trim (item.getValue ()); val! = '' & & value.push (val);}); regreso value.Join (this.options.ValueSeParator);}}, extender: eventBase}); return cascadeview;});CascadeItem:
Define (función (requerir, exportaciones, módulo) {var $ = require ('jQuery'); var class = require ('mod/class'); var eventbase = require ('mod/eventbase'); var publicDefaults = require ('mod/cascadePublicDefaults'); var ajax = require ('mod/aJaxCache');//esto es un componente de CAJAX = newAxax); AJAXCACHE ();/*** Hay una parte de la opción definida en PublicDefaults, porque el componente CascadeItem no se utilizará directamente por externo* El componente CascadeView se usa externamente, por lo que algunas opciones deben volverse públicas. PublicDefaults se pasa a cascadeItem*/var defaults = $ .extend ({}, publicDefaults, {previsitem: undefined, // apunte al valor de elemento de cascada anterior: '' // valor mostrado al principio}); var cascadeitem = class ({InstancEmembers: {init: function ($ el, opciones) {// Clamando el método inicial this.base; function () de proxy el elemento seleccionado {that.trigger ('cambia.cascadeItem');}); // Cuando cambia el valor de un elemento cascade, el procesamiento es borrar y volver a cargar los datos según sea necesario. que.hascontent && that.clear (); // Si no es el primer elemento en cascada y el elemento cascade anterior no selecciona una opción válida, no se procesará si (que.previtem && $ .trim (that.previtem.getvalue () == '' ') return; '' && this.one ('render.cascadeItem', function () {// Establezca el valor inicial que. $ el.val (value.split (',')); // notifica la posterior cascade para limpiar y recargar los datos que.rigger ('cambiando.cascadeitem');});}, getoption this.getDefaults (), options);}, getDefaults: function () {return defaults;}, clear: function () {var $ el = this. $ el; $ el.val (''); if (this.options.keepfirStOption) {// Mantenga la primera opción $ el.children (). $ el.html ('');} // notifica a los elementos de cascada posteriores para borrar y volver a cargar los datos this.trigger ('cambiando.cascadeItem'); Dado que los datos del primer elemento en cascada son los datos de nivel superior, se usa una clave fija y única cuando se almacena en caché: root // El nombre de clave utilizado cuando se almacena en caché por otros elementos en cascada está relacionado con la opción seleccionada por la selección anterior if (! This.previtem) {paramvalue = opts.defaultparam; datakey = 'root';} else {paramvalue = = = this.previtem.getParamValue (); datakey = paramValue;} // Primero verifique si hay datos cargados en la memoria caché de datos, y si existe, se mostrará directamente para evitar AJAXIF (datakey en this.cache) {this.render (this.cache [datakey]);} más paramValue; ajax.get (opts.url, params) .done (function (res) {// resolVeAJAX Esta devolución de llamada se usa para analizar los datos devueltos por Ajax externamente // debe devolver una matriz de datos var data = opts.resolveAdax (res); if (data) {that.cache] = data; that.render (data);}});}}, reender: function (data) {var html = [], opts = this.options; data.forEach (function (element) {html.push (['<opción valor = "', item [opts.valuefield], '" data-param-value = "', // almacenamiento el valor correspondiente del valor de paramfield de paramfield de paramfield Propiedad de valores de datos de datos del elemento de opción [opts.paramfield], '">', item [opts.textfield], '</option>']. Join (''));}); // Agregar dinámicamente en forma de append para evitar afectar la primera opción // en el final, establecer el valor para vaciar este. $ = true; // significa que hay contenido this.trigger ('render.cascadeItem');}, getValue: function () {return this. $ el.val ();}, getparamValue: function () {return this. $ el.find ('opción: seleccionado'). Data ('ParamValue');}, Extend:});4. Instrucciones de demostración
Demostrar la estructura del código:
Lo que está enmarcado es la parte relevante de la demostración. html/regist.html es la página que demuestra el efecto, y js/app/regist.js es la entrada al efecto de demostración js:
Define (función (requerir, exportar, módulo) {var $ = require ('jQuery'); var cascadeView = request ('mod/cascadeview'); function publicsetcascadeView (fieldName, opts) {this.cascadeview = new CascadeView ({$ Elements: $ ('#' ' + FieldName +' -view '). Find (encontrar (', '', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', ',', '). '../api/cascade.json', onChanged: this.onchanged, valores: opts.values, keepfirStOption: this.keepfirStOption, resolVeAdax: function (res) {if (res.code == 200) {return res.data; $ ('input [name = "LicensElocation"]'), KeepFirStOption: true, setCascadeView: PublicsetCascadEview, onChanged: function (e, valor) {ubicación_views.licenselocation. $ input.val (valor); SetCascadeView: PublicsetCascadeView, OnChanged: Function (E, Value) {ubicación_views.companylocation. $ input.val (valor); Ubicación_views.companylocation.setCascadeview ('CompanyLocation', {valores: ubicación_views.companylocation. $ Input.val ()});});Preste atención a la función de la variable ubicación_views en el código anterior, porque hay múltiples componentes en cascada en la página. Esta variable en realidad se gestiona de manera similar a través del modelo de política. Si no hace esto, es fácil generar código duplicado; Este formulario también es más propicio para la separación y la encapsulación de alguna lógica comercial en el archivo de entrada, como el lugar donde se procesa la lógica comercial.
5. Otros
Este es probablemente el último blog escrito por la compañía ahora. Tienes que ir a trabajar en una nueva unidad en dos días. No estoy seguro de si tiene tanto tiempo libre para grabar sus ideas de trabajo habituales, pero al menos ha desarrollado el hábito de escribir blogs, y exprimirá el tiempo si no tiene tiempo en el futuro. El objetivo de este año es principalmente ampliar el conocimiento y mejorar la calidad del código. Los blogs posteriores estarán más en la categoría de desarrollo de componentes. ¡Espero que pueda continuar prestando atención al sitio web de Wulin.com en el futuro!