Este artículo presenta la idea de implementación de las imágenes comunes en la página web para generar directamente pequeñas vistas previas en la página después de cargarlas. Teniendo en cuenta que esta función tiene cierta aplicabilidad, la lógica relevante se encapsula en un componente ImageUploadView. El efecto de uso real se puede utilizar para ver las representaciones GIT en el siguiente párrafo. En el proceso de implementación de este componente, utilizamos el contenido relevante introducido en los blogs anteriores, como la biblioteca de herencia class.js, y la biblioteca de eventos de gestión eventBase.js de cualquier componente, que también incluye algunas ideas sobre la separación de las responsabilidades, la separación del rendimiento y el comportamiento. Bienvenido a leer y comunicarse.
Efecto de demostración:
Nota: Dado que los códigos para la demostración son todos estáticos, el componente cargado por el archivo está simulado por SetTimeOut, pero su método de llamada es exactamente el mismo que cuando uso el componente de carga en mi trabajo real, por lo que la implementación del código del efecto de demostración está completamente en línea con los requisitos funcionales reales.
Según mis ideas de blog anteriores, primero presentemos los requisitos para esta función de vista previa de carga.
1. Análisis de requisitos
Según las representaciones de demostración anteriores, los requisitos de análisis son los siguientes:
1) En el área de carga inicial, solo se muestra un botón de carga que se puede hacer clic. Al hacer clic en este botón, la imagen cargada se mostrará en el área de vista previa posterior.
2) Después de agregar la imagen cargada al área de vista previa, puede eliminarla eliminando el botón.
3) Cuando el número total de imágenes cargadas alcanza un cierto límite, por ejemplo, el límite de carga en la demostración es 4, elimine el botón de carga;
4) Cuando el número total de imágenes cargadas alcanza un cierto límite, si se elimina una determinada imagen a través de la operación de eliminación, se debe mostrar el botón de carga.
Los requisitos anteriores son visibles. Según la experiencia, los requisitos que se pueden analizar son los siguientes:
1) Si la página está en el estado de edición, es decir, el estado consultado desde la base de datos, siempre que la lista de imágenes no esté vacía, la imagen debe mostrarse al principio; y también debe controlar si el botón de carga se muestra de acuerdo con la longitud de la lista de imágenes encontradas y el límite de carga;
2) Si la página actual es un estado que solo se puede ver pero no se cambia, entonces el botón de carga y el botón Eliminar se deben eliminar en la etapa inicial.
Después de completar el análisis de requisitos, permítanme explicar mis ideas de implementación.
2. Ideas de implementación
Dado que esta es una página de formulario, si desea enviarla al fondo después de cargar la imagen, debe necesitar un campo de texto. Entonces, cuando estaba haciendo una página estática, tomé este campo de texto en consideración. Después de subir una nueva imagen y eliminar la imagen, tuve que modificar el valor de este campo de texto. La estructura de esta parte cuando se hicieron páginas estáticas es la siguiente:
<div><label>Electronic version of the legal person ID card</label><div><input id="legalPersonIDPic-input" name="legalPersonIDPic"type="hidden"><ul id="legalPersonIDPic-view"><li><a href="javascript:;">+</a></li></ul><p>Please make sure the pictures are clear and the text is identificable <a href = "#"> <i> </i> Ejemplo de vista </a> </p> </div> </div>
Desde esta estructura, también podemos ver que pongo todo el área de carga en una UL, y luego uso el primer UL Li como el botón de carga. Para completar esta función, nuestras tareas principales son: cargar y cargar devoluciones de llamada, agregar o eliminar avances de imagen y administrar valores de campo de texto. Desde este punto de vista, combinado con la idea de separación de responsabilidades, esta función requiere al menos tres componentes, uno responsable de la carga de archivos, uno responsable de la gestión de la vista previa de imágenes y el otro responsable de la gestión de valores del dominio del texto. Nunca encapsule estas tres funciones en parejas o en todos juntos. En ese caso, el acoplamiento funcional es demasiado fuerte y los componentes escritos son extensibles y reutilizables. Si se requiere interacción entre estos tres componentes, solo necesitamos definirlos a interfaces llamadas externamente con la ayuda de funciones de devolución de llamada o el modo de publicación de suscripción.
Sin embargo, la gestión de los valores del dominio del texto es muy simple, y no importa si está escrito como un componente o no, pero se requiere al menos encapsulación a nivel de función. Aunque el componente de carga de archivos no es el foco de este artículo, existen muchos complementos de código abierto preparados en Internet, como Webuploader, que se puede aplicar si se usa directamente o la encapsulación secundaria. La función de la vista previa de la imagen es el contenido central de este artículo. El componente ImageUploAdView es la encapsulación del mismo. Desde el punto de vista de los requisitos, solo hay tres métodos de ejemplo semánticos para este componente, a saber, representar, agregar y delitem. El render se utiliza para mostrar la lista de vista previa inicial después de completar la inicialización, la apertura se usa para agregar una nueva vista previa de imagen después de la carga, y Delitem se usa para eliminar las vistas previas de imágenes existentes. Según esta idea básica, solo necesitamos diseñar opciones y eventos para TI en función de los requisitos y la experiencia en el desarrollo de componentes.
De los requisitos anteriores, encontramos que el render de este componente ImageUploAdView se verá afectado por el estado de la página. Cuando la página está en modo de vista, este componente no puede cargar y eliminar, por lo que puede considerar agregarle una opción de lectura. Al mismo tiempo, sus operaciones de carga y eliminación también afectarán la lógica de la interfaz de usuario del botón de carga, que está relacionada con el límite de carga. Para flexibilidad, el límite de carga también debe considerarse como una opción. De los tres métodos de instancia mencionados en el párrafo anterior, de acuerdo con su experiencia previa en la definición de eventos, un método de instancia generalmente define un par de eventos, al igual que el complemento de Bootstrap. Por ejemplo, el método de renderizado puede definir un render. Este evento se activa antes de ejecutar la lógica principal del render. Si el oyente externo llama al método PreventDefault () de este evento, entonces la lógica principal del render no se ejecutará; También hay un render. La ventaja de este evento de definición por pares es que no solo proporciona métodos externos para extender la funcionalidad de los componentes, sino que también aumenta la gestión del comportamiento predeterminado del componente.
Finalmente, desde mi experiencia laboral anterior, además de cargar imágenes para una vista previa, también he subido videos, cargar audio, cargar documentos ordinarios, etc., por lo que cuando encontré esta función esta función, sentí que debía extraer cosas similares de estas funciones como clase base, carga de imagen, carga de video, videos, etc., respectivamente, investigar esta clase base para implementar su lógico respectivo. Otra ventaja de esta clase base es que puede separar completamente la lógica general de la estructura HTML. En esta clase base, solo se hacen cosas comunes, como la definición de opciones y comportamiento de componentes (representar, agregar, delitem), así como la escucha y desencadenante de eventos generales. Solo necesita dejar una interfaz fija para que la subclase implementa. En la implementación posterior, definí un componente FileUploadBaseView para completar las funciones de esta clase base. Esta clase base no contiene ninguna lógica para el procesamiento HTML o CSS. Simplemente abstrae las funciones que queremos completar y no maneja ninguna lógica comercial. Las subclases implementadas según la lógica de negocios estarán limitadas por la estructura HTML, por lo que el alcance de la aplicación de subclases es pequeño; Y debido a que la clase base está completamente separada de la estructura HTML, tiene un mayor alcance de aplicación.
3. Detalles de implementación
Desde la idea de implementación de la Parte 2, las clases que se implementarán son: FileUploadBaseView e ImageUploadView, la primera es la clase base de la segunda. Al mismo tiempo, teniendo en cuenta que el componente necesita proporcionar funciones de administración de eventos, necesitamos usar eventbase.js en el blog anterior, y fileUploadbaseView debe heredar el componente de eventbase de la biblioteca; Teniendo en cuenta que hay una definición y herencia de clase, también necesitamos usar la biblioteca de herencia clase. Js escrito antes para definir el componente y la relación de herencia del componente. La relación de herencia de los componentes relacionados es: ImageUploadView Extend FileUploadBaseView Extend EventBase.
(Nota: El siguiente código relacionado usa SEAJ en modularidad).
Lo que hace FileUploadBaseView es:
1) Definir opciones comunes y gestión de eventos comunes
En la configuración predeterminada de este componente, puede ver las definiciones de todas las opciones comunes y eventos comunes:
var defaults = {data: [], // La lista de datos que se muestran debe ser de tipo de objeto, como [{url: 'xxx.png'}, {url: 'yyyy.png'}] sizeLimit: 0, // para limitar el número de elementos mostrados en BaseView, a 0, que significa que no lo hace. y eliminar onborerender: $ .Noop, //forRePort.Before Event, Trigger Onrender: $ .Noop, //forRePort.Afrited Event antes de que se llame el método de renderizado, y onbeforaPeppend: $ .Noop, // cumplido con el evento append.bebere, activador: $ .Noop, // cumplido con el evento apénd. // cumplió con el evento delitem.En el método init del componente, puede ver la lógica de inicialización para la opción general y la gestión de eventos:
init: function (element, options) {// llame al método init de la base de eventos de la clase principal a través de this.base; // instance attribute var opts = this.options = this.getOptions (opciones); this.data = resolvedata (opts.data); eliminar opts.data; this.sizelimit = opts.sizelimit; this.readonly = opts.readonly; // evento vinculante this.on ('render.before', $ .proxy (opts.onbeforerender, this)); this.on ('render.After', $ .proxy (opts.onrender, this)); this.on ('append.before', $ .proxy (opts.onbeforaAppend, esto)); this.on ('append.after', $ .proxy (opts.onappend, this)); this.on ('delitem.before', $ .proxy (opts.onbeforedElitem, this)); this.on ('delitem.fter', $ .proxy (opts.ondelitem, this));},2) Definir el comportamiento de los componentes e interfaces de reserva que pueden ser implementadas por subclases:
render: function () {/*** render es una plantilla. La subclase no necesita anular el método de renderizado, solo necesita anular el método _render* Cuando se llama el método de renderizado de la subclase, el método de representación de la clase principal se llama* pero al ejecutar el método _render, el método _render de la subclasa se llama* esto unirá las operaciones de activación de antes y después de los eventos*/var e; this.trigger (e = $ .event ('render.before')); if (e.isdefaultPrevented ()) return; this._render (); this.trigger ($. Event ('render.after'));}, // Las subclases deben implementar el _render método_render: function () {}, append: function (item) {var e; if (! item) return; item = item = item (item); this.trigger (e = $ .Event ('append.beberfore'), if (item); if ((item = resuelveAtem (item); this.trigger (e = $ .Event ('append.beberfore'), if (item); if ((item) return; this.data.push (item); this._append (item); this.trigger ($. Event ('append.after'), item);}, // Las subclases necesitan implementar _append método_append: function (data) {}, delitem: function (uuid) {var e, item = this.getDataItem (uuid); if ((if ((iptint) return; $ .Event ('delitem.before'), item); if (e.isdefaultprevent ()) Method_delitem: function (data) {}Para manejar de manera uniforme la lógica de distribución de eventos antes y después del comportamiento, la lógica principal de Render, Append, Delitem se extrae en Métodos _render, _append y _delitem que deben ser implementados por subclases. Cuando se llama el método de renderizado de una subclase, se llama en realidad el método de la clase principal, pero cuando la clase principal ejecuta el método _render, el método de la subclase se ejecuta y los otros dos métodos también son similares. Cabe señalar que las subclases no pueden anular los tres métodos renderizados, agregar y delitem, de lo contrario debe manejar la lógica desencadenante de eventos relacionados por sí mismo.
La implementación general de FileUploadBaseView es la siguiente:
Define (función (requerir, exportar, módulo) {var $ = require ('jQuery'); var class = request ('mod/class'); var eventbase = require ('mod/eventbase'); var defaults = {data: [], // Para mostrar la lista de datos, los elementos de lista deben ser de tipo de objeto, como [{url: 'xxx.png'}, {Url {url/{url {url/{url {url/{url {url/{url {url: 'aaaa yyy.png'}] SizeLimit: 0, // para limitar el número de elementos que se muestran en BaseView, si 0 no significa límite Readonly: false, // para controlar si los elementos en BaseView se permiten y se eliminan y se eliminan en Borerender: $ .Noop, // Render. onbeforaAppend: $ .Noop, // Corresponding append.bebore evento, activar onappend: $ .Noop, // Corresponding append.After Event, disparar onbeforedElitem: $ .Noop, // Corresponding append.after Event, disparar onBoreforedElitem: $ .Noop, // correspondiente evento de append.after, trigger onboreForForForForFterM. delitem.before el evento, activar el procesamiento de datos de ondelitem: $ .Noop // correspondiente. tiempo);});} función resuelveAtAtem (data, time) {time = time || new Date (). CLASE PARA LABIERA EVENTACIÓN $ .proxy (opts.onbeforerender, this)); this.on ('delitem.before', $ .proxy (opts.onbeforedElitem, this)); Predeterminados;}, getDataItem: function (uuid) {// get dateitemreturn this.data.filter (function (item) {return item._uuid === uuid;}) [0];}, getDataTemindex: function (uuid) {var; this.data.foreach (function (item, i) {item._uUid) = i);}); return ret;}, render: function () {/*** render es una plantilla. Operaciones de antes y después de los eventos*/var e; e; if (! item) return; item = resueldataTem (item); this.trigger (e = $ .event ('append.bebore'), item); if (e.isDefaultPrevented ()) return; this.data.push (item); this._append (item); this.trigger ($. método_append: function (data) {}, delitem: function (uuid) {var e, item = this.getDataItem (uuid); if (! item) return; this.trigger (e = $ .Event ('delitem.before'), item); if (e.isdefaultpreventent ()) 1); this._delitem (item); this.Rigger ($. Event ('delitem.after'), item);}, // Las subclases necesitan implementar _Delitem Method_delitem: function (data) {}}, extender: eventbase, staticmebers: {defaults: defaults}); regreso archioneuploadbase;});});});La implementación de ImageUploAdView es relativamente simple, similar a llenar los espacios en blanco. Hay algunos puntos para explicar:
1) Los valores predeterminados de esta clase deben extender los valores predeterminados de la clase principal para agregar las opciones predeterminadas de esta subclase, y también retener la definición de las opciones predeterminadas de la clase principal; De acuerdo con la estructura de la página estática, se ha agregado un evento OnAppendClick, y los métodos relevantes de los componentes de carga de archivos se pueden llamar externamente en este evento:
// heredar y extender el valor predeterminado predeterminado de la clase principal predeterminadas = $ .extend ({}, fileuploadbaseView.defaults, {onappendClick: $ .Noop // devolución de llamada al hacer clic en el botón de carga});2) En el método init, el método init de la clase principal debe llamarse para completar ese procesamiento lógico general; Al final de Init, debe llamar manualmente el método de renderizado para que pueda ver el efecto después de que se instanciará el componente:
Otras implementaciones son implementaciones lógicas puramente comerciales y están estrechamente relacionadas con los requisitos de la Parte 2.
La implementación general de ImageUploadView es la siguiente:
Define (function (requisito, exports, módulo) {var $ = require ('jQuery'); var class = require ('mod/class'); var fileUploadBaseView = require ('mod/fileuploadbaseView'); // inherit y extender el default predeterminado defaults = $ .extend ({}, fileuploview. $ .Noop // devolución de llamada al hacer clic en el botón de carga}); var imageUploAdView = class ({InstanceMembers: {init: function (elemento, opciones) {var $ element = this. $ element = $ (elemento); var opts = this.getOptions (opciones); // Llame al método init de la clase principal para completar la adquisición de opciones, la adquisición de datos, el procesamiento de la escucha y el procesamiento de la escucha de la audiencia de la audiencia de la audiencia de los eventos generales. opciones); // Agregar los oyentes de carga y eliminación y activar el procesamiento if (! this.readonly) {var that = this; that.on ('appendClick', $ .proxy (opts.onappendClick, this)); $ element.on ('click.append', '.view-act that.trigger('appendClick');});$element.on('click.remove', '.view-act-del', function (e) {var $this = $(e.currentTarget); that.delItem($this.data('uuid'));e.preventDefault();});}this.render();},getDefaults: function () {return defaults;}, _ setitemaddhtml: function () {this. $ element.prepend ($ ('<li> <a href = "javascript:;">+</a> </li>');}, _ clearItemadhtml: function ($ itemaddli) {$ itemaddli.remove (); () {var html = [], that = this; // Si no es un estado de solo lectura y no ha alcanzado el límite de carga, agregue el botón de carga if (! (this.readonly || (this.sizelimit && this.sizelimit <= this.data.length))))) {html.push (that._getItemrenderHtml (item))}); this. $ element.append ($ (html.Join ('' ')));}, _ getitemrenderHtml: function (item) {return [' <li id = "', item._uuid,"> "> <a href =" javaScript:; " src = "', item.url,">', this.readonly? this. $ element.find ('li.view-iteM-add'); // Si se ha alcanzado el límite de carga, elimine el botón de carga if (this.sizelimit && this.sizelimit <= this.data.length && $ itemaddli.length) {this._clareTeMaddhtml ($ itememaddli);} else if (ip (! {this._setItemAddhtml ();}}}, _ append: function (data) {this. $ element.append ($ (this._getitemrenderhtml (data))); this._dealwithsizeLimit ();}, _ delitem: function (data) {$ ('#' + ' +' + ' +' + ' +' + ' +' + ' +' + ' +' + ' +' + data._uuid) .remove (); this._dealwithsizeLimit ();}}, extender: fileUploadBaseView});4. Instrucciones de demostración
La estructura del proyecto de demostración es:
Lo que está enmarcado es el código central de la demostración. Entre ellos, FileUploadBaserview.js y ImageUploadView.js son los dos componentes principales implementados en la implementación anterior. FileUploader.js se usa para simular componentes de carga. Su instancia tiene una devolución de llamada en OnSuccess, lo que indica que la carga es exitosa; También hay un OpenChooseFileWin utilizado para simular el proceso de abrir la ventana del archivo de selección y cargarla:
Define (función (requerir, exportar, módulo) {return function () {var imglist = ['../img/1.jpg','../img/2.jpg','../img/3.jpg','../img/4.jpg'], i = 0; var that = this ;s.onsuccess = function (uploadvalue) {oplegenchoSeBenchOs.Opence function () {setTimeOut (function () {that.onsuccess (imglist [i ++]); if (i == imglist.length) {i = 0;}}, 1000);}}});App/Regist.js es el código lógico de la página de demostración, y las partes clave se han explicado con los comentarios:
Define (función (requerir, exportaciones, módulo) {var $ = require ('jQuery'); var imageUploadView = request ('mod/imageUploadView'); var fileUploader = require ('mod/fileUploader'); // Esta es un componente de carga de archivo Simulada por tareas asynchronales // $ legalpersonidpic, lo que es una información de carga de carga. El componente se carga correctamente y después del componente ImageUpploAdView elimina un cierto elemento, afectará el valor de $ LegalPersonIdPic var $ LegalPersonIdPic = $ ('#LegalPersonIdPic-Input'), data = JSON.PARSE ($ LegalPersonIdPic.Val () || ']' ''; // Data es el valor inicial. Se presenta con el componente ImageUploadView // Después de que la carga de archivo se cargue correctamente, guarde el archivo recientemente cargado en el valor de $ LegalPersonidPic // $ LegalPersonIdPic Stores VAR AppendImageInputValue = function ($ input, item) {var valor = json.parse ($ input.val () || '[]'); value.push (elemento); $ input.val (json.stringify (valor));}; // Cuando el componente ImageUpploAdView se llama para eliminar un elemento, la información almacenada en $ LegalPersonIdPic debe estar bordeada sincronamente ($ tope.val. '[]'), index; value.forEach (function (item, i) {if (item._uuid === uuid) {index = i;}}}); value.splice (index, 1); $ input.val (json.stringify (value));}; var fileuploader = newUploader {var item = {url: uploadValue}; legalPersonIdPicView.append (item); appendImageInputValue ($ legalPersonIdPic, item);}; var legalPersonIdPicView = new imageUploAdView ('#legalPersonIdpic-View', {Data: Data, Sizelimit: 4, OnappendClek () Ventana para seleccionar el archivo fileUploader.openchoosefilewin ();}, onDelitem: function (data) {removeMageInputValue ($ legalPersonIdPic, data._uuid);}});});5. Resumen de este artículo
El componente ImageuploadView no es difícil de implementar al final, pero también pasé mucho tiempo pensando en ello y otras clases de padres para implementarlo, y la mayoría de las veces se dedicó a abstraer la separación de responsabilidades y la separación de comportamiento. Las opiniones sobre estos dos aspectos de las ideas de programación expresadas en este artículo son solo mi experiencia personal. Debido al nivel abstracto, los métodos de pensamiento de todos y la comprensión final no serán lo mismo. Por lo tanto, no puedo decir directamente si tengo razón o no. El propósito de escribir es compartir y comunicarse, y ver si hay otros amigos experimentados que están dispuestos a contarle sobre sus ideas a este respecto. Creo que después de que todos hayan leído demasiado sobre las ideas de otras personas, también ayudarán a su propia capacitación en ideas de programación.