Este artigo apresenta a idéia de implementação das imagens comuns na página da web para gerar diretamente as visualizações de pequenas imagens na página após o upload delas. Considerando que essa função possui certa aplicabilidade, a lógica relevante é encapsulada em um componente ImageUploadView. O efeito de uso real pode ser usado para visualizar as renderizações Git no próximo parágrafo. No processo de implementação desse componente, usamos o conteúdo relevante introduzido nos blogs anteriores, como a herança da biblioteca de herança Class.js e a biblioteca de gerenciamento de eventos eventbase.js de qualquer componente, que também inclui algumas reflexões sobre a separação de responsabilidades, separação de desempenho e comportamento. Bem -vindo a ler e se comunicar.
Efeito de demonstração:
NOTA: Como os códigos para a demonstração são todos estáticos, o componente carregado pelo arquivo é simulado pelo setTimeout, mas seu método de chamada é exatamente o mesmo que quando eu uso o componente de upload no meu trabalho real, a implementação do código do efeito de demonstração está completamente alinhada com os requisitos funcionais reais.
De acordo com as idéias anteriores do blog, vamos primeiro apresentar os requisitos para esta função de visualização de upload.
1. Análise de requisitos
De acordo com as renderizações de demonstrações anteriores, os requisitos de análise são os seguintes:
1) Na área de upload inicial, apenas um botão de upload clicável é exibido. Ao clicar neste botão, a imagem carregada será exibida na área de visualização subsequente.
2) Depois que a imagem carregada for adicionada à área de visualização, você pode removê -la excluindo o botão.
3) Quando o número total de imagens carregadas atingir um determinado limite, por exemplo, o limite de upload na demonstração é 4, remova o botão de upload;
4) Quando o número total de imagens carregadas atingir um certo limite, se uma determinada imagem for removida através da operação de exclusão, o botão de upload deverá ser exibido.
Os requisitos acima são visíveis. Com base na experiência, os requisitos que podem ser analisados são os seguintes:
1) Se a página estiver no estado de edição, ou seja, o estado consultado no banco de dados, desde que a lista de imagens não esteja vazia, a imagem deverá ser exibida no início; e também deve controlar se o botão de upload é exibido de acordo com o comprimento da lista de imagens encontrada e o limite de upload;
2) Se a página atual for um estado que só puder ser visualizado, mas não alterado, o botão de upload e o botão Excluir devem ser removidos no estágio inicial.
Após a conclusão da análise de requisitos, deixe -me explicar minhas idéias de implementação.
2. Idéias de implementação
Como esta é uma página de formulário, se você deseja enviá -la para o plano de fundo após o upload da imagem, precisará de um campo de texto. Então, quando eu estava fazendo uma página estática, levei esse campo de texto em consideração. Depois de fazer upload de uma nova imagem e excluir a imagem, tive que modificar o valor deste campo de texto. A estrutura desta parte quando as páginas estáticas foram feitas é a seguinte:
<div> <brety> Versão eletrônica do cartão de identificação da pessoa legal </elabel> <div> <insput id = "LegalPersonid-input" name = "LegalPersonidpic" type = "Hidden"> <ul id = "LegalPersonidpic-view"> <li> <a href = "javascript:; href = "#"> <i> </i> Exemplo </a> </p> </div> </div>
A partir dessa estrutura, também podemos ver que eu coloquei toda a área de upload em um UL e, em seguida, uso o primeiro botão Ul Li como o botão de upload. Para concluir esta função, nossas principais tarefas são: Upload e upload de retornos de chamada, adicionar ou excluir visualizações de imagens e gerenciar valores de campo de texto. Desse ponto de vista, combinado com a idéia de separação de responsabilidades, essa função requer pelo menos três componentes, um responsável pelo upload de arquivos, um responsável pelo gerenciamento de visualização de imagem e o outro responsável pelo gerenciamento de valores de domínio de texto. Nunca encapsule essas três funções em pares ou todas juntas. Nesse caso, o acoplamento funcional é muito forte e os componentes escritos são extensíveis e reutilizáveis. Se for necessária uma interação entre esses três componentes, precisamos defini-los como chamados de interfaces externamente com a ajuda de funções de retorno de chamada ou o modo de publicação de inscrição.
No entanto, o gerenciamento dos valores do domínio de texto é muito simples e não importa se é escrito como um componente ou não, mas pelo menos o encapsulamento no nível da função é necessário. Embora o componente de upload de arquivo não seja o foco deste artigo, existem muitos plug-ins de código aberto pronto na Internet, como o WebUploader, que podem ser aplicados, seja usado diretamente ou secundário. A função da visualização da imagem é o conteúdo principal deste artigo. O componente ImageUploadView é o encapsulamento dele. Do ponto de vista dos requisitos, existem apenas três métodos de exemplo semânticos para esse componente, a saber, renderizar, apegar e delitem. A renderização é usada para exibir a lista de visualização inicial após a conclusão da inicialização, o Append é usado para adicionar uma nova visualização de imagem após o upload e o Delitem é usado para excluir visualizações de imagem existentes. De acordo com essa ideia básica, precisamos apenas projetar opções e eventos para ela com base nos requisitos e experiência no desenvolvimento de componentes.
A partir dos requisitos anteriores, descobrimos que a renderização deste componente ImageUploadView será afetada pelo status da página. Quando a página está no modo de exibição, esse componente não pode fazer upload e excluir, portanto, você pode considerar adicionar uma opção leitura a ele. Ao mesmo tempo, suas operações de upload e exclusão também afetarão a lógica da UI do botão de upload, que está relacionado ao limite de upload. Para flexibilidade, o limite de upload também deve ser considerado uma opção. A partir dos três métodos de instância mencionados no parágrafo anterior, de acordo com sua experiência anterior na definição de eventos, um método de instância geralmente define um par de eventos, assim como o plug-in do bootstrap. Por exemplo, o método de renderização pode definir uma renderização. Antes. Este evento é acionado antes que a lógica principal da renderização seja executada. Se o ouvinte externo chamar o método DeventDefault () deste evento, a lógica principal da renderização não será executada; Há também um evento de renderização. Após, que é acionado após a execução da lógica principal da renderização. A vantagem deste evento de definição em pares é que ele não apenas fornece métodos externos para estender a funcionalidade do componente, mas também aumenta o gerenciamento do comportamento padrão do componente.
Finalmente, a partir da minha experiência de trabalho anterior, além de fazer upload de fotos para visualização, também fiz o upload de vídeos, carregando áudio, carregando documentos comuns, etc., então, quando encontrei essa função desta vez, senti que deveria extrair coisas semelhantes dessas funções como uma classe base, o upload de imagem, o upload de vídeo, etc., respectivamente. Outra vantagem desta classe base é que ela pode separar completamente a lógica geral da estrutura HTML. Nesta classe base, apenas coisas comuns são feitas, como a definição de opções e comportamento do componente (renderizar, anexar, delitem), bem como a escuta e o desencadeamento de eventos gerais. Ele só precisa deixar uma interface fixa para a subclasse implementar. Na implementação subsequente, defini um componente FileUploadBaseView para concluir as funções desta classe base. Esta classe base não contém nenhuma lógica para o processamento HTML ou CSS. Ele apenas abstrava as funções que queremos concluir e não lida com nenhuma lógica de negócios. As subclasses implementadas de acordo com a lógica de negócios serão limitadas pela estrutura HTML; portanto, o escopo da aplicação de subclasses é pequeno; E como a classe base é completamente separada da estrutura HTML, ela possui um escopo de aplicação maior.
3. Detalhes da implementação
A partir da idéia de implementação da Parte 2, as classes a serem implementadas são: FileUploadBaseView e ImageUploadView, o primeiro é a classe base deste último. Ao mesmo tempo, considerando que o componente precisa fornecer funções de gerenciamento de eventos, precisamos usar o EventBase.js no blog anterior, e o FileUploadBaseView deve herdar o componente EventBase da biblioteca; Considerando que existe uma definição de classe e herança, também precisamos usar a classe da biblioteca de herança. A relação herança dos componentes relacionados é: ImageUploadView Extend FileUploadBaseView Extend EventBase.
(Nota: o seguinte código relacionado usa o SEAJS em modularidade.)
O que o FileUploadBaseView faz é:
1) Defina opções comuns e gerenciamento de eventos comuns
Na configuração padrão deste componente, você pode ver as definições de todas as opções e eventos comuns:
var padrão = {data: [], // A lista de dados a serem exibidos deve ser do tipo de objeto, como [{url: 'xxx.png'}, {url: 'yyyy.png'}] sizelimit: 0, // limitar o número de elementos exibidos em BasewView, para 0, não é que o sizeliMit: OnBeforeRender: $ .noop, //forereport.be for Event, Trigger onrender: $ .noop, //forereport.after evento antes que o método renderize seja chamado e, no evento, o Event OnAppend: $ .noop, // complicado com o Append.fore Event, Trigger OnAppênd: $ .noop, // complicado com o APEND.Fore Event, Trigger OnAppend: $ .noop, // complicado com o Apênd.Feend.fore, acionando: Com o evento DeliTem. antes, o gatilho ondelitem: $ .noop antes do método Delitem ser chamado // cumpriu o evento Delitem.After, acionado};No método init do componente, você pode ver a lógica de inicialização para o gerenciamento geral de opções e eventos:
init: function (elemento, opções) {// Chame o método init da classe pai EventBase através desse.base; // atributo da instância var opts = this.options = this.getOptions (options); this.data = resolvedata (opts.data); excluir opts.data; this.sizelimit = opts.sizelimit; this.readonly = opts.readonly; // evento de ligação this.on ('render.bee', $ .proxy (opts.onBeforeRender, isto)); this.on ('render.after', $ .proxy (opts.onRender, isto)); this.On ('Append.be antes', $ .Proxy (opts.onBeForEPpend, isto)); this.On ('Append.After', $ .Proxy (opts.Onappend, este)); this.On ('Delitem.Be', $ .Proxy (opts.onBeForedElitem, isso)); this.on ('Delitem.after', $ .Proxy (opts.ondelitem, este);},2) Defina o comportamento dos componentes e as interfaces de reserva que podem ser implementadas por subclasses:
render: function () {/*** renderize é um modelo. A subclasse não precisa substituir o método de renderização, só precisa substituir o método _render* quando o método de renderização da subclasse é chamado, o método de renderização da classe pai é chamado*, mas ao executar o método _render, o método de _Render da subclasse é chamado* Isso unificará as operações de acionamento de antes e depois de eventos*/vare; this.Trigger (e = $ .Event ('render.be antes')); if (e.isdefaultPrevented ()) retornar; this._render (); this.Trigger ($. Event ('render.after'));}, // As subclasses precisam implementar o _render method_render: function () {}, anexar: function (item) {var e; if (! Item) return; App. (eFefaultPreventEd ()) return; this.data.push (item); this._append (item); this.Trigger ($. Event ('Append.After'), Item);}, // Subclasses precisam implementar o _Append Method_Appênd: function (Data), {}, deLitem: Function: Function) (Dados), ({}, deLitem: Function). this.getDataitem (uuid); if (! Item) retornar; this.Trigger (e = $ .Event ('DeliTem.Be'), item); if (e.isDefaultPreventEd ()) return; this.data.splice (this.getDataAtEMIndex (uuid), 1); this._delitem (item); this.Trigger ($. Event ('Delitem.after'), item);}, // As subclasses precisam implementar _delitem method_delitem: function (dados) {}Para lidar uniformemente da lógica de distribuição de eventos antes e após o comportamento, a lógica principal da renderização, apendimento, Delitem é extraída em métodos _render, _append e _delitem que precisam ser implementados por subclasses. Quando o método de renderização de uma subclasse é chamado, o método da classe pai é realmente chamado, mas quando a classe pai executa o método _render, o método da subclasse é executado e os outros dois métodos também são semelhantes. Deve -se notar que as subclasses não podem substituir os três métodos renderizados, anexos e delitem; caso contrário, você deve lidar com a lógica do gatilho dos eventos relacionados por si mesmo.
A implementação geral do FileUploadBaseView é a seguinte:
define (função (requer, exporta, módulo) {var $ = requer ('jQuery'); var class = requim ('mod/class'); var eventBase = requer ('mod/eventbase'); var de padrão = {Data: [], // para exibir a lista de dados, os elementos da lista devem ser do tipo de objeto {{url: 'xx. 'yyyyy.png'}] sizelimit: 0, // para limitar o número de elementos exibidos no Baseview, se 0 significa nenhum limite readonly: false, // para controlar se os elementos no Baseview podem ser adicionados e excluídos no que o evento. .noop, // correspondente Append.Beve Evento, gatilho OnAppend: $ .noop, // Evento Append.After correspondente, Trigger onBeForedElitem: $ .noop, // correspondente Append.After, TriGer onEformEl, TriGer OnBeedElitem: $ .noop, // APEND.AFTELATEM, TRIGER ONBER ONBEREL Trigger ondelitem: $ .noop // Evento Delitem.After correspondente, Trigger};/*** Processamento de dados, adicione um atributo _uUID a cada registro de dados para facilitar a pesquisa*/function resolvedata (dados) {var time = new). Resolvedataitem (Data, Time) {Time = Time || new Date (). this.base; // atributo de instância var = thes isto); $ .Proxy (opts.onBeForedElitem, este)); (uuid) {// obtém dateItemReturn this.data.filter (function (item) {return item._uuid === uuid;}) [0];}, getDataitemIndex: function (uuid) {var ret; RET;}, renderizar: function () {/*** renderizar é um modelo. e; return; item = resolvedataitem (item); this.Trigger (e = $ .event ('append.be antes'), item); if (e.isdefaultPreved ()) return; this.data.push (item); this._append (item); this.Trigger ($. Method_Append: function (data) {}, Delitem: function (uuid) {var e, item = this.getDataitem (uuid); if (! Item) retornar; this.Trigger (e = $ .Event ('Delitem.Fee'), item); if (e.IspleTeTeTeTeTeNAT (Delitem.Be '), Item); if (e.IsplExeTeTeTeTeT () (' Delitem.Be '), Item); if (e.IsplExeTeTeTeTeNAT ('DeTem.De.De'), ItemeTeTeTeTeTeTEn () (). 1); this._delitem (item); this.Trigger ($. Event ('Delitem.After'), item);}, // subclasses precisam implementar o _delitem method_delitem: function (dados) {}}, extend: EventMeSe »);A implementação do ImageUploadView é relativamente simples, semelhante ao preenchimento dos espaços em branco. Existem alguns pontos a serem explicados:
1) Os padrões desta classe precisam estender os padrões da classe pai para adicionar as opções padrão desta subclasse e também manter a definição das opções padrão da classe pai; De acordo com a estrutura da página estática, um evento ONAPPENDCLICK foi adicionado e os métodos relevantes dos componentes de upload de arquivos podem ser chamados externamente neste evento:
// herdam e estendem o padrão padrão da classe pai padrão = $ .Extend ({}, FileUploadBaseView.Defaults, {OnAppendClick: $ .noop // Retorno de chamada ao clicar no botão de upload});2) No método init, o método init da classe pai precisa ser chamado para concluir o processamento lógico geral; No final do init, você deve chamar manualmente o método de renderização para poder ver o efeito depois que o componente for instanciado:
Outras implementações são implementações puramente lógicas de negócios e estão intimamente relacionadas aos requisitos da Parte 2.
A implementação geral do ImageUploadview é a seguinte:
define (function (requer, exportações, módulo) {var $ = requer ('jQuery'); var class = require ('mod/class'); var fileUploadBaseView = require ('mod/fileUploadBaseView'; // herança e estend o defaultsVARFAULTSEFASTENETENDETENETEN); {} $ .noop // callback Ao clicar no botão de upload}); var imageUploadView = classe ({InstanceMembers: {init: function, elemento, opções) {var $ element = this. $ element = $ (elemento); var opts = this.getOptions (options); // chama o método init da classe parental para completar a aquisição de opções, tis.getOptions (opções); Opções); // Adicione o upload e exclua os ouvintes e aciona o processamento se (! this.readonly) {var thats = this; that.on ('appendclick', $ .proxy (opts.onappendClick, isto); that.Trigger ('AppendClick');}); $ element.on ('click.remove', '.View-act-Del', function (e) {var $ this = $ (e.currentTarget); that.melitem ($ this.data ('uuid'); e.preventDefAult (); {return padrão;}, _ SetItemaddhtml: function () {this. $ element.prepend ($ ('<li> <a href = "javaScript :;">+</a> </li>');}, _ clearMaddhtml: function ($ iteddli)); function () {var html = [], que = this; // se não for um estado somente leitura e não atingiu o limite de upload, adicione o botão de upload if (! (this.readonly || (this.sizelimit && this.sizelimit <= this.data.Length)) {this._Setem. {html.push (that._getItemRenderhtml (item))}); this. $ element.append ($ (html.join ('')); src = "', item.url,'"> ', this.readonly? this. $ element.find ('li.view-item-add'); // se o limite de upload for atingido, remova o botão de upload se (this.sizelimit && this.sizelimit <= this.data.length && $ itemAddli.length) {this.learitemaddhtml ($ itemaddli.length) {this.leartemaddhtml ($ itemaddli.length) {this.leartemaddhtml ($ itemAddli.length) {this.__Clina) {this._setItemaddhtml ();}}}, _ Anexar: function (data) {this. $ element.append ($ (this._getItemRendehtml (data)); this._dealwithsizelimit ();}, _ delitem: function (dados) {$ data._uuid) .remove (); this._dealwithsizelimit ();}}, estend: fileUploadBaseView});4. Instruções de demonstração
A estrutura do projeto de demonstração é:
O que é enquadrado é o código central da demonstração. Entre eles, o FileUploadBaserview.js e o ImageUploadView.js são os dois componentes principais implementados na implementação anterior. FileUploader.js é usado para simular o upload de componentes. Sua instância possui um retorno de chamada ãccess, indicando que o upload é bem -sucedido; Há também um OpenChooseFileWin usado para simular o processo de abertura da janela do arquivo de seleção e enviá -lo:
define (function (requer, exporta, módulo) {return function () {var iMGlist = ['../Img/1.jpg'.'..'../img/2.jpg'.'../img/3.jpg'.'h/iMg/4.jpg'], i = 0; var th This; thatoOn.OnsuCess = FUNSCESS = FUNLOUNS = (} function () {setTimeout (function () {that.onsuccess (imglist [i ++]); if (i == imglist.length) {i = 0;}}, 1000);}}});App/regist.js é o código lógico da página de demonstração, e as principais peças foram explicadas com comentários:
define (função (requer, exporta, módulo) {var $ = requer ('jQuery'); var imageUploadView = requer ('mod/imageuploadview'); var fileUploader = requer ('mod/fileUPLOADER'); // Este é um componente de uplopom de arquivo. Carrega com êxito e após o componente ImageUploadview excluir um determinado item, isso afetará o valor de $ LegalPersonidpic Var $ LegalPersonidpic = $ ('#LegalPersonidpic-input'), Data = JSON.Parse ($ LegalPersonIdpic.Val () Atualmente, a página de Dats); ImageUploadView Component // Após o upload do arquivo ser enviado com sucesso, salve o arquivo recém -enviado no valor de $ LegalPersonidpic // $ LegalPersonidpic Stores var AppendImageInputValue = função ($ input, item) {var value = json.parse ($ input.val () || '[]'); value.push (item); $ input.val (json.stringify (value));}; // Quando o componente ImageUploadView é chamado para excluir um item, as informações armazenadas em $ LegalPersonidpic devem ser limpas síncronas {varear) '[]'), index; value.ForEach (function (item, i) {if (item._uuid === uuid) {index = i;}}); value.splice (index, 1); $ input.val (json.stringify (value);}; var fileUPLORER = new FileUPLODER (); {var item = {url: uploadValue};legalPersonIDPicView.append(item);appendImageInputValue($legalPersonIDPic, item);};var legalPersonIDPicView = new ImageUploadView('#legalPersonIDPic-view', {data: data,sizeLimit: 4,onAppendClick: function () {//Open the window to select the file FILEUPLOADER.OPENCHOOSEFILEWIN ();}, ONDELITEM: function (Data) {RemoneImageInputValue ($ LegalPersonidpic, Data._uuid);}});});5. Resumo deste artigo
O componente ImageUploadView não é difícil de implementar no final, mas também gastei muito tempo pensando nisso e em outras classes pais para implementá -lo, e na maioria das vezes foi gasto abstraindo a separação de responsabilidades e separação comportamental. As opiniões sobre esses dois aspectos das idéias de programação expressas neste artigo são apenas minha própria experiência pessoal. Por causa do nível abstrato, os métodos de pensamento de todos e o entendimento final não serão os mesmos. Portanto, não posso dizer diretamente se estou certo ou errado. O objetivo da escrita é compartilhar e se comunicar e ver se existem outros amigos experientes que estão dispostos a falar sobre suas idéias a esse respeito. Acredito que, depois que todos lêem muito sobre as idéias de outras pessoas, elas também ajudarão suas próprias idéias de programação.