В этой статье представлена идея реализации общих изображений на веб -странице, чтобы напрямую генерировать предварительные просмотра небольших изображений на странице после их загрузки. Учитывая, что эта функция имеет определенную применимость, соответствующая логика инкапсулируется в компонент ImageUploadView. Фактический эффект использования может быть использован для просмотра визуализации GIT в следующем абзаце. В процессе реализации этого компонента мы используем соответствующий контент, введенный в предыдущие блоги, такие как Class.js Class.js и библиотека библиотеки наследства, и библиотека управления событиями. Добро пожаловать, чтобы читать и общаться.
Демонстрационный эффект:
ПРИМЕЧАНИЕ. Поскольку все коды демонстрации являются статическими, компонент, загруженный файлом, моделируется SetTimeout, но его метод вызова точно так же, как при использовании компонента загрузки в моей фактической работе, поэтому реализация кода демонстрационного эффекта полностью соответствует реальным функциональным требованиям.
Согласно моим предыдущим идеям блога, давайте сначала представим требования для этой функции предварительного просмотра загрузки.
1. Анализ требований
Согласно предыдущим демонстрационным видам, требования к анализу следующие:
1) В начальной области загрузки отображается только одна кнопка загрузки. При нажатии на эту кнопку загруженное изображение будет отображаться в последующей области предварительного просмотра.
2) После того, как загруженное изображение добавлено в область предварительного просмотра, вы можете удалить его, удалив кнопку.
3) Когда общее количество загруженных изображений достигает определенного предела, например, предел загрузки в демонстрации составляет 4, удалите кнопку загрузки;
4) Когда общее количество загруженных изображений достигает определенного предела, если определенное изображение удаляется с помощью операции удаления, должна отображаться кнопка загрузки.
Вышеуказанные требования видны. Основываясь на опыте, требования, которые могут быть проанализированы, следующие:
1) Если страница находится в состоянии редактирования, то есть состояние, запросили из базы данных, если список изображений не является пустым, изображение должно отображаться в начале; и он также должен контролировать, отображается ли кнопка загрузки в соответствии с длиной списка найденных изображений и предела загрузки;
2) Если текущая страница представляет собой состояние, которое можно просмотреть только, но не изменять, то кнопка загрузки и кнопка удаления должна быть удалена на начальном этапе.
После того, как анализ требований завершен, позвольте мне объяснить мои идеи реализации.
2. Идеи реализации
Поскольку это страница формы, если вы хотите отправить ее на фон после загрузки изображения, вам должно быть текстовое поле. Поэтому, когда я делал статическую страницу, я принял во внимание это текстовое поле. После загрузки нового изображения и удаления изображения мне пришлось изменить значение этого текстового поля. Структура этой части, когда статические страницы были сделаны следующей:
<div> <bakel> Электронная версия идентификационной карты юридического лица </label> <div> <input id = "LegalPersonIdpic-input" name = "LegalPersonIdpic" type = "hidden"> <l ud = "LegalPersonPic-view"> <li> <a href = "javascript :; Идентифицируемый <a href = "#"> <i> </i> Пример просмотра </a> </p> </div> </div>
Из этой структуры мы также можем увидеть, что я поместил всю область загрузки в одном UL, а затем использую первую Ul Li в качестве кнопки загрузки. Чтобы выполнить эту функцию, наши основные задачи: загрузка и загрузка обратных вызовов, добавление или удаление предварительных просмотров изображений и управление значениями текстового поля. С этой точки зрения, в сочетании с идеей разделения обязанностей, эта функция требует как минимум трех компонентов, один из которых отвечал за загрузку файлов, один отвечает за управление предварительным просмотром изображений, а также другую ответственность за управление значениями текстового домена. Никогда не инкапсулируйте эти три функции в парах или все вместе. В этом случае функциональная связь слишком сильна, а написанные компоненты расширяются и многоразовые. Если между этими тремя компонентами требуется взаимодействие, нам просто нужно определить их на внешние интерфейсы с помощью функций обратного вызова или режима Publish-Subscribe.
Тем не менее, управление значениями текстовых доменов очень просто, и не имеет значения, написан ли он как компонент или нет, но, по крайней мере, требуется инкапсуляция на уровне функций. Хотя компонент загрузки файла не является в центре внимания этой статьи, в Интернете есть много готовых плагинов с открытым исходным кодом, таких как WebUploader, которые можно применить, независимо от того, используется ли он напрямую или вторичную инкапсуляцию. Функция предварительного просмотра изображения является основным содержанием этой статьи. Компонент ImageUploadView - это инкапсуляция. С точки зрения требований, для этого компонента есть только три семантических примера, а именно рендеринг, добавление и delitem. Рендерин используется для отображения начального списка предварительного просмотра после завершения инициализации, добавление для добавления нового предварительного просмотра изображения используется для добавления нового предварительного просмотра изображения, и Delitem используется для удаления существующих предварительных просмотров изображений. Согласно этой базовой идее, нам нужно только разработать варианты и события для нее на основе требований и опыта в разработке компонентов.
Из предыдущих требований мы обнаружили, что на визуализацию этого компонента ImageUploadView будет влиять статус страницы. Когда страница находится в режиме просмотра, этот компонент не может загружать и удалять, поэтому вы можете рассмотреть возможность добавления в него опцию Readonly. В то же время его операции загрузки и удаления также повлияют на логику пользовательского интерфейса кнопки загрузки, которая связана с пределом загрузки. Для гибкости ограничение загрузки также должно рассматриваться как вариант. Из трех методов экземпляра, упомянутых в предыдущем абзаце, в соответствии с вашим предыдущим опытом в определении событий метод экземпляра, как правило, определяет пару событий, как и подключаемость начальной загрузки. Например, метод рендеринга может определить рендеринг. Это событие запускается до выполнения основной логики рендеринга. Если внешний слушатель вызывает метод предотвращения () этого события, то основная логика рендеринга не будет выполнена; Существует также рендеринг. Преимущество этого события парного определения заключается в том, что оно не только предоставляет внешние методы для расширения функциональности компонентов, но также увеличивает управление поведением компонента по умолчанию.
Наконец, из моего предыдущего опыта работы, в дополнение к загрузке изображений для предварительного просмотра, я также сделал загрузку видео, загрузку звука, загрузку обычных документов и т. Д. Поэтому, когда я сталкивался с этой функцией на этот раз, я почувствовал, что я должен извлекать сходные вещи из этих функций в качестве базового класса, загрузки изображений, загрузки видео и т. Д., Соответственно унаследовать этот базовый класс. Другим преимуществом этого базового класса является то, что он может полностью отделить общую логику от структуры HTML. В этом базовом классе делаются только общие вещи, такие как определение вариантов и поведения компонентов (рендерин, добавление, делитеем), а также прослушивание и запуск общих событий. Он должен только оставить фиксированный интерфейс для реализации подкласса. В последующей реализации я определил компонент FileUploadBaseView для завершения функций этого базового класса. Этот базовый класс не содержит никакой логики для обработки HTML или CSS. Это просто абстрагирует функции, которые мы хотим выполнить, и не обрабатывает никакой бизнес -логики. Подклассы, внедренные в соответствии с бизнес -логикой, будут ограничены структурой HTML, поэтому объем применения подклассов невелик; А поскольку базовый класс полностью отделен от структуры HTML, он имеет больший объем применения.
3. Детали реализации
Из идеи реализации части 2 классы, которые будут реализованы: FileUploadBaseView и ImageUploadView, первый является базовым классом последнего. В то же время, учитывая, что компонент необходимо предоставить функции управления событиями, нам необходимо использовать EventBase.js в предыдущем блоге, а FileUploadBaseView должен наследовать компонент библиотеки; Учитывая, что существует определение класса и наследование, нам также необходимо использовать класс библиотеки наследования. Соотношение наследования связанных компонентов: ImageUploadView Extend FileUploadBaseView Event Eventbase.
(Примечание. Следующий связанный код использует SeaJs в модульности.)
Что делает FileUploadBaseView:
1) Определить общие варианты и общее управление событиями
В конфигурации по умолчанию этого компонента вы можете увидеть определения всех общих вариантов и общих событий:
var DEFAULTS = {data: [], //The list of data to be displayed must be of object type, such as [{url: 'xxx.png'},{url: 'yyyy.png'}]sizeLimit: 0, //To limit the number of elements displayed in BaseView, to 0, it means that it does not limit readonly: false, //To control whether elements in BaseView allow Добавление и удаление на ForeChererender: $ .noop, //forereport.fefore Event, Trigger Onrender: $ .noop, //forereport.fter событие до того, как вызовет метод рендеринга, и OnbeForeAppend: $ .noop, // Следует с приложением. // выполняется с событием Delitem.fore, запустите Ondelitem: $ .noop перед Delitem метод называется // выполняется с событием Delitem.fter, запускаемое};В методе инициации компонента вы можете увидеть логику инициализации для общего опциона и управления событиями:
init: function (element, options) {// Вызовите метод инициирования события родительского класса через это .base; // Атрибут экземпляра var opts = this.options = this.getoptions (options); this.data = Resolvedata (Opts.Data); Delete Opts.Data; this.sizelimit = opts.sizelimit; this.readonly = opts.readonly; // Связанное событие this.on ('render.fore', $ .proxy (opts.onbeforerender, this)); this.on ('render.fter', $ .proxy (Opts.onrender, это)); this.on ('append.fefore', $ .proxy (opts.onbeforeapend, это)); this.on ('append.fter', $ .proxy (opts.onappend, это)); this.on ('delitem.fefore', $ .proxy (opts.onbeforedelitem, это)); this.on ('delitem.fter', $ .proxy (opts.ondelitem, это));},2) Определите поведение компонентов и резервных интерфейсов, которые могут быть реализованы подклассами:
рендеринг: function () {/*** рендеринг - это шаблон. Подкласс не должен переопределять метод рендеринга, он должен только переопределять метод _render*, когда называется метод рендеринга подкласса, метод рендеринга родительского класса называется*, но при выполнении метода _render метод _render подкласса называется* Это будет объединять операции запуска до событий после событий*/var; this.trigger (e = $ .event ('render.fofore')); if (e.isdefaultprevented ()) return; this._render (); this.trigger ($. event ('render.after'));}, // Подклассы должны реализовать _render method_render: function () {}, приложение: функция (элемент) {var e; if (! item) return; item = resolvedataitem (item); this.trigger (e = $ .event ('append.before'); return; this.data.push (item); this._append (item); this.trigger ($. event ('append.after'), item);}, // Подклассы должны реализовать _append method_append: function (data) {}, delitem: function (uuid) {var e, item. item.getDataitem (uuid) = $ .Event ('delitem.before'), item); if (e.isdefaultprevented ()) return; this.data.splice (this.getDataiteMindex (uuid), 1); this._delitem (item); this.trigger ($. method_delitem: function (data) {}Чтобы равномерно обрабатывать логику распределения событий до и после поведения, основная логика рендеринга, добавление, Delitem извлекается в методы _render, _Append и _delitem, которые необходимо реализовать подклассами. Когда вызывается метод рендеринга подкласса, на самом деле вызывается метод родительского класса, но когда родительский класс выполняет метод _render, выполняется метод подкласса, а два других метода также аналогичны. Следует отметить, что подклассы не могут переопределить три метода рендеринга, добавления и delitem, в противном случае вы должны обрабатывать логику триггеров связанных событий самостоятельно.
Общая реализация FileUploadBaseView заключается в следующем:
Определите (function (require, exports, module) {var $ = require ('jquery'); var class = require ('mod/class'); var eventbase = require ('mod/eventbase'); var default 'yyyy.png'}] sizelimit: 0, // Ограничение количества элементов, отображаемых в Baseview, если 0 означает, что нет ограничения Readonly: false, // контролировать, разрешено ли элементы в Baseview и удалить и удалить OnbeForerender: $ .noop, // Соответствующий Render. OnbeForeAppend: $ .noop, // Соответствующее append.fefore Event, Trigger Onappend: $ .noop, // Соответствующее событие Append.after, триггер OnbeForedElitem: $ .noop, // Соответствующее событие Append.after, триггер OnbeForeDeLITEM: $ .noop, // Cropersing Append.after Event.after Event.after, Trigger -on. delitem.fefore Event, Trigger Ondelitem: $ .noop // Соответствующее событие Delitem.fter, триггер};/*** Обработка данных, добавьте атрибут _uuid в каждую запись данных для облегчения поиска*/function resolvedata (data) {var = новая дата (). Time);});} Function ResolvedAtaitem (data, time) {time = time || new Date (). getTime (); data._uuid = '_uuid' + time + math.floor (math.random () * 100000); return Data;} varuploadView = callas Class Event Base через это. $ .proxy (opts.onbeforerender, это); this.on ('delitem.fefore', $ .proxy (opts.onbeforedelitem, this)); По умолчанию;}, getDataitem: function (uuid) {// получить dateItemreturn this.data.filter (function (item) {return item._uuid === uuid;}) [0];}, getDataiteMindex: function (uuid) {var ret; this.Data.ForeAge (item) {uuid) {var ret; this.data.foreach (item) {uuid) {var ret; && (ret = i);}); return ret;}, рендеринг: function () {/*** рендер является шаблоном. Запуск операций до и после событий*/var e; {var e; if (! item) return; item = resolvedataitem (item); this.trigger (e = $ .event ('append.fore'), item); if (e.isdefaultprevented ()) return; this.data.push (item); this._append (item); this.trigger ($. _Append method_append: function (data) {}, delitem: function (uuid) {var e, item = this.getDataitem (uuid); if (! item) return; this.trigger (e = $. return; this.data.splice (this.getDataiteMindex (uuid), 1); this._delitem (item); this.trigger ($. {По умолчанию: по умолчанию}}); return fileuploadbaseview;});Реализация ImageUploadView относительно проста, похожая на заполнение пробелов. Есть несколько моментов, чтобы объяснить:
1) по умолчанию этого класса необходимо расширить дефолты родительского класса, чтобы добавить параметры по умолчанию этого подкласса, а также сохранить определение параметров по умолчанию родительского класса; Согласно структуре статической страницы, было добавлено событие OnappendClick, и соответствующие методы компонентов загрузки файлов могут быть вызваны внешне в этом событии:
// наследуйте и расширяйте по умолчанию по умолчанию. Веса родительского класса defaults = $.
2) в методе init метод init родительского класса должен быть вызван для завершения этой общей логической обработки; В конце концов, вы должны вручную вызвать метод рендеринга, чтобы вы могли видеть эффект после создания компонента:
Другие реализации являются чисто внедрением бизнес -логики и тесно связаны с требованиями части 2.
Общая реализация ImageUploadView выглядит следующим образом:
Определите (function (require, exports, module) {var $ = require ('jquery'); var class = require ('mod/class'); var fileuploadbaseview = require ('mod/fileuploadbaseview'); // наследство и расширение defaults defaultsvar =. {onAppendClick: $ .noop // обратный вызов при нажатии кнопки загрузки}); var ImageUploadView = class ({instancemembers: {init: function (элемент, опции) {var $ element = this. $ element = $ (элемент); var posts = this.getoptions (опции); // Вызовы метод инициации. this.base(this.$element, options);//Add the upload and delete listeners and trigger processing if (!this.readOnly) {var that = this;that.on('appendClick', $.proxy(opts.onAppendClick, this));$element.on('click.append', '.view-act-add', function (e) {e.preventDefault(); that.trigger ('appendclick');}); $ element.on ('click.remove', '.view-act-del', function (e) {var $ this = $ (e.currenttarget); that.delitem ($ this.data ('uuid'); e.preventdefault ();); {return defalault;}, _ setIteMaddhtml: function () {this. $ element.prepend ($ ('<li> <a href = "javascript :;">+</a> </li>'));}, _ realytemaddhtml: function ($ itemaddli) {$ item. function () {var html = [], что = this; // Если оно не является состоянием только для чтения и не достиг предела загрузки, добавьте кнопку загрузки if (! {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? это. $ element.find ('li.view-item-add'); // Если предел загрузки был достигнут, удалите кнопку загрузки if (this.sizelimit && this.sizelimit <= this.data.length && $ itemaddli.length) {this._clearitemaddhtml ($ itemaddli); (! $ itemaddli.length) {this._setitemaddhtml ();}}}, _ Приложение: function (data) {this. $ element.append ($ (this._getitemrenderhtml (data)); this._dealwithsizelimit (); data._uuid) .remove (); this._dealwithsizelimit ();}}, extend: fileuploadbaseview});4. Демо -инструкции
Демонстрационная структура проекта:
То, что создано, является основным кодом демонстрации. Среди них FileUploadBaserview.js и ImageUploadView.js являются двумя основными компонентами, реализованными в предыдущей реализации. fileuploader.js используется для моделирования загрузки компонентов. Его экземпляр имеет обратный вызов ONSUCCESS, указывающий, что загрузка успешна; Существует также OpenChoosefilewin, используемый для моделирования процесса открытия окна файла выбора и его загрузки:
Определите (function (require, exports, module) {return function () {var imglist = ['../img/1.jpg', ’../img/2.jpg','../img/3.jpg','../Img/4.jpg'], i = 0; var = this; ononsuccess = function (uploadval) {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{wase wase woseench function () {settimeout (function () {that.onsuccess (imglist [i ++]); if (i == imglist.length) {i = 0;}}, 1000);}}});app/regist.js - логический код демонстрационной страницы, а ключевые части были объяснены комментариями:
Определить (функция (require, exports, module) {var $ = require ('jquery'); var imageUploadView = require ('mod/imageUploadView'); var fileUploader = require ('mod/fileUploader'); // Это компонент для загрузки файла с помощью Asynchronous tasks // $ LegalPersonIddPic, который используется в подготовке к нагрузке. успешно загружен, и после того, как компонент ImageUploadView удаляет определенный элемент, это повлияет на стоимость $ LegalPersonDpic var $ LegalPersonDpic = $ ('#LegalPersonPic-Input'), Data = json.parse ($ LegalPersonIdpic.val () || С помощью компонента ImageUploadView // после успешной загрузки файла сохранена вновь загруженный файл до значения $ LegalPersonIdpic // $ LegalPersonIdpic сохраняет var AppendImageInputValue = function ($ input, item) {var value = json.parse ($ input.val () || '[]'); value.push (item); $ input.val (json.stringify (value));}; // Когда компонент ImageUploadView вызывается для удаления элемента, хранящаяся информация в $ LegalPersonDpic должна быть очищена синхронически. '[]'), index; value.foreach (function (item, i) {if (item._uuid === uuid) {index = i;}}); value.splice (index, 1); $ input.val (json.stringify (value));}; varuploader = newululader (uploader); {var item = {url: uploadValue}; LegalPersonIdpicView.append (item); AppendImageInputValue ($ LegalPersonIdpic, item);}; var LegalPersonPicView = new ImagePloadView ('#LegalPersonDIDPIC-View', {Data: Data: SizelImit: 4, On ApperSclclclclclclclclclclclclyclclclclick: Выберите файл fileuploader.openchosefilewin ();}, ondelitem: function (data) {removeImageInputValue ($ LegalPersonIdpic, data._uuid);}});});5. Резюме этой статьи
Компонент ImageUploadView в конце концов не сложно реализовать, но я также потратил много времени на размышления об этом и других родительских классах для его реализации, и большую часть времени была потрачена на абстракцию разделения обязанностей и поведенческого разделения. Взгляды на эти два аспекта идеи программирования, выраженные в этой статье, являются лишь моим личным опытом. Из -за абстрактного уровня методы мышления каждого и окончательное понимание не будут одинаковыми. Поэтому я не могу напрямую сказать, прав ли я или неправильно. Цель письма - поделиться и общаться, и посмотреть, есть ли другие опытные друзья, которые готовы рассказать вам о своих идеях в этом отношении. Я считаю, что после того, как все слишком много читали об идеях других людей, они также помогут своим собственным идеям программирования.