The idea of separation and inheritance realize the preview function after uploading Picture: Imageuploadview_javascript skill

Source: Internet
Author: User
Tags eventbase instance method uuid

This article is to introduce the Web page is a common picture upload directly in the page to generate small map preview of the implementation of ideas, taking into account the function has a certain applicability, so the relevant logic encapsulated into a imageuploadview component, the actual use of the effect can see the next section of git effect map. In the implementation of this component, useful to the previous blog introduction of the relevant content, such as the inheritance library Class.js, any component of the Event Management library Eventbase.js, while including their separation of responsibilities, performance and behavior of the separation of the two aspects of thinking, welcome to read and exchange.

Demo Effect:

Note: Because the demo code is static, so the file upload component is simulated with settimeout, but it's called the way I do in the actual work with the upload component exactly the same, so the demo effect of the code to achieve full compliance with the real functional requirements.

According to my previous blog ideas, first to introduce the requirements of the upload preview function.

1. Demand Analysis

According to the previous demo effect diagram, the analysis requirements are as follows:

1 The initial upload area only shows a clickable upload button, when the button is clicked, upload the successful picture displayed in the following preview area

2 After the uploaded picture is added to the preview area, you can remove it by removing the button

3 when the total number of uploaded pictures reaches a certain limit, such as the limit of 4 in the demo, the upload button is removed;

4 when the total number of uploaded pictures reaches a certain limit, if you remove a picture by removing the operation, you have to show the upload button again.

The above requirements are seen, according to experience, you can also analyze the requirements as follows:

1 if the page is the editing state, that is, from the database query out of the state, as long as the picture list is not empty, the initial picture should also be displayed, but also according to the length of the list of pictures and upload restrictions to control the upload button is displayed;

2 If the current page is a can only see can not change the state, then in the initial must be the upload button and delete button removed.

Demand analysis finished, then explain my idea of implementation.

2. Realize the idea

Since this is a form page, so after uploading the picture, if you want to submit to the background, definitely need a text field, so I do the static page when the text field into consideration, when the upload of new pictures and delete the picture after all have to modify the value of this text field. The structure of this section when doing static pages is as follows:

<div class= "Holy-layout-am appform-group appform-group-img-upload clearfix" > <label "
Holy-layout-al "> Corporate identity Card electronic version </label>
<div class=" holy-layout-m ">
<input id=" Legalpersonidpic-input "Name=" Legalpersonidpic "class=" Form-control Form-field "type=" hidden "
>
<ul Id= "Legalpersonidpic-view" class= "Image-upload-view clearfix" >
<li class= "View-item-add" ><a "View-act-add" href= "javascript:;" title= "click Upload" >+</a>
</li>
</ul>
<p class= " Img-upload-msg ">
make sure the picture is clear, the text can be distinguished
<a href=" # "title=" View Sample "><i class=" fa fa-question-circle "> </i> View Samples </a>
</p>
</div>
</div>

From this structure can also be seen, I put the entire upload area in a UL inside, and then the first of the UL Li as an upload button to use. In order to complete this function, our main task is: Upload and upload the callback, add or remove picture preview and the Text field value management. From this point, combined with the idea of separation of responsibilities, this feature requires at least three components, one responsible for file upload, one responsible for picture preview management, and one responsible for the management of text field values. Do not put these three functions, 22 or all packaged together, so that the function coupling is too strong, the written component scalability reusability is not high. If the three components need to interact, we simply define the interfaces they give to the external invocation with the help of a callback function or a publish-subscribe pattern.

However, the management of text field values is inherently simple, writing is not written as a component is not very related, but at least the function level of the package is available; File Upload component is not the focus of this article, but there are many out-of-the-box open source plug-ins, such as Webuploader, either directly or do two times encapsulation can be applied in , the function of the picture preview is the core content of this article, Imageuploadview this component is to its encapsulation, from the demand, this component has the semantic instance method to be three, respectively is render, append, Delitem, Where render is used to display the initial preview list after the initialization is completed, append is used to add a new picture preview after the upload is successful, delitem to delete the existing picture preview, according to this basic idea, We just need to combine the experience of requirements and component development to design options and events for it.

From the previous requirements we found that the render of this Imageuploadview component will be affected by the page state, when the page is viewing mode, this component can not do upload and delete operations, so you can consider to add a readonly option. At the same time its upload and delete operations will also affect the Upload button UI logic, which is related to the upload restrictions, in order to be flexible, but also to upload restrictions as an option. From the three instance methods mentioned earlier, in terms of their previous experience defining events, a general instance method would define a pair of events, like the Bootstrap plug-in approach, such as the Render method, You can define a Render.before event that fires before render's main logic executes, and if the external listener invokes the Preventdefault () method of the event, then the primary logic of render does not execute ; There is also a render.after event that fires after the main logic of the render is executed. The benefit of this pair-defining event is that it provides an external way to extend the functionality of the component and also to increase the management of the default behavior of the component.

Finally, from my previous work experience, in addition to uploading pictures to preview such functions, I have also done upload video, upload audio, upload common documents and similar, so this time when I came across this feature should be similar to these features extracted, as a base class, picture upload, Video upload, etc. inherit this base class separately to realize their own logic. The benefit of this base class is that it allows the generic logic to be completely detached from the HTML structure, where only common things such as options and component behavior (render, append, delitem) are defined, and common events are monitored and triggered, It simply leaves a fixed interface for the subclass to implement. In the following implementation, I defined a Fileuploadbaseview component to complete the functionality of this base class, which does not contain any HTML or CSS processing logic, but simply abstracts the functionality we want to accomplish and does not deal with any business logic. Subclasses implemented according to business logic are limited by the HTML structure, so the scope of the subclass is small, and the base class has a greater scope of application because it is completely detached from the HTML structure.

3. Implementation Details

From the 2nd part of the realization of the idea, the class to be implemented are: Fileuploadbaseview and Imageuploadview, the former is the base class. Taking into account the ability to provide event management to components, So the eventbase.js,fileuploadbaseview of the previous blog has to inherit the Eventbase component of the library, and with the definition and inheritance of the class, the inherited library class.js is used to define the component and the inheritance of the component. The inheritance relationship of the related component is: Imageuploadview extend Fileuploadbaseview extend Eventbase.

(Note: Seajs is used for modularity in the following related code.) )

The things that Fileuploadbaseview do are:

1 define generic option and common event management

All common option and generic event definitions are visible in the defaults configuration of the component:

var DEFAULTS = {data
: [],//List of list elements to be displayed must be of type object, such as [{URL: ' xxx.png '},{url: ' Yyyy.png '}]
sizelimit:0,// The number of elements that are used to limit the display in Baseview, 0 for
Readonly:false,//for controlling whether elements in Baseview allow additions and deletions
Onbeforerender: $.noop,// Corresponds to the Render.before event, which triggers the onRender before the Render method call
: $.noop,//corresponds to the Render.after event, triggering render after the Onbeforeappend method call
: $. NoOp,//corresponds to Append.before event, triggers Onappend before Append method call
: $.noop,//corresponding Append.after event, triggering
after Append method call Onbeforedelitem: $.noop,//Correspondence Delitem.before event, triggering Ondelitem before Delitem method call
: $.noop// Corresponds to the Delitem.after event, which is triggered after the Delitem method call
;

The initialization logic for generic option and event management is seen in the Init method of this component:

Init:function (element, options) {
//The Init method
This.base (element) that invokes the parent class eventbase by This.base;
Instance attribute
var opts = This.options = this.getoptions (options);
This.data = Resolvedata (opts.data);
Delete Opts.data;
This.sizelimit = Opts.sizelimit;
This.readonly = opts.readonly;
Binding event
This.on (' Render.before ', $.proxy (Opts.onbeforerender, this));
This.on (' Render.after ', $.proxy (Opts.onrender, this));
This.on (' Append.before ', $.proxy (Opts.onbeforeappend, this));
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 defines the behavior of the component and reserves the interfaces that can be implemented by subclasses:

Render:function () {/** * Render is a template that does not need to override the Render method, only needs to override the _render method * The Render method of the parent class is invoked when invoking the Render method of the subclass * but executes to the _render method
, the _render method of the subclass is invoked * so that it can unify the triggering operation of the before with the after event/var e; This.trigger (e = $.
Event (' Render.before '));
if (e.isdefaultprevented ()) return;
This._render (); This.trigger ($.
Event (' Render.after ')); The//subclass needs to implement the _render method _render:function () {}, Append:function (item) {var e; if (!item) return; item = Resolvedataitem (i
TEM); This.trigger (e = $.
Event (' Append.before '), item);
if (e.isdefaultprevented ()) return;
This.data.push (item);
This._append (item); This.trigger ($.
Event (' Append.after '), item); The//subclass needs to implement the _append method _append:function (data) {}, Delitem:function (UUID) {var e, item = This.getdataitem (UUID); if (!it
EM) return; This.trigger (e = $.
Event (' Delitem.before '), item);
if (e.isdefaultprevented ()) return;
This.data.splice (This.getdataitemindex (UUID), 1);
This._delitem (item); This.trigger ($.
Event (' Delitem.after '), item); The//subclass needs to implement the _delitem method _delitem:function (data) {
} 

In order to unify the event distribution logic before and after the behavior, the main logic of Render, append, Delitem is taken as the method to realize the quilt _render, _append and _delitem. When the Render method of a subclass is invoked, the method of the actual parent class is invoked, but when the parent class executes to the _render method, it executes the method of the subclass, and the other two methods are similarly handled. It should be noted that subclasses cannot cover render, append, Delitem three methods, otherwise they have to handle the triggering logic of related events themselves.

Fileuploadbaseview overall implementation is as follows:

Define (function (Require, exports, module) {var $ = require (' jquery '); var Class = require (' Mod/class '); var eventbase =
Require (' mod/eventbase '); var DEFAULTS = {data: []////////////////////////////////////////////////////////// The number of elements shown in, 0 for unrestricted readonly:false,//to control whether elements in Baseview allow additions and deletions Onbeforerender: $.noop,// Corresponds to the Render.before event, which triggers the onRender before the Render method call: $.noop,//corresponds to the Render.after event, triggering the render after the Onbeforeappend method call: $.noop,// Corresponds to the Append.before event, which triggers the onappend before the Append method call: $.noop,//corresponds to the Append.after event, triggering the append after the Onbeforedelitem method call: $.noop,//
Corresponds to the Delitem.before event, which triggers the ondelitem before the Delitem method call: $.noop//corresponds to the Delitem.after event, which is triggered after the Delitem method call; /** * Data processing, to each record of data add a _uuid attribute, easy to find * * function resolvedata (data) {var time = new Date (). GetTime (); return $.map (data
, function (d) {return Resolvedataitem (d, time);}); function Resolvedataitem (data, time) {time = time | | | new Date (). GetTime (); data._uuid = ' _uuid ' + Time + math.floor (Mat
H.random () * 100000); Return Data var Fileuploadbaseview = Class ({instancemembers: {init:function (element, options) {//The init side of the parent class eventbase is invoked through This.base
Law this.base (element);
instance attribute var opts = This.options = this.getoptions (options);
This.data = Resolvedata (Opts.data);
Delete Opts.data;
This.sizelimit = Opts.sizelimit;
This.readonly = opts.readonly;
Binding event This.on (' Render.before ', $.proxy (Opts.onbeforerender, this));
This.on (' Render.after ', $.proxy (Opts.onrender, this));
This.on (' Append.before ', $.proxy (Opts.onbeforeappend, this));
This.on (' Append.after ', $.proxy (Opts.onappend, this));
This.on (' Delitem.before ', $.proxy (Opts.onbeforedelitem, this));
This.on (' Delitem.after ', $.proxy (Opts.ondelitem, this));  }, Getoptions:function (options) {return $.extend ({}, This.getdefaults (), options); Getdefaults:function () {return
DEFAULTS; 
Getdataitem:function (UUID) {//Dateitem return This.data.filter (function (item) {return item._uuid = = UUID) is obtained from the UUID;
}) [0]; }, Getdataitemindex:function (UUID) {var ret;
This.data.forEach (function (item, i) {item._uuid = = uuid && (ret = i);});
return ret; Render:function () {/** * Render is a template that does not need to override the Render method, only needs to override the _render method * The Render method of the parent class is invoked when invoking the Render method of the subclass * but executes to _render
method, the _render method of the subclass is invoked * This will unify the triggering operation of the before with the after event/var e; This.trigger (e = $.
Event (' Render.before '));
if (e.isdefaultprevented ()) return;
This._render (); This.trigger ($.
Event (' Render.after ')); The//subclass needs to implement the _render method _render:function () {}, Append:function (item) {var e; if (!item) return; item = Resolvedataitem (i
TEM); This.trigger (e = $.
Event (' Append.before '), item);
if (e.isdefaultprevented ()) return;
This.data.push (item);
This._append (item); This.trigger ($.
Event (' Append.after '), item); The//subclass needs to implement the _append method _append:function (data) {}, Delitem:function (UUID) {var e, item = This.getdataitem (UUID); if (!it
EM) return; This.trigger (e = $.
Event (' Delitem.before '), item);
if (e.isdefaultprevented ()) return;
This.data.splice (This.getdataitemindex (UUID), 1); This._delitem (item); This.trigger ($.
Event (' Delitem.after '), item);
The//subclass needs to implement the _delitem method _delitem:function (data) {}}, Extend:eventbase, staticmembers: {defaults:defaults}});
return fileuploadbaseview; });

The realization of the Imageuploadview is relatively simple, with the fill in the blanks, there are several points need to explain:

1 The defaults of this class needs to extend the defaults of the parent class to add the default options for this subclass, while retaining the definition of the default options for the parent class; A new Onappendclick event is added according to the static page structure. External methods that can invoke the file upload component in this event:

Inherits and extends the default DEFAULTS
var DEFAULTS = $.extend ({}, Fileuploadbaseview.defaults, {
Onappendclick: $.noop// Click on the Upload button when the callback
});

2 in the Init method, it is necessary to invoke the Init method of the parent class in order to complete those common logical processing, while at the end of Init you will have to manually call the Render method to see the effect after the component is instantiated:

Other implementations are purely business logic implementations, closely related to the requirements of the 2nd part.

The overall implementation of the Imageuploadview is as follows:

Define (function (Require, exports, module) {var $ = require (' jquery '); var Class = require (' Mod/class '); var fileuploadb
Aseview = require (' Mod/fileuploadbaseview ');
Inherits and extends the default DEFAULTS var DEFAULTS = $.extend ({}, Fileuploadbaseview.defaults, {onappendclick: $.noop//Click the Upload button callback}); var Imageuploadview = Class ({instancemembers: {init:function (element, options) {var $element = this. $element = $ (elem
ENT);
var opts = this.getoptions (options);
Invoke the Init method of the parent class to complete options acquisition, data parsing, and listener processing of generic events this.base (this. $element, options); Add upload and delete listeners and trigger handling if (!this.readonly) {var that = this; That.on (' Appendclick ', $.proxy (Opts.onappendclick, this)); $ele
Ment.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 ();
});
} this.render (); }, Getdefaults:function () {return DEFAULTS;}, _setitemaddhtml:functiOn () {this. $element. Prepend ($ (' <li class= ' View-item-add "><a class=" View-act-add "href=" javascript:; "title
= "Click to upload" >+</a></li>)); }, _clearitemaddhtml:function ($itemAddLi) {$itemAddLi. Remove ();}, _render:function () {var html = [], that = this; /If it is not a read-only state and has not yet reached the upload limit, add the upload button if (! this.readonly | | (This.sizelimit && this.sizelimit <= this.data.length)))
{this._setitemaddhtml ();}
This.data.forEach (function (item) {Html.push (that._getitemrenderhtml (item))});
this. $element. Append ($ (html.join ('))); }, _getitemrenderhtml:function (item) {return [' <li id= ', Item._uuid, ' ><a class= ' view-act-preview ' href= ' ja Vascript:; " > ', this.readonly? ': ' <span class= ' View-act-del ' data-uuid= ', item._uuid , ' > Delete </span> ', ' </a></li> '].join (');}, _dealwithsizelimit:function () {if (this.sizelimit) {V
Ar $itemAddLi = this. $element. Find (' Li.view-item-add '); If you have reached the upload limit, moveExcept upload button if (this.sizelimit && this.sizelimit <= this.data.length && $itemAddLi. Length) {this._
Clearitemaddhtml ($itemAddLi);
else if (! $itemAddLi. Length) {this._setitemaddhtml ();}} }, _append:function (data) {this. $element. Append ($ (this._getitemrenderhtml (data)); This._dealwithsizelimit ();}, _
Delitem:function (data) {$ (' # ' + data._uuid). Remove (); This._dealwithsizelimit ();}}, Extend:fileuploadbaseview});
return imageuploadview; });

4. Demo description

The project structure of the demo is:

The box is the core code of the demo. Where Fileuploadbaserview.js and Imageuploadview.js are the two core components of the previous implementation. Fileuploader.js is used to simulate the upload component, its instance has a onsuccess callback, indicating that the upload was successful; there is also a openchoosefilewin to simulate the real open selection File window and upload the process:

Define (function (Require, exports, module) {return
function () {
var imglist = ['. /img/1.jpg ', '. /img/2.jpg ', '. /img/3.jpg ', '. /img/4.jpg '], i = 0;
var that = this;
that.onsuccess = function (uploadvalue) {}
This.openchoosefilewin = function () {
settimeout (function () {
That.onsuccess (imglist[i++]);
if (i = = imglist.length) {
i = 0;
}
},1000);}}
);

App/regist.js is the logical code for the demo page, and the key part has been explained with annotations:

Define (function (Require, exports, module) {var $ = require (' jquery '); var Imageuploadview = require (' Mod/imageuploadvie
W '); var Fileuploader = require (' Mod/fileuploader '); This is a file Upload component//$legalPersonIDPic that is simulated with an asynchronous task to store uploaded file information. After the upload component is uploaded successfully and the Imageuploadview component deletes an item, it will affect the value of $legalpersonidpic var $legalPersonIDPic = $ (' # Legalpersonidpic-input '), data = Json.parse ($legalPersonIDPic. val () | | "[]");//data is the initial value, such as the current page may be loaded from the database, need to use the Imageuploadview component to render//after the file upload success, the file just uploaded to the $legalpersonidpic value//$  Legalpersonidpic stores var appendimageinputvalue = function ($input, item) {var value = Json.parse ($input. val) in JSON string () | |
'[]');
Value.push (item);
$input. Val (json.stringify (value));
};
After calling the Imageuploadview component to delete an item, synchronize the stored information in $legalpersonidpic to the var removeimageinputvalue = function ($input, uuid) { var value = Json.parse ($input. val () | |
' [] '), 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.onsuccess = function (uploadvalue) {var item = {Url:uploadvalue}; legalpersonidpicview.append (item); Appen
Dimageinputvalue ($legalPersonIDPic, item);
}; var legalpersonidpicview = new Imageuploadview (' #legalPersonIDPic-view ', {data:data, sizelimit:4, onappendclick:funct Ion () {//Open the window of the selection file Fileuploader.openchoosefilewin ();}, Ondelitem:function (data) {Removeimageinputvalue ($
Legalpersonidpic, Data._uuid);
}
}); });

5. This paper concludes

Imageuploadview This component was ultimately not difficult to implement, but I also spent a lot of time pondering it and other implementations of the parent class, most of the time spent in the abstract process of separation of duties and behavior. I have expressed in this article on these two aspects of programming ideas is only my own personal experience, because the abstract level of things, each person's way of thinking different final understanding of the results will not be the same, so I can not directly say that my right or wrong, write out the purpose is to share and exchange, To see if there are other experienced friends are willing to put their own ideas in this regard to tell you, I believe everyone to see more people's ideas, but also to their own programming ideas of the exercise to bring help.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.