Cloud uploader add-on for Angular Schema Form, using Angular File Upload.
The form type cloud-uploader
(used with the Bootstrap Decorator) generates a field set for selecting files and uploading them to a remote location, and saves the file names in the form model. Cloud storage, such as Amazon S3 (see how to configure CORS for a bucket) or Google Cloud Storage (see how to configure CORS for a bucket), is the anticipated use case.
(This is probably clear, but just in case: this is a package for Angular, Angular File Upload, Bootstrap, and Angular Schema Form, but it is not affiliated with any of them.)
- Examples and description
- Installation and usage
- Options and behavior
- Future changes; semantic versioning
- Contributions
- License
The minimal field schema is one of
{ "type": "string" }
{ "type": "array", "items": { "type": "string"} }
(in the first case, only one file is allowed and its name is returned; in the second, several files are allowed and an array with their names is returned). In order to use the add-on, the form specification must include "type": "cloud-uploader"
.
The image shows what happens for the following schema and form.
{
"type": "object",
"properties": {
"multiple": {
"type": "array",
"title": "Multiple files",
"items": { "type": "string" },
"description": "Files uploaded to cloud storage"
},
"single": {
"type": "string",
"title": "Single file",
"description": "File uploaded to cloud storage"
}
}
}
[{
key: 'multiple',
type: 'cloud-uploader',
queueItemRemoveHtmlClass: 'btn btn-default btn-xs',
queueItemRemoveIcon: 'glyphicon glyphicon-trash',
triggerSubmit: 'Trigger form submission after successful upload',
url: '' // the relevant URL
}, {
key: 'single',
type: 'cloud-uploader',
dropZoneHtmlClass: 'drop-zone well',
queueItemRemoveHtmlClass: 'btn btn-default btn-xs',
queueItemRemoveIcon: 'glyphicon glyphicon-erase',
queueItemRemoveLabel: 'Replace',
selectIcon: 'glyphicon glyphicon-file',
selectLabel: 'Select file',
triggerSubmit: 'Trigger form submission after successful upload',
url: '' // the relevant URL
}]
A field set is created for each upload field. It includes a status message (or field description), a table listing all files (and their progress), an optional drop zone, a file selection input, and an optional form submission trigger.
The table lists all files uploaded or pending (all submitted files are automatically uploaded if not unlisted before the upload completes). Each row includes a removal button: queueItemRemoveHtmlClass
specifies the class for the button, queueItemRemoveIcon
specifies classes for the icon (if desired), and queueItemRemoveLabel
specifies the text of the button.
The drop zone is displayed only in HTML5 browsers, and only if dropZoneHtmlClass
is specified (in the example 'drop-zone'
is a class defined in the stylesheet).
The file selection input is configured with selectIcon
(classes for the icon, if desired) and selectLabel
(text label). The button is disabled if the file limit has been reached.
The trigger selection checkbox is available only if triggerSubmit
is provided. After the uploads complete the checkbox is disabled (it is enabled again if all files are removed from the list). If the checkbox was selected when the uploads complete, the 'submit'
event (as with ngSubmit
) is triggered for the parent form. This is useful in cases where the user may assume it is enough to select the files and wait for the upload to complete.
There are several other options (including for specifying the remote location and for setting global defaults), discussed below.
If the abilities to send additional fields (other than the file) in the POST
request or to remove specific files from the queue are not needed, or showing image previews is desired, the Angular Schema Form File-Upload add-on by Netzwerkplan GmbH may be a better choice.
As other ASF add-ons do, this one manages its dependencies via Bower. Angular (1.6), Angular File Upload, Angular Schema Form and its Bootstrap Decorator, and their dependencies must all be available for this add-on to work. If they are already installed in typical locations,
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-sanitize/angular-sanitize.min.js"></script>
<script src="bower_components/angular-file-upload/dist/angular-file-upload.min.js"></script>
<script src="bower_components/tv4/tv4.js"></script>
<script src="bower_components/objectpath/lib/ObjectPath.js"></script>
<script src="bower_components/angular-schema-form/dist/schema-form.min.js"></script>
<script src="bower_components/angular-schema-form-bootstrap/bootstrap-decorator.min.js"></script>
<script src="bower_components/angular-schema-form-cloud-uploader/angular-schema-form-cloud-uploader.min.js"></script>
<link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
inside <head></head>
invokes all required files, and
angular.module('app', ['schemaForm', 'angularSchemaFormCloudUploader']);
creates an app including this add-on.
In a system with Bower already installed,
$ bower install angular-schema-form-cloud-uploader
installs all dependencies.
The add-on can be installed from npm, with
$ npm install angular-schema-form-cloud-uploader
after which bower install
installs (Bower) dependencies.
To work with the package as a stand-alone, clone the repository:
$ git clone https://github.com/jboavida/asf-cloud-uploader.git
creates a new directory asf-cloud-uploader
and clones the repository into it. Within the new directory, npm install
installs all development dependencies (including Bower), npm test
runs all tests, and npm start
generates the minified file and starts a demo server at http://localhost:8080/
(using the files at ./demo
) that accepts mock uploads at http://localhost:8080/uploads
.
Almost all standard options are supported. placeholder
is not applicable, htmlClass
is applied to the entire fieldset, fieldHtmlClass
is applied to the file input, labelHtmlClass
is applied to the fieldset legend.
The other form options (and their defaults) are:
{
url: undefined, // url for upload
alias: 'file', // field name for file content (in upload POST)
data: {}, // additional form fields (in upload POST)
headers: {}, // headers (for upload POST)
maxItems: Number.MAX_VALUE, // maximum queue size
withCredentials: false, // passed on to the uploader
queueHtmlClass: 'table table-condensed table-striped', // class for queue
queueItemNameHtmlClass: '', // class for file name
queueItemRemoveHtmlClass: 'btn btn-danger btn-xs', // class for removal btns
queueItemRemoveIcon: undefined, // removal button icon
queueItemRemoveLabel: 'Unlist', // removal button label
queueItemProgressHtmlClass: 'progress-bar', // class for progress bar
dropZoneHtmlClass: 'ng-hide', // class for drop zone
dropOverHtmlClass: 'nv-file-over', // additional class when over drop zone
fieldHtmlClass: 'btn btn-default', // class for file select button
fieldFocusHtmlClass: undefined, // additional class for focused button
selectIcon: undefined, // file select button icon
selectLabel: 'Upload', // file select button label
selectMultiple: true, // allow multiple file selection
triggerSubmit: undefined, // trigger submit checkbox label
triggerSubmitHtmlClass: 'checkbox' // class for trigger submit checkbox
}
Only schemas compatible with { "type": "string" }
or { "type": "array", "items": { "type": "string" } }
are supported. Additional schema keys (such as format
or maxItems
) are processed as usual.
If a string schema is provided, the form option maxItems
is set to 1
and the form option selectMultiple
is removed.
If an array schema is provided, the schema keys maxItems
is copied to the form option maxItems
.
In addition to any schema validation requirements, the field is not valid if any upload failed (checked by $validators.uploadFailed
) or if there are files waiting to be uploaded or files being uploaded (checked by $validators.uploadOngoing
).
If the model already has a value (string or array of strings) when the form is generated, dummy files are added to the list of uploaded files, with names as provided by that value.
Some options are converted into Angular File Upload uploader options:
url
is the remote path where files are to be uploaded to;alias
is the name of the form field for the file contents; this will always be the last field in the form;data
is an object with additional fields (and their values) to include in the form; if the order of fields matters, an array of objects should be used (all fields from one object are added before moving on to the following object in the array); this value (or, if it is an object, the array with it) is passed on to the uploader'sformData
option;headers
is an object with additional HTTP headers for the request;maxItems
is passed on asqueueLimit
;withCredentials
is passed on to the uploader.
Only POST
requests are supported.
The uploader's autoUpload
is set to true
.
The upload queue is displayed in a table, whose class is set with the queueHtmlClass
option. Each row corresponds to a file in the queue, with elements for the filename, a removal button (cancels the upload if possible and removes the file from the queue), a progress bar, and feedback.
The queueItemNameHtmlClass
option sets the class for the filename element.
The queueItemRemoveHtmlClass
options sets the class for the removal button (if replaced, the classes 'btn btn-default'
, or others with similar roles, need to be included in the replacement), queueItemRemoveIcon
sets the icon class (in a span element), and queueItemRemoveLabel
sets the text in the button. Removing a file from the queue is not reversible (and does not remove the file from the remote location if the upload had already finished); the styling (or even supporting text) should convey that irreversibility. The button can be removed by setting the class to 'ng-hide'
.
Only the inner div
of the progress bar can be styled, using the option queueItemProgressHtmlClass
(if replaced, the class 'progress-bar'
, or another with a similar role, needs to be included in the replacement). The current progress is always reported to screen readers.
The feedback element is not displayed if the feedback
option is false
. Otherwise, a success icon appears (unless the option disableSuccessState
is true
) if the upload completed successfully, and an error icon appears (unless the option disableErrorState
is true
) if there was an error during the upload. The status is always reported to screen readers.
The drop zone is displayed only in HTML5 browsers.
The dropZoneHtmlClass
option controls its class (if missing, 'ng-hide'
is used, effectively making the drop zone invisible), while dropOverHtmlClass
contains additional classes for when the files to be dropped are already over the drop zone. (This uses nvFileDrop
and nvFileOver
from Angular File Upload; dropOverHtmlClass
is passed on to over-class
.)
The file selection input is actually displayed as the label for a file input control that is displayed only for screen readers. (The input control uses nvFileSelect
from Angular File Upload.)
The option fieldHtmlClass
sets the class for the label. The option fieldFocusHtmlClass
sets what classes to add when the control is in focus. (If the users' browsers are known to support the :focus.within
pseudo-class, it may be used instead.) The option selectIcon
sets the icon class (in a span element), and selectLabel
sets the text in the button.
If a string schema was used for the field, or maxItems
was set to 1
(either in the schema or as a form option), or the option selectMultiple
was set to false
, the file input will allow selection of a single file. Otherwise, it will allow multiple files.
In some forms, it may appear to users that once uploads finish, the form is automatically submitted (or does not need to be). The user should always control form submission, but sometimes it is indeed necessary that the form be explicitly submitted. For such cases, the submission trigger lets the user stay in control, while providing (via its label) indication that submission (either automated or manual) is indeed necessary.
The option triggerSubmit
specifies the label for the checkbox. The checkbox is made available only if this label is provided. Only the outer div
of the checkbox can be styled, using the option triggerSubmitHtmlClass
(if replaced, the class 'checkbox'
, or another with a similar role, needs to be included in the replacement).
The submission trigger checkbox is enabled only when the queue is empty or there are ongoing uploads. The trigger is activated when the uploads finish, but only if the field is valid, no upload failed, and the queue is not empty.
Usually, we want similar defaults for all similar fields. Defaults are provided by the asfCloudUploaderDefaults
service; all form options may have defaults configured. For example,
angular.module('angularSchemaFormCloudUploader').run([
'asfCloudUploaderDefaults', function (defaults) {
angular.extend(defaults, {
dropZoneHtmlClass: 'drop-zone well',
dropOverHtmlClass: 'drop-over',
queueItemRemoveHtmlClass: 'btn btn-default btn-xs',
queueItemRemoveIcon: 'glyphicon glyphicon-trash',
selectIcon: 'glyphicon glyphicon-file',
triggerSubmit: 'Trigger form submission after successful upload'
});
}
]);
sets defaults very close to those of the examples above. (Note that angular.extend
is necessary if we wish to maintain the defaults that are not being replaced.)
To set different defaults for different controllers, we can use a decorator and make a shallow copy of the original value before modifying it:
angular.module('app').config(['$provide', function($provide) {
$provide.decorator('asfCloudUploaderDefaults', [
'$delegate', function (defaults) {
defaults = angular.copy(defaults);
angular.extend(defaults, {
dropZoneHtmlClass: 'drop-zone well',
dropOverHtmlClass: 'drop-over',
queueItemRemoveHtmlClass: 'btn btn-default btn-xs',
queueItemRemoveIcon: 'glyphicon glyphicon-trash',
selectIcon: 'glyphicon glyphicon-file',
triggerSubmit: 'Trigger form submission after successful upload'
});
return defaults; // don't forget this
}
]);
}]);
In this example, the changes apply only to that app.
Another possibility is to set global options for the form.
This add-on has two additional error types: 'uploadFailed'
is present if there was an error if an upload, 'uploadOngoing'
is present if there are pending uploads.
There are several ways to configure the validation messages.
For example,
angular.module('app').config([
'sfErrorMessageProvider', function (sfErrorMessageProvider) {
sfErrorMessageProvider.setDefaultMessage(
'uploadFailed', 'An upload is broken'
);
}
]);
replaces the 'uploadFailed'
message for the entire app.
This package started as a submodule of another project. I factored it out as a stand-alone module and cleaned it up enough to make it easy to maintain, but I'm still adjusting it as needed for the other project.
I intend to follow semantic versioning. I don't expect to make big changes, but there are some areas where changes may be needed:
- I'm not sure all those options to control classes of different subcomponents are really needed, or are all that are needed;
- I may end up needing some practical way to replace the structure of some subcomponents (possibly leveraging the template cache to make it reasonably easy to configure);
- I tried to make all components accessible (checking focus options, tab index, keyboard interaction, etc.), but it's likely I missed something.
On the other hand, it does not make much sense to make big changes before the next version of Angular Schema Form is released. So, I'm very likely to make only changes that are clearly needed (or helpful) for the other project.
Until I'm confident the package is sufficiently stable, I won't accept code contributions.
Issues (bug reports, suggestions, etc.) are welcome, but I may take some time to get to them.
Copyright © 2017 João Pedro Boavida. Licensed under the MIT License.