Now.js Framework Documentation

Now.js Framework Documentation

FileElementFactory - File Upload Component

EN 01 Dec 2025 13:46

FileElementFactory - File Upload Component

Documentation for FileElementFactory, a powerful file upload component with preview, drag & drop, sorting, and validation capabilities.

📋 Table of Contents

  1. Overview
  2. Installation and Setup
  3. Basic Usage
  4. HTML Attributes
  5. Configuration Options
  6. Features
  7. File Validation
  8. Preview System
  9. Drag & Drop
  10. Sortable Files
  11. Existing Files
  12. Events
  13. JavaScript API
  14. Usage Examples
  15. Best Practices

Overview

FileElementFactory is an ElementFactory that enhances file input elements with advanced features like preview, drag & drop, file validation, sorting, and seamless integration with the Now.js Form component.

Key Features

  • Live Preview: Real-time preview of uploaded images and files
  • Drag & Drop: Intuitive drag and drop interface
  • File Validation: Validate file types and sizes
  • Sortable Files: Reorder files by dragging (requires Sortable.js)
  • Multiple Files: Support for single and multiple file uploads
  • Existing Files: Load and display existing files from server
  • Remove Files: Delete files before submission
  • Progress Tracking: Upload progress indicator
  • Form Integration: Seamless integration with FormManager
  • Auto-Initialization: Automatically enhances file inputs

When to Use FileElementFactory

Use FileElementFactory when:

  • You need file upload with preview
  • You want drag and drop functionality
  • You need to validate file types and sizes
  • You want to display existing uploaded files
  • You need sortable file lists
  • You want seamless form integration

Don't use FileElementFactory when:

  • You need very basic file input (use native <input type="file">)
  • You need custom upload logic (use custom implementation)
  • You don't need any enhanced features

Installation and Setup

FileElementFactory is part of the Now.js Framework and is automatically registered with ElementManager.

Dependencies

  • ElementFactory - Base class
  • EventSystemManager - Event handling
  • FormError - Error display
  • Sortable.js (optional) - For sortable file lists
  • MediaViewer (optional) - For image preview modal

Auto-Registration

// FileElementFactory is automatically registered
ElementManager.registerElement('file', FileElementFactory);

// All <input type="file"> elements are automatically enhanced

Basic Usage

1. Simple File Upload

<input type="file"
       id="avatar"
       name="avatar"
       accept="image/*">

2. File Upload with Preview

<input type="file"
       id="avatar"
       name="avatar"
       data-preview="true"
       accept="image/*">

3. Multiple Files with Drag & Drop

<input type="file"
       id="files"
       name="files"
       multiple
       data-preview="true"
       data-drag-drop="true"
       accept="image/*,application/pdf">

4. Complete Example with All Features

<form data-form="upload">
  <label for="files">Upload Files</label>
  <span class="form-control icon-upload">
    <input type="file"
           id="files"
           name="files"
           multiple
           data-preview="true"
           data-drag-drop="true"
           data-sortable="true"
           data-allow-remove-existing="true"
           data-file-reference="url"
           data-max-file-size="5242880"
           accept="image/*,application/pdf"
           placeholder="Drag files here or click to browse">
  </span>
</form>

HTML Attributes

FileElementFactory supports these data attributes for configuration:

Basic Attributes

Attribute Type Default Description
data-preview boolean false Enable live preview of uploaded files
data-drag-drop boolean false Enable drag and drop functionality
data-sortable boolean false Allow reordering of uploaded files
data-allow-remove-existing boolean false Allow removal of existing uploaded files
multiple boolean false Allow multiple file selection
accept string * Allowed file types (e.g., "image/*,application/pdf")
placeholder string - Placeholder text for drag & drop zone

File Reference Attributes

Attribute Type Default Description
data-file-reference string 'id' Field name for file reference (e.g., "url", "id")
data-attr string - Bind to data source (e.g., "value:files")
data-files JSON - Existing files data

Validation Attributes

Attribute Type Default Description
data-max-file-size number 10485760 Maximum file size in bytes (default: 10MB)
required boolean false Make file upload required

Server Integration Attributes

Attribute Type Default Description
data-action-url string - API endpoint for file operations (delete, sort)

Preview Container Attribute

Attribute Type Default Description
data-preview-container string - CSS selector for custom preview container

Configuration Options

When creating an instance programmatically:

const instance = FileElementFactory.create(element, {
  // Preview
  preview: true,
  previewContainer: '.custom-preview',

  // Drag & Drop
  dragDrop: true,

  // Sortable
  sortable: true,

  // File Management
  allowRemoveExisting: true,
  fileReference: 'url',

  // Validation
  maxFileSize: 5 * 1024 * 1024, // 5MB
  allowedMimeTypes: ['image/*', 'application/pdf'],

  // Server Integration
  actionUrl: '/api/files',

  // Existing Files
  existingFiles: [
    { url: 'files/photo.jpg', name: 'photo.jpg' },
    { url: 'files/document.pdf', name: 'document.pdf' }
  ],

  // UI
  placeholder: 'Drag files here or click to browse',
  downloadEnabled: true,

  // Callbacks
  onChange: (files, element) => {
    console.log('Files changed:', files);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});

Features

1. Live Preview

Display thumbnails of uploaded files:

<input type="file"
       name="images"
       multiple
       data-preview="true"
       accept="image/*">

Features:

  • Image thumbnails for image files
  • File icons for documents (PDF, Word, Excel, etc.)
  • File name and size display
  • Remove button for each file

2. Drag & Drop

Enable drag and drop file upload:

<input type="file"
       name="files"
       multiple
       data-drag-drop="true"
       placeholder="Drag files here or click to browse">

Features:

  • Visual drop zone
  • Drag over feedback
  • Multiple file drop support
  • Click to browse fallback

3. Multiple Files

Upload multiple files at once:

<input type="file"
       name="files"
       multiple
       data-preview="true">

4. File Validation

Validate file types and sizes:

<input type="file"
       name="avatar"
       accept="image/jpeg,image/png,image/webp"
       data-max-file-size="2097152">
<!-- Max size: 2MB -->

Validation Rules:

  • File type validation (MIME types and extensions)
  • File size validation
  • Automatic error display
  • Form integration

5. Sortable Files

Reorder uploaded files by dragging (requires Sortable.js):

<input type="file"
       name="gallery"
       multiple
       data-preview="true"
       data-sortable="true"
       data-action-url="/api/files/sort">

Features:

  • Drag handle for each file
  • Visual feedback during sorting
  • Auto-save order to server
  • Revert on error

6. Existing Files

Load and display existing files from server:

<input type="file"
       name="files"
       multiple
       data-preview="true"
       data-attr="value:files"
       data-file-reference="url"
       data-allow-remove-existing="true"
       data-action-url="/api/files">

Server Response Format:

{
  "data": {
    "files": [
      {
        "url": "uploads/photo.jpg",
        "name": "photo.jpg",
        "size": 1258291
      },
      {
        "url": "uploads/document.pdf",
        "name": "document.pdf",
        "size": 524288
      }
    ]
  }
}

File Validation

Supported File Types

Default allowed MIME types:

allowedMimeTypes: [
  'image/*',
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'text/plain',
  'application/zip',
  'application/x-zip-compressed'
]

Custom Validation

<!-- Accept only images -->
<input type="file" accept="image/*">

<!-- Accept specific image types -->
<input type="file" accept="image/jpeg,image/png,image/webp">

<!-- Accept images and PDFs -->
<input type="file" accept="image/*,application/pdf">

<!-- Accept by extension -->
<input type="file" accept=".jpg,.jpeg,.png,.pdf">

File Size Validation

<!-- Max 5MB -->
<input type="file" data-max-file-size="5242880">

<!-- Max 10MB (default) -->
<input type="file" data-max-file-size="10485760">

Validation Errors

Errors are automatically displayed using FormError:

// Error messages are translated
"File size cannot exceed {maxsize}"
"File type not allowed"
"File is required"

Preview System

Automatic Preview

Preview is automatically created when data-preview="true":

<span class="form-control icon-upload">
  <input type="file"
         name="files"
         multiple
         data-preview="true">
</span>

Generated HTML:

<span class="form-control icon-upload">
  <input type="file" name="files" multiple>
  <div class="file-preview">
    <div class="preview-item" data-file-name="photo.jpg">
      <span class="image-preview" style="background-image: url(...)"></span>
      <div class="file-info">photo.jpg (1.2 MB)</div>
      <button type="button" class="icon-delete" title="Delete file"></button>
    </div>
  </div>
</span>

Custom Preview Container

<input type="file"
       name="files"
       data-preview="true"
       data-preview-container="#custom-preview">

<div id="custom-preview"></div>

Preview Features

  • Image Thumbnails: Background images for photos
  • File Icons: Icons for documents (PDF, Word, Excel, ZIP)
  • File Info: Name and size display
  • Remove Button: Delete files before upload
  • Click to View: Click images to open in modal (requires MediaViewer)

Drag & Drop

Basic Drag & Drop

<input type="file"
       name="files"
       multiple
       data-drag-drop="true"
       placeholder="Drag files here or click to browse">

Generated Drop Zone

<div class="file-drop-zone">
  <input type="file" name="files" multiple>
  <div class="file-drop-content">
    <div class="file-drop-message">Drag files here or click to browse</div>
  </div>
  <div class="file-preview">
    <!-- Preview items -->
  </div>
</div>

CSS Classes

Class When Applied Description
file-drop-zone Always Drop zone container
drag-over During drag File is being dragged over
file-drop-content Always Drop zone content area
file-drop-message Always Drop zone message

Styling Example

.file-drop-zone {
  border: 2px dashed #ccc;
  border-radius: 8px;
  padding: 20px;
  text-align: center;
  transition: all 0.3s;
}

.file-drop-zone.drag-over {
  border-color: #007bff;
  background-color: #f0f8ff;
}

.file-drop-message {
  color: #666;
  font-size: 16px;
  padding: 40px 20px;
}

Sortable Files

Enable Sortable

Requires Sortable.js library:

<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>

<input type="file"
       name="gallery"
       multiple
       data-preview="true"
       data-sortable="true"
       data-action-url="/api/files/sort"
       data-file-reference="id">

Server-Side Sorting

When files are reordered, a POST request is sent to data-action-url:

Request:

{
  "action": "sort",
  "order": [
    { "id": "123", "position": 0 },
    { "id": "456", "position": 1 },
    { "id": "789", "position": 2 }
  ]
}

Response:

{
  "status": 200,
  "message": "Sort order saved"
}

Sortable Features

  • Drag handle for each file
  • Visual feedback during drag
  • Auto-save to server
  • Revert on error
  • Success notification

Existing Files

Load Existing Files

Use data-attr and data-file-reference to load existing files:

<form data-form="settings" data-load-api="settings.php">
  <input type="file"
         name="files"
         multiple
         data-preview="true"
         data-attr="value:files"
         data-file-reference="url"
         data-allow-remove-existing="true"
         data-action-url="/api/files">
</form>

Server Response Format

{
  "data": {
    "files": [
      {
        "id": "123",
        "url": "uploads/photo.jpg",
        "name": "photo.jpg",
        "size": 1258291
      },
      {
        "id": "456",
        "url": "uploads/document.pdf",
        "name": "document.pdf",
        "size": 524288
      }
    ]
  }
}

Delete Existing Files

When data-allow-remove-existing="true", users can delete existing files:

Request:

{
  "action": "delete",
  "url": "uploads/photo.jpg"
}

Response:

{
  "status": 200,
  "message": "File deleted successfully"
}

Events

FileElementFactory emits events through EventSystemManager:

Event Types

Event When Detail
file:change Files changed {elementId, files}

Listen to Events

Now.on('file:change', (data) => {
  console.log('Files changed:', data.files);
  console.log('Element ID:', data.elementId);
});

Form Integration

FileElementFactory integrates with FormManager:

  • Automatic validation on form submit
  • File data included in form submission
  • Error display integration

JavaScript API

Get Instance

// From element
const instance = ElementManager.getInstance(element);

// From ID
const element = document.getElementById('files');
const instance = ElementManager.getInstance(element);

Instance Methods

// Clear all files
instance.clearFiles();

// Validate files
const isValid = instance.validateFiles();

// Upload files
const result = await instance.upload({
  url: '/api/upload',
  data: { userId: 123 }
});

Static Methods

// Validate single file
await FileElementFactory.validateFile(file, config);

// Format file size
const size = FileElementFactory.formatFileSize(1258291);
// Returns: "1.2 MB"

// Get file icon class
const icon = FileElementFactory.getFileIcon('document.pdf');
// Returns: "icon-pdf"

// Check file type
const isValid = FileElementFactory.isValidFileType(file, ['image/*', 'application/pdf']);

Upload Method

const result = await instance.upload({
  url: '/api/upload',           // Upload URL
  data: {                        // Additional data
    userId: 123,
    category: 'photos'
  }
});

if (result.success) {
  console.log('Upload successful');
} else {
  console.error('Upload failed:', result.message);
}

Features:

  • Progress tracking
  • FormData creation
  • Additional data support
  • Error handling
  • Promise-based

Usage Examples

Example 1: Simple Avatar Upload

<form data-form="profile" method="POST" action="/api/profile">
  <label for="avatar">Profile Photo</label>
  <span class="form-control icon-image">
    <input type="file"
           id="avatar"
           name="avatar"
           data-preview="true"
           accept="image/*"
           data-max-file-size="2097152"
           placeholder="Choose your profile photo">
  </span>
  <button type="submit">Save Profile</button>
</form>

Example 2: Multiple Files with Drag & Drop

<form data-form="documents" method="POST" action="/api/documents">
  <label for="files">Upload Documents</label>
  <span class="form-control icon-upload">
    <input type="file"
           id="files"
           name="files"
           multiple
           data-preview="true"
           data-drag-drop="true"
           accept="image/*,application/pdf"
           data-max-file-size="10485760"
           placeholder="Drag files here or click to browse">
  </span>
  <button type="submit">Upload Files</button>
</form>
<form data-form="gallery" data-load-api="/api/gallery">
  <label for="photos">Photo Gallery</label>
  <span class="form-control icon-image">
    <input type="file"
           id="photos"
           name="photos"
           multiple
           data-preview="true"
           data-drag-drop="true"
           data-sortable="true"
           data-allow-remove-existing="true"
           data-file-reference="id"
           data-action-url="/api/gallery"
           data-attr="value:photos"
           accept="image/*"
           placeholder="Drag photos here">
  </span>
  <button type="submit">Save Gallery</button>
</form>

Example 4: Programmatic Usage

const element = document.getElementById('files');

const instance = FileElementFactory.create(element, {
  preview: true,
  dragDrop: true,
  sortable: true,
  maxFileSize: 5 * 1024 * 1024,
  allowedMimeTypes: ['image/*', 'application/pdf'],
  onChange: (files) => {
    console.log('Files selected:', files);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});

// Upload files
document.querySelector('button').addEventListener('click', async () => {
  const result = await instance.upload({
    url: '/api/upload',
    data: { category: 'documents' }
  });

  if (result.success) {
    alert('Upload successful!');
  }
});

Best Practices

1. Always Validate File Types

<!-- ✅ Good: Specific file types -->
<input type="file" accept="image/jpeg,image/png,image/webp">

<!-- ❌ Bad: No validation -->
<input type="file">

2. Set Reasonable File Size Limits

<!-- ✅ Good: 5MB limit for images -->
<input type="file"
       accept="image/*"
       data-max-file-size="5242880">

<!-- ❌ Bad: Default 10MB might be too large -->
<input type="file" accept="image/*">

3. Use Preview for Better UX

<!-- ✅ Good: Show preview -->
<input type="file"
       data-preview="true"
       accept="image/*">

<!-- ❌ Bad: No visual feedback -->
<input type="file" accept="image/*">

4. Enable Drag & Drop for Multiple Files

<!-- ✅ Good: Drag & drop for multiple files -->
<input type="file"
       multiple
       data-drag-drop="true"
       data-preview="true">

<!-- ❌ Bad: Multiple files without drag & drop -->
<input type="file" multiple>

5. Provide Clear Placeholders

<!-- ✅ Good: Clear instructions -->
<input type="file"
       data-drag-drop="true"
       placeholder="Drag images here or click to browse (max 5MB)">

<!-- ❌ Bad: Generic placeholder -->
<input type="file" data-drag-drop="true">

6. Handle Existing Files Properly

<!-- ✅ Good: Load and allow deletion -->
<input type="file"
       data-attr="value:files"
       data-file-reference="url"
       data-allow-remove-existing="true"
       data-action-url="/api/files">

<!-- ❌ Bad: No way to manage existing files -->
<input type="file" data-attr="value:files">

7. Use Form Integration

<!-- ✅ Good: Integrated with form -->
<form data-form="upload" data-ajax-submit="true">
  <input type="file" name="files" required>
  <button type="submit">Upload</button>
</form>

<!-- ❌ Bad: Manual handling -->
<input type="file" id="files">
<button onclick="uploadFiles()">Upload</button>

8. Provide Feedback

// ✅ Good: User feedback
const instance = FileElementFactory.create(element, {
  onChange: (files) => {
    console.log(`${files.length} file(s) selected`);
  },
  onError: (error) => {
    alert('Error: ' + error.message);
  }
});

// ❌ Bad: Silent errors
const instance = FileElementFactory.create(element, {});

Common Patterns

Pattern 1: User Profile Photo

<form data-form="profile">
  <div class="avatar-upload">
    <label for="avatar">Profile Photo</label>
    <span class="form-control">
      <input type="file"
             id="avatar"
             name="avatar"
             data-preview="true"
             data-attr="value:avatar"
             data-file-reference="url"
             accept="image/*"
             data-max-file-size="2097152">
    </span>
  </div>
</form>

Pattern 2: Document Upload

<form data-form="documents">
  <label for="files">Upload Documents</label>
  <span class="form-control icon-upload">
    <input type="file"
           id="files"
           name="files"
           multiple
           data-preview="true"
           data-drag-drop="true"
           accept="application/pdf,application/msword"
           data-max-file-size="10485760"
           placeholder="Drag PDF or Word documents here">
  </span>
</form>
<form data-form="gallery" data-load-api="/api/gallery">
  <label for="images">Gallery Images</label>
  <span class="form-control">
    <input type="file"
           id="images"
           name="images"
           multiple
           data-preview="true"
           data-drag-drop="true"
           data-sortable="true"
           data-allow-remove-existing="true"
           data-file-reference="id"
           data-action-url="/api/gallery"
           data-attr="value:images"
           accept="image/*"
           placeholder="Drag images to upload">
  </span>
</form>

Troubleshooting

Files Not Uploading

Problem: Files selected but not uploading

Solution:

<!-- Ensure form has correct attributes -->
<form data-form="upload"
      method="POST"
      action="/api/upload"
      data-ajax-submit="true">
  <input type="file" name="files" multiple>
  <button type="submit">Upload</button>
</form>

Preview Not Showing

Problem: Preview not displaying

Solution:

<!-- Ensure data-preview is set -->
<input type="file" data-preview="true">

<!-- Or check if preview container exists -->
<input type="file"
       data-preview="true"
       data-preview-container="#preview">
<div id="preview"></div>

Sortable Not Working

Problem: Cannot reorder files

Solution:

<!-- 1. Include Sortable.js -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>

<!-- 2. Enable sortable -->
<input type="file"
       data-sortable="true"
       data-action-url="/api/files/sort">

File Validation Errors

Problem: Valid files being rejected

Solution:

<!-- Check accept attribute -->
<input type="file" accept="image/*,application/pdf">

<!-- Check file size limit -->
<input type="file" data-max-file-size="10485760">

API Reference Summary

Configuration

interface FileElementConfig {
  // Display
  preview: boolean;
  previewContainer: string | null;
  placeholder: string | null;

  // Interaction
  dragDrop: boolean;
  sortable: boolean;
  allowRemoveExisting: boolean;
  downloadEnabled: boolean;

  // Validation
  maxFileSize: number;
  allowedMimeTypes: string[];

  // Data
  fileReference: string;
  existingFiles: FileData[];

  // Server
  actionUrl: string;

  // Callbacks
  onChange: (files: File[], element: HTMLElement) => void;
  onError: (error: Error) => void;
}

interface FileData {
  id?: string;
  url: string;
  name: string;
  size?: number;
}

Methods

class FileElementFactory {
  // Instance Methods
  clearFiles(): void;
  validateFiles(): boolean;
  upload(options: UploadOptions): Promise<UploadResult>;

  // Static Methods
  static validateFile(file: File, config: Config): Promise<boolean>;
  static formatFileSize(bytes: number): string;
  static getFileIcon(filename: string): string;
  static isValidFileType(file: File, acceptedTypes: string[]): boolean;
}