2805 Custom Components
Custom Components
Introduction to Custom Components
Custom components allow you to create completely unique functionality using HTML, CSS, and JavaScript. When built-in components and plugins don't meet your specific needs, custom components provide unlimited flexibility to build exactly what you envision. This article teaches you how to create powerful custom components from scratch.
What Are Custom Components?
Definition
Custom components are user-created elements built with standard web technologies (HTML, CSS, JavaScript) that integrate seamlessly with Tadabase. They can display data, interact with users, trigger actions, and communicate with your Tadabase application.
Capabilities
- Display Data: Show Tadabase data in unique ways
- User Interaction: Capture input and trigger events
- Visualizations: Create custom charts and graphics
- Integrations: Connect to external APIs
- Calculations: Perform complex computations
- Animations: Dynamic, interactive experiences
When to Create Custom Components
Create custom components when:
Built-In Components Insufficient:
- Need specific visualization not available
- Require unique user interaction
- Want custom business logic
Plugins Don't Exist:
- No plugin for your use case
- Need proprietary functionality
- Want complete control
Business Requirements:
- Industry-specific needs
- Unique workflows
- Custom calculations or logic
Branding & Design:
- Exact visual requirements
- Unique user experience
- Custom interactions
Custom Component Structure
Basic HTML Structure
Custom components use standard HTML:
<div class="custom-component">
<div class="component-header">
<h3>Component Title</h3>
</div>
<div class="component-body">
<!-- Component content -->
<div id="data-container"></div>
</div>
<div class="component-footer">
<button id="action-button">Click Me</button>
</div>
</div>
Adding CSS
Style your component:
<style>
.custom-component {
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
padding: 20px;
margin: 20px 0;
}
.component-header {
border-bottom: 2px solid #e5e7eb;
padding-bottom: 15px;
margin-bottom: 20px;
}
.component-header h3 {
margin: 0;
color: #1f2937;
font-size: 20px;
font-weight: 600;
}
.component-body {
min-height: 200px;
padding: 20px 0;
}
.component-footer {
margin-top: 20px;
text-align: right;
}
#action-button {
background: #3b82f6;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
}
#action-button:hover {
background: #2563eb;
transform: translateY(-1px);
}
</style>
Adding JavaScript
Add interactivity:
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get component elements
const dataContainer = document.getElementById('data-container');
const actionButton = document.getElementById('action-button');
// Initialize component
function init() {
loadData();
setupEventListeners();
}
// Load data
function loadData() {
// Component logic here
dataContainer.innerHTML = '<p>Data loaded!</p>';
}
// Setup event listeners
function setupEventListeners() {
actionButton.addEventListener('click', function() {
handleButtonClick();
});
}
// Handle button click
function handleButtonClick() {
alert('Button clicked!');
}
// Initialize on load
init();
});
</script>
Accessing Tadabase Data
Component Helpers
Tadabase provides helpers to access data in custom components:
Current Record Data:
/* Access field values from current record */
const customerName = '{Customer Name}';
const orderTotal = '{Order Total}';
const orderDate = '{Order Date}';
// Use in component
document.getElementById('customer-name').textContent = customerName;
document.getElementById('total').textContent = '$' + orderTotal;
Connected Record Data:
/* Access connected record data */
const companyName = '{Customer.Company Name}';
const accountManager = '{Customer.Account Manager}';
// Display related data
document.getElementById('company').textContent = companyName;
User Information:
/* Access current user data */
const userName = '{User.Full Name}';
const userEmail = '{User.Email}';
const userRole = '{User.Role}';
// Personalize component
document.getElementById('welcome').textContent = 'Welcome, ' + userName;
API Integration
Fetch data from Tadabase API:
/* Example: Fetch records from Tadabase */
async function fetchRecords() {
const apiKey = 'YOUR_API_KEY';
const tableId = 'YOUR_TABLE_ID';
try {
const response = await fetch(`https://api.tadabase.io/api/v1/data-tables/${tableId}/records`, {
headers: {
'X-Tadabase-App-id': 'YOUR_APP_ID',
'X-Tadabase-App-Key': apiKey,
'X-Tadabase-App-Secret': 'YOUR_APP_SECRET'
}
});
const data = await response.json();
displayRecords(data.records);
} catch (error) {
console.error('Error fetching data:', error);
}
}
function displayRecords(records) {
const container = document.getElementById('records-container');
records.forEach(record => {
const div = document.createElement('div');
div.className = 'record-item';
div.textContent = record.field_name;
container.appendChild(div);
});
}
Example 1: Custom Dashboard Widget
Create a KPI dashboard widget:
<div class="kpi-widget">
<div class="kpi-header">
<h4>Monthly Revenue</h4>
</div>
<div class="kpi-value">
<span id="revenue-amount">$0</span>
</div>
<div class="kpi-change">
<span id="change-indicator"></span>
<span id="change-value">0%</span>
</div>
</div>
<style>
.kpi-widget {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.kpi-header h4 {
margin: 0 0 20px 0;
font-size: 16px;
font-weight: 500;
opacity: 0.9;
}
.kpi-value {
font-size: 48px;
font-weight: 700;
margin-bottom: 15px;
}
.kpi-change {
font-size: 18px;
display: flex;
align-items: center;
gap: 8px;
}
.kpi-change.positive {
color: #10b981;
}
.kpi-change.negative {
color: #ef4444;
}
</style>
<script>
// Calculate and display KPI
function updateKPI() {
const currentRevenue = parseFloat('{Sum of This Month Revenue}');
const lastMonthRevenue = parseFloat('{Sum of Last Month Revenue}');
// Display revenue
document.getElementById('revenue-amount').textContent =
'$' + currentRevenue.toLocaleString();
// Calculate change percentage
const change = ((currentRevenue - lastMonthRevenue) / lastMonthRevenue) * 100;
const changeValue = Math.abs(change).toFixed(1);
const changeElement = document.querySelector('.kpi-change');
const indicator = document.getElementById('change-indicator');
const value = document.getElementById('change-value');
if (change >= 0) {
changeElement.classList.add('positive');
indicator.textContent = '↑';
value.textContent = '+' + changeValue + '%';
} else {
changeElement.classList.add('negative');
indicator.textContent = '↓';
value.textContent = '-' + changeValue + '%';
}
}
updateKPI();
</script>
Example 2: Custom Chart
Create custom visualization using Chart.js:
<div class="chart-container">
<canvas id="salesChart"></canvas>
</div>
<style>
.chart-container {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
</style>
<!-- Include Chart.js library -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Prepare data from Tadabase
const salesData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Monthly Sales',
data: [
parseFloat('{Jan Total}'),
parseFloat('{Feb Total}'),
parseFloat('{Mar Total}'),
parseFloat('{Apr Total}'),
parseFloat('{May Total}'),
parseFloat('{Jun Total}')
],
backgroundColor: 'rgba(59, 130, 246, 0.2)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 2,
tension: 0.4
}]
};
// Create chart
const ctx = document.getElementById('salesChart').getContext('2d');
const salesChart = new Chart(ctx, {
type: 'line',
data: salesData,
options: {
responsive: true,
plugins: {
legend: {
display: true,
position: 'top'
},
title: {
display: true,
text: 'Sales Trend',
font: {
size: 18,
weight: 'bold'
}
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
}
}
}
}
}
});
</script>
Example 3: Custom Calculator
Interactive calculator component:
<div class="calculator-widget">
<h3>Loan Calculator</h3>
<div class="calc-input-group">
<label>Loan Amount:</label>
<input type="number" id="loanAmount" value="100000">
</div>
<div class="calc-input-group">
<label>Interest Rate (%):</label>
<input type="number" id="interestRate" value="5.5" step="0.1">
</div>
<div class="calc-input-group">
<label>Term (years):</label>
<input type="number" id="loanTerm" value="30">
</div>
<button id="calculateBtn">Calculate</button>
<div class="calc-result" id="result" style="display:none;">
<h4>Monthly Payment</h4>
<div class="result-amount" id="monthlyPayment">$0</div>
<div class="result-details">
<p>Total Interest: <span id="totalInterest">$0</span></p>
<p>Total Paid: <span id="totalPaid">$0</span></p>
</div>
</div>
</div>
<style>
.calculator-widget {
max-width: 500px;
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.calculator-widget h3 {
margin: 0 0 25px 0;
color: #1f2937;
}
.calc-input-group {
margin-bottom: 20px;
}
.calc-input-group label {
display: block;
margin-bottom: 8px;
color: #4b5563;
font-weight: 500;
}
.calc-input-group input {
width: 100%;
padding: 12px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.2s;
}
.calc-input-group input:focus {
outline: none;
border-color: #3b82f6;
}
#calculateBtn {
width: 100%;
background: #3b82f6;
color: white;
border: none;
padding: 15px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
#calculateBtn:hover {
background: #2563eb;
transform: translateY(-1px);
}
.calc-result {
margin-top: 30px;
padding: 25px;
background: #f0f9ff;
border-radius: 12px;
border: 2px solid #3b82f6;
}
.calc-result h4 {
margin: 0 0 15px 0;
color: #1e40af;
}
.result-amount {
font-size: 36px;
font-weight: 700;
color: #1e40af;
margin-bottom: 20px;
}
.result-details p {
margin: 8px 0;
color: #4b5563;
}
.result-details span {
font-weight: 600;
color: #1f2937;
}
</style>
<script>
document.getElementById('calculateBtn').addEventListener('click', function() {
// Get input values
const principal = parseFloat(document.getElementById('loanAmount').value);
const annualRate = parseFloat(document.getElementById('interestRate').value);
const years = parseFloat(document.getElementById('loanTerm').value);
// Calculate monthly payment
const monthlyRate = (annualRate / 100) / 12;
const numPayments = years * 12;
const monthlyPayment = principal *
(monthlyRate * Math.pow(1 + monthlyRate, numPayments)) /
(Math.pow(1 + monthlyRate, numPayments) - 1);
const totalPaid = monthlyPayment * numPayments;
const totalInterest = totalPaid - principal;
// Display results
document.getElementById('monthlyPayment').textContent =
'$' + monthlyPayment.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('totalInterest').textContent =
'$' + totalInterest.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('totalPaid').textContent =
'$' + totalPaid.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('result').style.display = 'block';
});
// Trigger calculation on input change
['loanAmount', 'interestRate', 'loanTerm'].forEach(id => {
document.getElementById(id).addEventListener('input', function() {
if (document.getElementById('result').style.display === 'block') {
document.getElementById('calculateBtn').click();
}
});
});
</script>
Example 4: Progress Tracker
Visual progress tracker:
<div class="progress-tracker">
<h3>Project Completion</h3>
<div class="progress-steps">
<div class="step completed">
<div class="step-icon">✓</div>
<div class="step-label">Planning</div>
</div>
<div class="step-line completed"></div>
<div class="step completed">
<div class="step-icon">✓</div>
<div class="step-label">Design</div>
</div>
<div class="step-line active"></div>
<div class="step active">
<div class="step-icon">3</div>
<div class="step-label">Development</div>
</div>
<div class="step-line"></div>
<div class="step">
<div class="step-icon">4</div>
<div class="step-label">Testing</div>
</div>
<div class="step-line"></div>
<div class="step">
<div class="step-icon">5</div>
<div class="step-label">Launch</div>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="progress-percentage">
<span id="percentageText">60%</span> Complete
</div>
</div>
<style>
.progress-tracker {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.progress-tracker h3 {
margin: 0 0 30px 0;
}
.progress-steps {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
flex: 0 0 auto;
}
.step-icon {
width: 50px;
height: 50px;
border-radius: 50%;
background: #e5e7eb;
color: #9ca3af;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 20px;
margin-bottom: 10px;
transition: all 0.3s;
}
.step.completed .step-icon {
background: #10b981;
color: white;
}
.step.active .step-icon {
background: #3b82f6;
color: white;
animation: pulse 2s infinite;
}
.step-label {
font-size: 14px;
color: #6b7280;
}
.step.completed .step-label,
.step.active .step-label {
color: #1f2937;
font-weight: 600;
}
.step-line {
flex: 1;
height: 4px;
background: #e5e7eb;
margin: 0 10px;
position: relative;
top: -20px;
}
.step-line.completed {
background: #10b981;
}
.step-line.active {
background: linear-gradient(to right, #10b981 0%, #3b82f6 100%);
}
.progress-bar {
height: 10px;
background: #e5e7eb;
border-radius: 10px;
overflow: hidden;
margin-bottom: 15px;
}
.progress-fill {
height: 100%;
background: linear-gradient(to right, #10b981 0%, #3b82f6 100%);
border-radius: 10px;
width: 60%;
transition: width 0.5s ease;
}
.progress-percentage {
text-align: center;
font-size: 18px;
color: #4b5563;
}
.progress-percentage span {
font-weight: 700;
color: #3b82f6;
font-size: 24px;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
</style>
<script>
// Calculate progress based on Tadabase data
const totalTasks = parseInt('{Total Task Count}');
const completedTasks = parseInt('{Completed Task Count}');
const percentage = Math.round((completedTasks / totalTasks) * 100);
// Update progress bar
document.getElementById('progressFill').style.width = percentage + '%';
document.getElementById('percentageText').textContent = percentage + '%';
</script>
Testing Custom Components
Testing Checklist
Before deploying:
- ☐ Test with real data
- ☐ Verify all calculations correct
- ☐ Check responsive design
- ☐ Test on multiple browsers
- ☐ Verify mobile compatibility
- ☐ Test error scenarios
- ☐ Check performance
- ☐ Validate accessibility
- ☐ Review security
- ☐ Get user feedback
Debugging Techniques
Use Browser Console:
console.log('Component loaded');
console.log('Data:', dataValue);
console.error('Error occurred:', error);
Add Error Handling:
try {
// Component logic
processData();
} catch (error) {
console.error('Component error:', error);
displayErrorMessage('Something went wrong. Please try again.');
}
Best Practices
Code Organization
- Separate Concerns: HTML structure, CSS styling, JS logic
- Use Comments: Document complex logic
- Consistent Naming: Clear, descriptive names
- Modular Functions: Break into small, reusable functions
- Avoid Global Variables: Use local scope
Performance
- Minimize DOM Manipulation: Batch updates
- Optimize Images: Compress and resize
- Lazy Load: Load content when needed
- Cache Data: Avoid redundant API calls
- Debounce Events: Limit rapid firing
Security
- Validate Input: Never trust user data
- Sanitize Output: Prevent XSS attacks
- Secure API Keys: Don't expose in client code
- Use HTTPS: Encrypt data transmission
- Error Messages: Don't reveal sensitive info
Accessibility
- Semantic HTML: Use proper elements
- ARIA Labels: Add for screen readers
- Keyboard Navigation: Support tab/enter
- Color Contrast: Ensure readability
- Alt Text: Describe images
Common Pitfalls
Avoid:
- Hardcoded Values: Use field references
- Browser-Specific Code: Ensure cross-browser compatibility
- Blocking Operations: Use async for API calls
- Missing Error Handling: Always catch errors
- Poor Mobile Experience: Test on small screens
- Inline Styles: Use CSS classes
- Memory Leaks: Clean up event listeners
Next Steps
You now know how to create powerful custom components. The next article covers advanced styling techniques including responsive design, animations, and mobile optimization.
Next: Advanced Styling - Responsive Design and Optimization
Hands-On Exercise (To Be Added)
Exercise placeholders will include practical activities such as:
- Creating a custom dashboard widget
- Building an interactive calculator
- Developing a progress tracker
- Integrating external JavaScript library
Knowledge Check (To Be Added)
Quiz questions will test understanding of:
- When to create custom components
- HTML/CSS/JavaScript integration
- Accessing Tadabase data in components
- Best practices and security
We'd love to hear your feedback.