이 기사에서는 웹 페이지의 공통 이미지의 구현 아이디어를 소개하여 업로드 한 후 페이지에서 작은 이미지 미리보기를 직접 생성합니다. 이 함수에 특정 적용 가능성이 있다는 것을 고려하면 관련 로직이 ImageUploadView 구성 요소에 캡슐화됩니다. 실제 사용 효과는 다음 단락에서 git 렌더링을 볼 수 있습니다. 이 구성 요소를 구현하는 과정에서, 우리는 상속 도서관 class.js와 같은 이전 블로그에 소개 된 관련 컨텐츠와 모든 구성 요소의 이벤트 관리 라이브러리 이벤트베이스. 읽고 의사 소통하는 데 오신 것을 환영합니다.
데모 효과 :
참고 : 데모 코드는 모두 정적이므로 파일에 의해 업로드 된 구성 요소는 Settimeout으로 시뮬레이션되지만 호출 방법은 실제 작업에서 업로드 구성 요소를 사용할 때와 정확히 동일하므로 데모 효과의 코드 구현은 실제 기능 요구 사항과 완전히 일치합니다.
이전 블로그 아이디어에 따르면 먼저이 업로드 미리보기 기능의 요구 사항을 소개하겠습니다.
1. 요구 사항 분석
이전 데모 렌더링에 따르면 분석 요구 사항은 다음과 같습니다.
1) 초기 업로드 영역에서 클릭 가능한 업로드 버튼 하나만 표시됩니다. 이 버튼을 클릭하면 업로드 된 이미지가 후속 미리보기 영역에 표시됩니다.
2) 업로드 된 이미지가 미리보기 영역에 추가 된 후 버튼을 삭제하여 제거 할 수 있습니다.
3) 업로드 된 이미지의 총 수가 특정 제한에 도달하면 데모의 업로드 한계는 4이면 업로드 버튼을 제거하십시오.
4) 업로드 된 사진의 총 수가 특정 제한에 도달하면 삭제 작업을 통해 특정 사진이 제거되면 업로드 버튼을 표시해야합니다.
위의 요구 사항이 표시됩니다. 경험을 바탕으로 분석 할 수있는 요구 사항은 다음과 같습니다.
1) 페이지가 편집 상태에있는 경우, 즉 이미지 목록이 비어 있지 않은 한 데이터베이스에서 쿼리 된 상태가있는 경우 이미지는 처음에 표시되어야합니다. 또한 발견 된 이미지 목록의 길이와 업로드 제한에 따라 업로드 버튼이 표시되는지 여부도 제어해야합니다.
2) 현재 페이지가 볼 수 있지만 변경되지 않은 상태 인 경우 업로드 버튼과 삭제 버튼을 초기 단계에서 제거해야합니다.
요구 사항 분석이 완료되면 구현 아이디어를 설명하겠습니다.
2. 구현 아이디어
이 페이지는 폼 페이지이므로 이미지를 업로드 한 후 배경에 제출하려면 텍스트 필드가 필요합니다. 그래서 정적 페이지를 수행 할 때이 텍스트 필드를 고려했습니다. 새 이미지를 업로드하고 이미지를 삭제 한 후이 텍스트 필드의 값을 수정해야했습니다. 정적 페이지가 만들어 졌을 때이 부분의 구조는 다음과 같습니다.
<div> <lable> 법적 인원 ID 카드의 전자 버전 </label> <div> <입력 ID = "julegpersonIdpic-input"name = "legalpersonIdpic"type = "hidden"> ul id = "legalpersonIdpic-view"> <li> <a href = "javaScript :;"+</li> 식별 가능한 <a href = "#"> <i> </i> 예시보기 </a> </p> </div> </div>
이 구조에서 전체 업로드 영역을 하나의 UL에 넣은 다음 첫 번째 UL Li를 업로드 버튼으로 사용하는 것을 알 수 있습니다. 이 기능을 완료하려면 주요 작업은 다음과 같습니다. 콜백 업로드 및 업로드, 이미지 미리보기 추가 또는 삭제 및 텍스트 필드 값 관리입니다. 이 관점에서 책임 분리에 대한 아이디어와 결합 하여이 기능에는 최소한 세 가지 구성 요소, 하나는 파일 업로드를 담당하고 이미지 미리 관리 담당자 및 텍스트 도메인 값 관리를 담당합니다. 이 세 가지 기능을 쌍으로 또는 모두 함께 캡슐화하지 마십시오. 이 경우 기능적 커플 링이 너무 강하고 작성된 구성 요소는 확장 가능하고 재사용 가능합니다. 이 세 가지 구성 요소간에 상호 작용이 필요한 경우 콜백 함수 또는 게시-구독 모드를 사용하여 외부로 호출되는 인터페이스로 정의하면됩니다.
그러나 텍스트 도메인 값의 관리는 매우 간단하며 구성 요소로 작성되었는지 여부는 중요하지 않지만 최소한 기능 수준 캡슐화가 필요합니다. 파일 업로드 구성 요소는이 기사의 초점이 아니지만 WebUploader와 같은 인터넷에는 직접 사용되거나 보조 캡슐화에 관계없이 적용 할 수있는 많은 기성품 오픈 소스 플러그인이 있습니다. 이미지 미리보기의 기능은이 기사의 핵심 내용입니다. ImageUploadView 구성 요소는 캡슐화입니다. 요구 사항 관점 에서이 구성 요소에 대한 세 가지 의미 론적 예제, 즉 렌더링, 추가 및 델리 템이 있습니다. 렌더는 초기화가 완료된 후 초기 미리보기 목록을 표시하는 데 사용되며 Append는 업로드 후 새 이미지 미리보기를 추가하는 데 사용되며 Delitem은 기존 이미지 미리보기를 삭제하는 데 사용됩니다. 이 기본 아이디어에 따르면, 우리는 구성 요소 개발의 요구 사항과 경험을 바탕으로 옵션과 이벤트 만 설계하면됩니다.
이전 요구 사항 에서이 ImageUploadView 구성 요소의 렌더가 페이지 상태의 영향을받는 것으로 나타났습니다. 페이지가보기 모드에 있으면이 구성 요소는 업로드 및 삭제할 수 없으므로 Readonly 옵션을 추가하는 것을 고려할 수 있습니다. 동시에 업로드 및 삭제 작업은 업로드 제한과 관련된 업로드 버튼의 UI 논리에도 영향을 미칩니다. 유연성을 위해 업로드 한도도 옵션으로 간주되어야합니다. 이전 단락에 언급 된 세 가지 인스턴스 방법에서 이벤트 정의에 대한 이전 경험에 따르면 인스턴스 방법은 일반적으로 부트 스트랩의 플러그인과 마찬가지로 한 쌍의 이벤트를 정의합니다. 예를 들어, 렌더 메소드는 렌더를 정의 할 수 있습니다. 이 이벤트는 렌더의 주요 논리가 실행되기 전에 트리거됩니다. 외부 청취자 가이 이벤트의 extendefault () 메소드를 호출하면 렌더의 주요 논리가 실행되지 않습니다. 렌더도 이벤트 후에도 렌더의 주요 논리가 실행 된 후 트리거됩니다. 이 쌍 정의 정의 이벤트의 장점은 구성 요소 기능을 확장하기위한 외부 메소드를 제공 할뿐만 아니라 구성 요소 기본 동작의 관리를 향상 시킨다는 것입니다.
마지막으로, 이전 작업 경험에서 미리보기 사진 업로드 외에도 비디오 업로드, 오디오 업로드, 일반 문서 업로드 등도이 기능을 수행했을 때이 기능을 각각 기본 클래스, 이미지 업로드, 비디오 업로드 등으로 추출해야한다고 생각했습니다. 이 기본 클래스의 또 다른 장점은 일반 논리를 HTML 구조와 완전히 분리 할 수 있다는 것입니다. 이 기본 클래스에서는 옵션 및 구성 요소 동작의 정의 (렌더링, 부록, 델리 템), 일반 이벤트의 청취 및 트리거와 같은 일반적인 일만 수행됩니다. 서브 클래스가 구현하려면 고정 된 인터페이스 만 남겨야합니다. 후속 구현 에서이 기본 클래스의 함수를 완성하기 위해 FileUploadBaseView 구성 요소를 정의했습니다. 이 기본 클래스에는 HTML 또는 CSS 처리에 대한 논리가 포함되어 있지 않습니다. 그것은 우리가 완료하고자하는 기능을 추상화하고 비즈니스 논리를 처리하지 않습니다. 비즈니스 로직에 따라 구현 된 서브 클래스는 HTML 구조에 의해 제한되므로 서브 클래스의 적용 범위는 작습니다. 기본 클래스는 HTML 구조와 완전히 분리되어 있기 때문에 응용 범위가 더 큽니다.
3. 구현 세부 사항
Part 2의 구현 아이디어에서 구현 될 클래스는 다음과 같습니다. FileUploadBaseView 및 ImageUploadView, 전자는 후자의 기본 클래스입니다. 동시에, 구성 요소가 이벤트 관리 기능을 제공해야한다는 점을 고려할 때 이전 블로그에서 eventBase.js를 사용해야하며 FileUploadBaseView는 라이브러리의 eventbase 구성 요소를 상속해야합니다. 클래스 정의와 상속이 있다는 것을 고려할 때, 우리는 구성 요소의 구성 요소와 상속 관계를 정의하기 위해 작성하기 전에 작성된 상속 라이브러리 클래스를 사용해야합니다. 관련 구성 요소의 상속 관계는 다음과 같습니다. ImageUploadView 확장 FileUploadBaseView EventBase.
(참고 : 다음 관련 코드는 Modularity에서 SEAJ를 사용합니다.)
FileUploadBaseView는 다음과 같습니다.
1) 일반적인 옵션 및 공통 이벤트 관리를 정의하십시오
이 구성 요소의 기본 구성에서 모든 일반적인 옵션 및 공통 이벤트의 정의를 볼 수 있습니다.
var defaults = {data : [], // 표시 될 데이터 목록은 [{url : 'xxx.png'}, {url : 'yyyy.png'}] sizelimit : 0, //와 같은 객체 유형이어야합니다. 기본 View에 표시되는 요소의 수를 제한하지 않음을 의미합니다. 추가 및 삭제 onbebeforerender : $ .noop, //forereport.before event, render : $ .noop, //forereport.fter render method가 호출되기 전에 이벤트 및 onbeforeappend : $ .noop, // append.noop, // 호환 된 이벤트를 준수했습니다. // delitem.crompedirected event, trigger ondelitem : $ .noop 전에 delitem method가 delitem.fter 이벤트, 트리거 된}를 준수하기 전에 트리거 ondelitem을 트리거합니다.구성 요소의 init 메소드에서 일반 옵션 및 이벤트 관리에 대한 초기화 로직을 볼 수 있습니다.
init : function (요소, 옵션) {// this.base; // 인스턴스 속성 var opts = this.options = this.getOptions (옵션); this.data = resolvedata (opts.data); opts.data 삭제; this.sizelimit = opts.sizelimit; this.readonly = opts.readonly; // 바인딩 이벤트 this.on ( 'render.before', $ .proxy (opts.onbeforerender, this)); this.on ( 'render.apter', $ .proxy (opts.onrender, this)); this.on ( '부록. this.on ( 'append.after', $ .proxy (opts.onappend, this)); this.on ( 'delitem.before', $ .proxy (opts.onbeforedelitem, this)); this.on ( 'delitem.after', $ .proxy (opts.ondelitem, this));},2) 서브 클래스로 구현할 수있는 구성 요소 및 예비 인터페이스의 동작을 정의하십시오.
렌더 : function () {/*** 렌더는 템플릿입니다. 서브 클래스는 렌더 메소드를 무시할 필요가 없으며, 서브 클래스의 렌더 메소드를 호출 할 때 _render 메소드*를 무시하면 부모 클래스의 렌더 메소드가*라고 불리면 _Render 메소드를 실행할 때 서브 클래스의 _Render 메소드가 이전 및 이벤트의 트리거 작업을 통제합니다.*/var e; this.trigger (e = $ .event ( 'render.before')); if (e.isdefaultPrevented ()) reto; this._render (); this.trigger ($. event ( 'render.after'));}, // 서브 클래스는 _render method_render : function () {}, append : function (item) {var e; if (! item) return; item = resolvedAtaitem (e = $. event ( 'acpend.becore'); return; this.data.push (항목); this._append (item); this.trigger ($. event ( 'append.after'), item);}, // 하위 클래스는 _append method_append : function (data) {}, delitem : function (var e, item = this); if (var e.); $ .Event ( 'delitem.before'), item); if (e.isdefaultprevented ()) return; this.data.splice (this.getDataitemIndex (uuid), 1); this._delitem (item); this.trigger ( 'delitem.af'), item), // subclass re ke re king to refte to ruited re kuite to ruited regublass to re undriger (this.the.taitemindex (uuid), 1); method_delitem : 함수 (data) {}동작 전후에 이벤트 분포 로직을 균일하게 처리하기 위해 렌더링, 부록, 델리 템의 주요 논리는 서브 클래스에 의해 구현되어야하는 메소드, _append 및 _delitem으로 추출됩니다. 서브 클래스의 렌더 메소드가 호출되면, 상위 클래스의 메소드가 실제로 호출되지만, 상위 클래스가 _Render 메소드를 실행하면 서브 클래스의 메소드가 실행되고 다른 두 가지 방법도 유사합니다. 서브 클래스는 세 가지 메소드 렌더링, 추가 및 삭제를 무시할 수 없으며, 그렇지 않으면 관련 이벤트의 트리거 논리를 직접 처리해야합니다.
FileUploadBaseView의 전체 구현은 다음과 같습니다.
define (함수 (요구, 내보내기, 모듈) {var $ = require ( 'jQuery'); var class = require ( 'mod/class'); var eventbase = require ( 'mod/eventbase'); var defaults = {data : [], // 목록 요소는 [url : 'url :'xx.png '}, {url. 'yyyy.png'}] sizelimit : 0, // baseview에 표시된 요소의 수를 제한하기 위해, 기본 뷰의 요소를 제한하는 경우 // onbeforerender의 요소를 추가하고 삭제할 수 있는지 여부를 제어하기 위해 // // 해당 렌더링. onbeforeappend : $ .noop, // 해당 부록. 이벤트, 이벤트, onappend : $ .noop, // 해당 부록, 이벤트, onbeforedElitem : $ .noop, // 해당 부록을 트리거합니다. 이벤트, $ .noop, // 해당 부록 이벤트 트리거 delitem.cefore endelitem : $ .noop // 해당 delitem.after 이벤트, 트리거};/*** 데이터 처리, 각 데이터 레코드에 _uuid 속성을 추가하여*/function resolvedata (data) {var time = new date (); get $ .map (data, function, function) time);});} 함수 resolvedataitem (data, time) {time = time || this.base. $ .Proxy (Opts.onbeforerender); this.on ( 'delitem.before', $ .proxy (opts.onforedelitem, this); defaults;}, getDataitem : function (uuid) {// getitemreturn this.data.filter (function (item) {return item._uuid === uuid;}) [0];}, getDataitemIndex : function (uuid) {var ret; this.data.foreach (item, i) el it). (ret = i);}); return ret;}, render : function () {/*** 렌더는 서브 클래스가 렌더 메소드를 무시할 필요가 없으며, 서브 클래스 렌더 메소드가 호출 될 때만 _Render 클래스의 렌더 메소드가 호출 될 때, _Render의 _Render는* _Render의 _Render 방법을 통합 할 때. 이벤트 후*/var e. thregger (e = $ .event ( 'render.fore')); if (e.isdefaultprevented ()); return; item = resolvedAtaitem (item); this.trigger (e = $ .event ( 'append.fore'), item); if (e.isdefaultPrevented ()) retur; this.data.push (item); this.tigger ( 'actubter ('부록)), // subclass가 refind to the <append : // subclass가 refort _}; (data) {}, delitem : function (uuid) {var e, item = this.getDataitem (uuid); if (! item) return; this.trigger (e = $ .event ( 'delitem.before'), item); if (e.isdefaultprevented ()) return; 1); this._delitem (item); this.trigger ($. 이벤트 ( 'delitem.after'), item);}, // 서브 클래스는 _delitem method_delitem : function (data) {}}, extend : eventbase, staticmbers : {defaults});imageUploadView의 구현은 빈칸을 채우는 것과 비슷한 비교적 간단합니다. 설명해야 할 몇 가지 요점이 있습니다.
1)이 클래스의 기본값은 부모 클래스의 기본값을 확장 하여이 서브 클래스의 기본 옵션을 추가하고 부모 클래스의 기본 옵션의 정의를 유지해야합니다. 정적 페이지 구조에 따르면, onappendClick 이벤트가 추가되었으며,이 경우 관련 파일 업로드 구성 요소의 관련 방법을 외부에서 호출 할 수 있습니다.
// 부모 클래스 기본값의 기본 기본값을 상속하고 확장합니다. 기본값 = $ .Extend ({}, fileUploadBaseView.Defaults, {onappendClick : $ .noop // 업로드 버튼을 클릭하면});2) init 방법에서, 일반적인 논리적 처리를 완료하기 위해 부모 클래스의 초기 방법을 호출해야한다. Init가 끝나면 구성 요소가 인스턴스화 된 후 효과를 볼 수 있도록 렌더 메소드를 수동으로 호출해야합니다.
다른 구현은 순전히 비즈니스 로직 구현이며 Part 2의 요구 사항과 밀접한 관련이 있습니다.
ImageUploadView의 전체 구현은 다음과 같습니다.
define (function (요구, 내보내기, 모듈) {var $ = require ( 'jQuery'); var class = require ( 'mod/class'); var fileUploadBaseView = require ( 'mod/fileUploadBaseView'); // defaultsvar defaults = $. extend ({}, fileUploadBiew.defaUnts) {onappendClick : $ .noop // 업로드 버튼을 클릭 할 때 콜백}); var imageUploadView = class ({instancemembers : {init : function (element, 옵션) {var $ element = this. $ element = $ (요소); var opts = this.getOptions (getOptions); this.base (this. $ 요소, 옵션); // 업로드를 추가하고 리스너를 삭제하고 (! this.readonly) {var that = that; that.on ( 'appendclick', $ .onappendClick, this)); $ element.on ( 'click.append', '.view-act-add'; that.trigger ( 'appendclick');}); $ element.on ( 'click.remove', '.view-act-del', function (e) {var $ this = $ (e.currentTarget); that.data ( 'uuid')); e.preventDefault (); {return defaults;}, _ setitemaddhtml : function () {this. $ element.prepend ($ ( '<li> <a href = "javaScript :;">+</a> </li>');}, _ clearItemaddhtml : function ($ itemAddli) _); () {var html = [], that = this = this; this; this; this; // 업로드 제한에 도달하지 않은 경우 업로드 버튼을 추가하면 (! this.readonly || (this.sizelimit && this.sizelimit <= this.data.length)))) {this.data (this.data) {html.push (that._getItemrenderhtml (item))}); this. $ element.append ($ (html.join ( '' ')));}, _ getItemrenderhtml : function (item) {return ['<li id = " ', item._uuid,'"> src = " ', item.url,'", this. this.leadonly? this. $ element.find ( 'li.view-item-add'); // 업로드 제한에 도달하면 업로드 버튼을 제거하면 (this.sizelimit && this.sizelimit <= this.data.length && $ itemaddli.length) {this._clearitemaddhtml ($ itemaddli); if (! {this data._uuid) .remove (); this._dealwithSizelimit ()}, extend : fileUploadBaseView});4. 데모 지침
데모 프로젝트 구조는 다음과 같습니다.
프레임은 데모의 핵심 코드입니다. 그중에서도 FileUploadBaserview.js 및 ImageUploadView.js는 이전 구현에서 구현 된 두 가지 핵심 구성 요소입니다. FileUploader.js는 업로드 구성 요소를 시뮬레이션하는 데 사용됩니다. 인스턴스에는 onsuccess 콜백이있어 업로드가 성공했음을 나타냅니다. 선택 파일 창을 열고 업로드하는 프로세스를 시뮬레이션하는 데 사용되는 OpenChooseFileWin도 있습니다.
define (function (요구, 내보내기, 모듈) {return function () {var imglist = [ '../img/1.jpg' ,'../img/2.jpg','/img/3.jpg' ,'../img/4.jpg '], i = 0; var that = this; that.onsuccess = function (voplovevalue) function () {settimeout (function () {that.onsuccess (imglist [i ++]); if (i == imglist.length) {i = 0;}}, 1000);}});app/regist.js는 데모 페이지의 논리 코드이며 주요 부분은 주석으로 설명되었습니다.
define (function (요구, 내보내기, 모듈) {var $ = require ( 'jQuery'); var imageUploadView = require ( 'mod/imageUploadView'); var fileUploader = require ( 'mod/fileUploader'); // 이것은 Synchronous Tasks에서 시뮬레이션 한 파일 업로드 구성 요소 // $ legalpersonIdpic에 사용되는 파일 업로드 구성 요소입니다. 성공적으로 업로드하고 ImageUploadView 구성 요소가 특정 항목을 삭제 한 후에는 $ legalpersonIdpic var $ legalpersonIdpic = $ ( '#legalpersonIdpic-input'), data = json.parse ($ legalpersonIdpic.val () [] ')의 값에 영향을 미칩니다. imageUploadView 구성 요소 // 파일 업로드가 성공적으로 업로드 된 후 새로 업로드 된 파일을 $ legalpersonIdpic // $ legalpersonIdpic smores var actendimageInputValue = function ($ input, item) {var value = json.parse ($ input.val () || '[]'); value.push (item); $ input.val (json.stringify (value));}; // ImageUploadView 구성 요소가 항목을 삭제하도록 호출되면 $ legalpersonIdpic에 저장된 정보는 동기식으로 지워야합니다. '[]'), index; value.foreach (function (item, i) {if (item._uuid === uuid) {index = i;}}); value.splice (index, 1); $ input.val (json.stringify (value));}; var fileUploader = new FileUPloader (); fileUploader.onsucces.onupploader. 항목 = {url : uploadValue}; legalpersonIdpicView.Append (item); AppendImageInputValue ($ legalpersonIdpic, item);}; var legalpersonIdpicView = new ImageUploadView ( '#legalpersonIdpic-view', {data : 4 onependClick : on eperning (function) (function), function (function) : 4, open file fileUploader.openChooseFileWin ();}, ondelitem : function (data) {removePersonIdPic, data._uuid);});});5.이 기사의 요약
ImageUploadView 구성 요소는 결국 구현하기가 어렵지 않지만,이를 구현하기 위해 그것과 다른 부모 수업에 대해 많은 시간을 보냈으며, 대부분의 시간은 책임의 분리와 행동 분리를 추상화하는 데 소비되었습니다. 이 기사에 표현 된 프로그래밍 아이디어 의이 두 가지 측면에 대한 견해는 저의 개인적인 경험 일뿐입니다. 추상적 수준으로 인해 모든 사람의 사고 방법과 최종 이해는 동일하지 않습니다. 그러므로 나는 내가 옳고 그름인지 직접 말할 수 없습니다. 글쓰기의 목적은 공유하고 의사 소통하는 것입니다. 나는 모든 사람들이 다른 사람들의 아이디어에 대해 너무 많이 읽은 후에도 자신의 프로그래밍 아이디어 훈련에도 도움이 될 것이라고 생각합니다.