ostovjs
v1.7.8
Published
Give your JS App some Ostov with Models, Views, Collections, and Events.
Maintainers
Readme
Ostov.js
A modern reimagining of Backbone.js — same proven MVC patterns, no jQuery, no Underscore, ships as a native ES module.
- Zero hard dependencies (includes a minimal built-in utility layer)
- Native ES module — works in browsers and any bundler (Vite, Rollup, webpack 5+)
- Full TypeScript types included
- Familiar Backbone API — drop-in for most use cases
Install
npm install ostovjsOr grab the file directly:
- ostov.js — development build
- ostov.min.js — production build
Usage
npm / bundler
import { Model, Collection, View, Router, Events } from 'ostovjs';Or import the full namespace:
import Ostov from 'ostovjs';Browser — ES module
<script type="module">
import { Model, Collection } from './ostov.js';
</script>Browser — classic script tag
<script src="ostov.js"></script>
<!-- Ostov is now available as a global variable -->Core concepts
Model
Manages data and business logic. Triggers "change" events when attributes are modified.
import { Model } from 'ostovjs';
const Book = Model.extend({
defaults: {
title: '',
author: '',
read: false
}
});
const book = new Book({ title: 'Dune', author: 'Herbert' });
book.on('change:title', (model, value) => {
console.log('Title changed to:', value);
});
book.set('title', 'Dune Messiah'); // → "Title changed to: Dune Messiah"
book.get('author'); // → "Herbert"Collection
A group of models with helpers for sorting, filtering, and syncing with the server.
import { Model, Collection } from 'ostovjs';
const Task = Model.extend({
defaults: { title: '', done: false }
});
const TaskList = Collection.extend({
model: Task,
url: '/api/tasks',
pending() {
return this.filter(t => !t.get('done'));
}
});
const tasks = new TaskList([
{ title: 'Write docs' },
{ title: 'Ship it', done: true }
]);
console.log(tasks.length); // 2
console.log(tasks.pending().length); // 1REST API integration
Point a Collection (or Model) at a URL and Ostov handles the REST mapping automatically:
import { Collection } from 'ostovjs';
class Books extends Collection {
url = '/api/books';
}
const books = new Books();
books.fetch(); // GET /api/books
// If the API wraps data in metadata, use parse():
class Books extends Collection {
url = '/api/books';
parse(data) {
return data.books; // unwrap { page, total, books: [...] }
}
}HTTP methods map to model/collection methods:
GET /books/ → collection.fetch()
POST /books/ → collection.create()
GET /books/1 → model.fetch()
PUT /books/1 → model.save()
DEL /books/1 → model.destroy()View
Manages rendering and user interaction within a DOM element. Each View owns its own el and listens to model events to re-render itself.
import { Model, View } from 'ostovjs';
const Book = Model.extend({
defaults: { title: '', author: '' }
});
const BookView = View.extend({
tagName: 'article',
initialize() {
this.listenTo(this.model, 'change', this.render);
},
render() {
this.el.innerHTML = `
<h2>${this.model.get('title')}</h2>
<p>${this.model.get('author')}</p>
`;
return this;
}
});
const book = new Book({ title: 'Dune', author: 'Herbert' });
const view = new BookView({ model: book });
document.body.appendChild(view.render().el);
book.set('title', 'Dune Messiah'); // view re-renders automaticallyDOM events are declared in an events hash:
const BookView = View.extend({
events: {
'click .btn-read': 'markRead',
'dblclick h2': 'editTitle'
},
markRead() {
this.model.set('read', true);
}
});TypeScript Support
Ostov is written in TypeScript and provides excellent support for type safety.
Strongly Typed Models
Define attributes using a generic parameter for autocomplete and type checking.
import { Model } from 'ostovjs';
interface UserAttrs {
name: string;
age: number;
}
class User extends Model<UserAttrs> {
defaults() {
return { name: 'Unknown', age: 0 };
}
}
const user = new User({ name: 'Dmitry' });
const age = user.get('age'); // number
user.set('name', 'Dima'); // OK
user.set('wrong', 123); // TypeScript Error!Typed Collections
Collections can be typed to know their model kind.
import { Collection } from 'ostovjs';
class Users extends Collection<User> {
model = User;
}
const users = new Users();
users.add({ name: 'Alice', age: 25 });
const first = users.at(0); // User modelTyped Views
Views can specify model and collection types for better internal typing.
import { View } from 'ostovjs';
class UserView extends View<User, Users> {
render() {
this.el.innerHTML = this.model.get('name');
return this;
}
}Events
Mix event handling into any object:
import { Events } from 'ostovjs';
const bus = Object.assign({}, Events);
bus.on('user:login', user => {
console.log('Welcome,', user.name);
});
bus.trigger('user:login', { name: 'Alice' });Listen to multiple events at once:
book.on('change:title change:author', () => console.log('metadata changed'));
// Or with an event map:
book.on({
'change:title': titleView.render,
'change:author': authorView.render,
'destroy': bookView.remove
});Router
Keeps your app in sync with the browser URL:
import { Router } from 'ostovjs';
const AppRouter = Router.extend({
routes: {
'': 'home',
'books': 'bookList',
'books/:id': 'bookDetail'
},
home() { console.log('home'); },
bookList() { console.log('all books'); },
bookDetail(id) { console.log('book', id); }
});
const router = new AppRouter();
Ostov.history.start();License
MIT
