appblocks
Version:
A lightweight javascript library for building micro apps for the front-end.
580 lines (463 loc) ⢠14.6 kB
Markdown
# Introduction
## Getting Started with AppBlocks
AppBlocks is a tiny, fast, and lightweight JavaScript library for building micro applications. It's designed to be used primarily as a script tag to enhance web pages with self-contained micro applications.
The goal of AppBlocks is to provide all the necessary ingredients to develop micro apps in websites while being ridiculously easy to integrate, practical, and small.
> **New to AppBlocks?** Read about the [AppBlocks use case](whyappblocks.md) to understand when and why you might want to use it.
## Installation
### Option 1: CDN (Quickest)
Add AppBlocks directly to your HTML:
```html
<script src="https://cdn.jsdelivr.net/npm/appblocks@2.1.1/dist/appblocks.min.js"></script>
```
### Option 2: NPM
Install via npm for use with bundlers:
```bash
npm install appblocks
```
Then import in your JavaScript:
```javascript
import { AppBlock } from 'appblocks';
```
### Option 3: Direct Download
Download the latest version and include it in your HTML:
```html
<!-- Development version -->
<script src="/path/to/appblocks.umd.js"></script>
<!-- Minified production version -->
<script src="/path/to/appblocks.min.js"></script>
```
## Your First AppBlock
Let's build a simple interactive app step by step. We'll start with an empty HTML page:
```html
<html lang="en">
<head>
<title>My first AppBlocks app</title>
</head>
<body>
<!-- Load AppBlocks. -->
<script src="https://cdn.jsdelivr.net/npm/appblocks@2.1.1/dist/appblocks.min.js"></script>
<script>
// This is where we will write our AppBlock code
</script>
</body>
</html>
```
### Step 1: Create the Container and Template
An AppBlock needs two elements:
1. **Container** - Where the app will render
2. **Template** - What the app will display
Add these inside the `<body>`, before the script tags:
```html
<!-- Container where our app will render -->
<div id="app"></div>
<!-- Template containing our app's markup -->
<template id="appTemplate">
<h1>{data.message}</h1>
<p>You've been here {data.visits} times.</p>
<button id="increment-btn">Visit Again</button>
</template>
```
> **Placeholders**: Notice the `{data.message}` and `{data.visits}` syntax? These are placeholders that will be replaced with actual data values.
### Step 2: Initialize Your AppBlock
Add this JavaScript inside the `<script>` tag at the bottom:
```html
<script>
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
message: "Welcome to AppBlocks!",
visits: 0
},
events: {
'click #increment-btn': function() {
var currentVisits = this.Parent.data.visits;
this.Parent.setData({ visits: currentVisits + 1 });
}
}
});
</script>
```
### Step 3: See It in Action!
Reload the page and click the button. Watch the visit count increase automatically!
> **How it works**: When you call `setData()`, AppBlocks updates the data and automatically re-renders the interface to reflect the changes.
### Complete Example
Here's the full working code:
```html
<html lang="en">
<head>
<title>My First AppBlock</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
#app { max-width: 400px; margin: 0 auto; }
button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<div id="app"></div>
<template id="appTemplate">
<h1>{data.message}</h1>
<p>You've been here {data.visits} times.</p>
<button id="increment-btn">Visit Again</button>
</template>
<script src="https://cdn.jsdelivr.net/npm/appblocks@2.1.1/dist/appblocks.min.js"></script>
<script>
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
message: "Welcome to AppBlocks!",
visits: 0
},
events: {
'click #increment-btn': function() {
var currentVisits = this.Parent.data.visits;
this.Parent.setData({ visits: currentVisits + 1 });
}
}
});
</script>
</body>
</html>
```
### Try It Yourself
Open your browser's console and experiment:
```javascript
// Update the message
app.setData({ message: "Hello from the console!" });
// Reset the visits
app.setData({ visits: 0 });
// Update multiple properties at once
app.setData({
message: "AppBlocks is awesome!",
visits: 100
});
```
## Next Steps
Now that you have your first AppBlock running, explore these core concepts:
- **[Data Management](data.md)** - Learn how to work with data effectively
- **[Filters](filters.md)** - Transform data before displaying it
- **[Directives](directives.md)** - Control element visibility with `if` conditionals and loops
- **[Methods](methods.md)** - Organize your application logic
- **[Event Handling](events.md)** - Respond to user interactions
- **[HTTP Requests](requests.md)** - Fetch data from APIs
## Core Concepts Overview
### Filters - Transform Your Data
Filters are functions that transform values before displaying them. They're perfect for formatting data without cluttering your templates.
**Example: filters**
```js
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
name: 'john doe',
price: 49.99,
tax: 23,
rawText: ' hello world '
},
filters: {
uppercase(self, value) {
return value.toUpperCase();
},
afterTaxes(self, value) {
return value + (value * self.data.tax / 100);
},
currency(self, value) {
return '$ ' + value.toFixed(2);
}
}
});
```
**Usage in templates:**
```html
<template id="appTemplate">
<p>Welcome, {data.name|uppercase}!</p>
<p>Total: {data.price|currency}</p>
<!-- Chain multiple filters -->
<p>With tax {data.tax}%: {data.price|afterTaxes|currency}</p>
</template>
```
**Output:**
```
Welcome, JOHN DOE!
Total: $ 49.99
With tax 23%: $ 61.49
```
> Notice the `self` parameter on every filter. The first parameter in filters and methods is your app's instance. You can name it however you want, like `self`, `app` etc. AppBlocks will pass your app's instance to the first parameter automatically when you call a method or filter from the template and you can use it inside your method/filter to access data and methods from your app (just like in the `afterTaxes` filter).
[š Read more about Filters](filters.md#filters)
### Directives - Control Your Template
Directives are special attributes that control element visibility and behavior. They make it easy to build dynamic interfaces.
#### c-if & c-ifnot - Conditional Rendering
Show or hide elements based on conditions:
```js
var app = new AppBlock({
data: {
isLoggedIn: false,
age: 25,
score: 85
}
});
```
```html
<template id="appTemplate">
<!-- Simple boolean check -->
<p c-if="data.isLoggedIn">Welcome back!</p>
<p c-ifnot="data.isLoggedIn">Please log in</p>
<!-- Comparison operators -->
<p c-if="data.age >= 18">You can vote</p>
<p c-if="data.score > 60">You passed!</p>
<!-- Complex expressions -->
<p c-if="data.age >= 18 && data.score > 60">Congratulations, adult graduate!</p>
</template>
```
[š Read more about Conditional Rendering](directives.md#c-if)
#### c-for - Loop Rendering
Display lists and iterate over data:
```js
var app = new AppBlock({
data: {
users: [
{ name: 'Alice', role: 'Admin' },
{ name: 'Bob', role: 'User' },
{ name: 'Charlie', role: 'User' }
],
settings: {
theme: 'dark',
language: 'en',
notifications: true
}
}
});
```
**Arrays:**
```html
<ul>
<li c-for="user in data.users">
{user.name} - {user.role}
</li>
</ul>
```
**Output:**
```
⢠Alice - Admin
⢠Bob - User
⢠Charlie - User
```
**Objects:**
```html
<div c-for="key, value in data.settings">
<strong>{key}:</strong> {value}
</div>
```
**Output:**
```
theme: dark
language: en
notifications: true
```
[š Read more about Directives](directives.md)
### Event Handling - Respond to User Actions
AppBlocks makes event handling clean and organized. Define all your event listeners in the `events` object:
```js
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
count: 0,
message: ''
},
events: {
'click #increment': function(e, element) {
this.Parent.setData({
count: this.Parent.data.count + 1
});
},
'click #decrement': function(e, element) {
this.Parent.setData({
count: this.Parent.data.count - 1
});
},
'input #message-input': function(e, element) {
this.Parent.setData({
message: element.value
});
},
// Event delegation with complex selectors
'click .todo-list li .delete-btn': function(e, element) {
// Handle delete button clicks on todo items
}
}
});
```
```html
<template id="appTemplate">
<div>
<p>Count: {data.count}</p>
<button id="increment">+</button>
<button id="decrement">-</button>
</div>
<div>
<input id="message-input" type="text" placeholder="Type something..." value="{data.message}">
<p>You typed: {data.message}</p>
</div>
</template>
```
**Event format:** `"eventName selector"`
The selector can include spaces and use descendant combinators for complex element targeting.
[š Read more about Event Handling](api.md#events)
### Methods - Organize Your Logic
Methods are where your application logic lives. They keep your code DRY (Don't Repeat Yourself) and reusable.
```js
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
todos: [],
newTodo: ''
},
methods: {
addTodo(self) {
if (self.data.newTodo.trim()) {
var updatedTodos = self.data.todos.concat({
id: Date.now(),
text: self.data.newTodo,
done: false
});
self.setData({
todos: updatedTodos,
newTodo: ''
});
}
},
removeTodo(self, id) {
var updatedTodos = self.data.todos.filter(function(todo) {
return todo.id !== id;
});
self.setData({ todos: updatedTodos });
},
toggleTodo(self, id) {
var updatedTodos = self.data.todos.map(function(todo) {
if (todo.id === id) {
return { ...todo, done: !todo.done };
}
return todo;
});
self.setData({ todos: updatedTodos });
}
},
events: {
'click #add-btn': function() {
this.Parent.methods.addTodo(this.Parent);
},
'input #new-todo-input': function(e, element) {
this.Parent.setData({ newTodo: element.value });
},
'click .remove-btn': function(e, element) {
var id = parseInt(element.dataset.id);
this.Parent.methods.removeTodo(this.Parent, id);
}
}
});
```
```html
<template id="appTemplate">
<input id="new-todo-input" type="text" placeholder="New todo" value="{data.newTodo}">
<button id="add-btn">Add Todo</button>
<ul>
<li c-for="todo in data.todos">
{todo.text}
<button class="remove-btn" data-id="{todo.id}">Remove</button>
</li>
</ul>
</template>
```
**Calling methods:**
- From events: `this.Parent.methods.methodName(this.Parent, arg1, arg2)`
- From directives (c-if, c-ifnot, c-for): `methodName(arg1, arg2)` (app instance auto-injected)
- From placeholders: `{methodName(arg1, arg2)}` (app instance auto-injected)
[š Read more about Methods](methods.md#methods)
### HTTP Requests - Fetch Data from APIs
AppBlocks provides built-in methods for making HTTP requests with automatic state management. You can use either `fetch` or Axios.
#### Using fetchRequest
```js
var app = new AppBlock({
el: document.getElementById('app'),
template: document.getElementById('appTemplate'),
data: {
users: [],
errorMessage: ''
},
events: {
'click #load-users': function() {
var app = this.Parent;
app.fetchRequest(
'https://jsonplaceholder.typicode.com/users',
{ method: 'GET' },
{
success: function(data) {
app.setData({ users: data });
},
error: function(err) {
app.setData({ errorMessage: err.message });
},
finally: function() {
console.log('Request completed');
}
}
);
}
}
});
```
#### Template with Loading States
```html
<template id="appTemplate">
<button id="load-users">Load Users</button>
<!-- Loading state -->
<p c-if="isLoading()">Loading users...</p>
<!-- Error state -->
<div c-if="hasError()">
<p style="color: red;">Error: {data.errorMessage}</p>
</div>
<!-- Success state -->
<div c-if="isSuccessful()">
<h2>Users ({data.users.length})</h2>
<ul>
<li c-for="user in data.users">
{user.name} - {user.email}
</li>
</ul>
</div>
</template>
```
**Built-in state methods:**
- `isLoading()` - Returns `true` while request is in progress
- `isSuccessful()` - Returns `true` when request succeeds
- `hasError()` - Returns `true` when request fails
[š Read more about Requests](requests.md#http-requests)
## What's Next?
You now have a solid understanding of AppBlocks basics! Here are some next steps:
### Deep Dive into Features
- **[Data Management](data.md#data-management)** - Learn `setData()`, direct updates, and data patterns
- **[Filters](filters.md#filters)** - Create custom filters and chain transformations
- **[Directives](directives.md#directives)** - Master `c-if`, `c-for`, and custom directives
- **[Methods](methods.md#methods)** - Build reusable application logic
- **[HTTP Requests](requests.md#http-requests)** - Work with APIs using fetch and Axios
- **[Utilities](utils.md#utilities)** - Helper functions for DOM manipulation
- **[API Reference](api.md#api-reference)** - Complete API documentation
### Advanced Topics
- Custom placeholder delimiters
- Expression evaluation with built-ins
- Render engine options (Idiomorph vs plain)
- Custom directives and filters
- Performance optimization
### Examples
Check out practical examples and common patterns:
- Todo List Application
- Form Validation
- Search with Debounce
- Data Tables
- Real-time Updates
Happy coding with AppBlocks! š