Collections

Backbone.js-style Collections for managing model data

Interactive Examples

Try these examples with your saved connection. If you haven't connected yet, visit the Try It Out page first.

Basic Fetch

Fetch the first page of projects using Collections.

const projects = api.collections.project;
await projects.fetch({ limit: 5 });

console.log('Loaded:', projects.length);
console.log('Has more:', projects.hasMore);
console.log('Projects:', projects.models);

Fast ID Lookup (O(1))

Use the built-in ID index for instant lookups.

const projects = api.collections.project;
await projects.fetch({ limit: 50 });

// O(1) lookup by ID
const project = projects.get(1);
console.log('Found:', project?.name);

// Check if ID exists
console.log('Has ID 1:', projects.has(1));
console.log('Has ID 999:', projects.has(999));

Client-Side Filtering

Filter already-loaded data using where() and filter().

const projects = api.collections.project;
await projects.fetch({ limit: 50 });

// Filter by property
const utcProjects = projects.where({ timezone: 'UTC' });

// Custom filter
const recentProjects = projects.filter(p => {
  const created = new Date(p.created_at);
  const monthAgo = new Date();
  monthAgo.setMonth(monthAgo.getMonth() - 1);
  return created > monthAgo;
});

console.log('UTC projects:', utcProjects.length);
console.log('Recent projects:', recentProjects.length);

Pluck - Extract Property

Extract a specific property from all models.

const projects = api.collections.project;
await projects.fetch({ limit: 20 });

// Extract all project names
const names = projects.pluck('name');
console.log('Names:', names);

// Extract all timezones
const timezones = projects.pluck('timezone');
console.log('Timezones:', timezones);

GroupBy - Group by Property

Group models by a property value.

const projects = api.collections.project;
await projects.fetch({ limit: 50 });

// Group by timezone
const byTimezone = projects.groupBy('timezone');

Object.entries(byTimezone).forEach(([tz, projects]) => {
  console.log(`${tz}: ${projects.length} projects`);
});

SortBy - Sort Collection

Sort models by a property or function.

const projects = api.collections.project;
await projects.fetch({ limit: 10 });

// Sort by name
const sorted = projects.sortBy('name');
console.log('Sorted:', sorted.map(p => p.name));

// Sort by custom function
const byNameLength = projects.sortBy(p => p.name.length);
console.log('By length:', byNameLength.map(p => p.name));

Pagination - Load More

Fetch additional pages using fetchNext().

const projects = api.collections.project;

// Fetch first page
await projects.fetch({ limit: 5 });
console.log('Page 1:', projects.length);

// Check if more pages exist
if (projects.hasMore) {
  // Fetch next page (appends to collection)
  await projects.fetchNext();
  console.log('After page 2:', projects.length);
}

Why Collections?

Instead of making raw HTTP requests, you work with Collections that:

  • Handle pagination automatically - Use fetch(), fetchNext(), or fetchAll()
  • Provide fast lookups - O(1) ID-based indexing with get(id)
  • Support filtering - Use where(), filter(), find() for client-side operations
  • Include metadata - Track hasMore, pagination state, and more
  • Offer array methods - Map, filter, reduce, and 50+ utility methods

Available Collections

Access collections through the API instance:

import { initializeSGERP } from 'sgerp-frontend-lib';

const api = initializeSGERP({ /* config */ });

// Access collections
const users = api.collections.user;
const projects = api.collections.project;
const members = api.collections.projectmember;

Currently supported collections:

  • user - For managing user models
  • project - For managing project models
  • projectmember - For managing project member models

Fetching Data

Collections provide three methods for fetching data:

fetch()

Fetch the first page of models:

// Fetch first 20 projects
await projects.fetch({ limit: 20 });

// Access the models
console.log('Total loaded:', projects.length);
projects.forEach(project => {
  console.log(project.name);
});

fetchNext()

Fetch the next page and append to the collection:

// Fetch first page
await projects.fetch({ limit: 20 });

// Check if there are more
if (projects.hasMore) {
  // Fetch and append next page
  await projects.fetchNext();
  console.log('Now have:', projects.length, 'projects');
}

fetchAll()

Fetch all pages automatically:

// Fetch all projects across all pages
await projects.fetchAll();
console.log('Total projects:', projects.length);

Accessing Models

Collections provide multiple ways to access models:

By ID (Index Lookup)

The fastest way to find a model by ID:

await projects.fetch({ limit: 100 });

// Get project with ID 1 (O(1) lookup)
const project = projects.get(1);
if (project) {
  console.log('Found:', project.name);
}

// Check if collection has a project
if (projects.has(42)) {
  console.log('Project 42 exists');
}

By Position

Access models by array index:

// Get first project
const first = projects.at(0);

// Get last project
const last = projects.at(projects.length - 1);

Direct Array Access

Access the underlying models array:

// Get all models
const allProjects = projects.models;

// Iterate
projects.forEach((project, index) => {
  console.log(`${index}: ${project.name}`);
});

// Map
const names = projects.map(p => p.name);

// Filter
const activeProjects = projects.filter(p => !p.is_invalidated);

// Find
const testProject = projects.find(p => p.name.includes('test'));

Managing Collection Data

Add Models

// Add a single model
projects.add(newProject);

// Add multiple models
projects.add([project1, project2, project3]);

// Adding updates the index automatically
const added = projects.get(newProject.id); // Fast lookup

Remove Models

// Remove by ID
projects.remove(42);

// Model is removed from both array and index
console.log(projects.has(42)); // false

Reset Collection

// Replace all models with new ones
projects.reset([project1, project2]);

// Clear the collection
projects.clear();
console.log(projects.length); // 0

Pagination Metadata

Access pagination information:

await projects.fetch({ limit: 20 });

// Check if there are more pages
console.log('Has more:', projects.hasMore);

// Access full metadata
console.log('Meta:', projects.meta);
// {
//   has_more: true,
//   next: "/api/v2/...",
//   next_offset: 20
// }

Complete Example

import { initializeSGERP } from 'sgerp-frontend-lib';

const api = initializeSGERP({
  activeConnection: 'admin@staging',
  connections: {
    'admin@staging': {
      id: 'admin@staging',
      environment: 'staging',
      user: 'admin',
      password: 'your-password',
      baseUrl: 'https://sgerp-stage.d.gcdev.swatrider.com',
    },
  },
});

// Access collection
const projects = api.collections.project;

// Fetch first page
await projects.fetch({ limit: 50 });
console.log(`Loaded ${projects.length} projects`);

// Quick lookup by ID
const project = projects.get(1);
if (project) {
  console.log('Project 1:', project.name);
}

// Iterate through all
projects.forEach((project, index) => {
  console.log(`${index + 1}. ${project.name} (${project.timezone})`);
});

// Load more pages manually
while (projects.hasMore) {
  console.log('Loading next page...');
  await projects.fetchNext();
}

console.log(`Total projects loaded: ${projects.length}`);

// Or load all at once
const allProjects = api.collections.project;
await allProjects.fetchAll();
console.log(`All projects: ${allProjects.length}`);

// Filter and map
const testProjects = allProjects
  .filter(p => p.name.toLowerCase().includes('test'))
  .map(p => ({
    id: p.id,
    name: p.name,
  }));

console.log('Test projects:', testProjects);

API Filtering vs Client Filtering

When searching for specific records, use API filters to reduce data transfer:

const users = api.collections.user;

// ✅ Good - Fetch only matching records from server
await users.fetch({ username: 'admin' } as any);
const user = users.first();

Use Client Filters

When working with already-loaded data or complex conditions:

const users = api.collections.user;
await users.fetch({ limit: 100 });

// ✅ Good - Data already loaded, filter in memory
const activeStaff = users.filter(u => u.is_active && u.is_staff);

Underscore.js-Style Methods (Client-Side)

Collections include powerful underscore.js-style methods for data manipulation:

Property Extraction

const projects = api.collections.project;
await projects.fetch({ limit: 100 });

// Extract all names
const names = projects.pluck('name');
// ['Project A', 'Project B', 'Project C']

// Extract all timezones
const timezones = projects.pluck('timezone');

Filtering & Finding (Client-Side)

// Find all projects with a specific timezone (client-side filter)
const utcProjects = projects.where({ timezone: 'UTC' });

// Find the first project with a specific timezone (client-side)
const firstUtc = projects.findWhere({ timezone: 'UTC' });

// Reject invalidated projects (client-side)
const active = projects.reject(p => p.is_invalidated);

Note: These filter already-loaded data. For server-side filtering, use API filters in fetch().

Sorting & Grouping

// Sort by name
const sorted = projects.sortBy('name');

// Sort by custom function
const byLength = projects.sortBy(p => p.name.length);

// Group by timezone
const byTimezone = projects.groupBy('timezone');
// { 'UTC': [...], 'America/New_York': [...] }

// Count by timezone
const counts = projects.countBy('timezone');
// { 'UTC': 10, 'America/New_York': 5 }

// Index by name for fast lookup
const byName = projects.indexBy('name');
// { 'Project A': {...}, 'Project B': {...} }

Collection Utilities

// Split into active and inactive
const [active, inactive] = projects.partition(p => !p.is_invalidated);

// Get random project(s)
const random = projects.sample(); // One random
const randomThree = projects.sample(3); // Three random

// Shuffle all projects
const shuffled = projects.shuffle();

// Get first/last
const first = projects.first();
const firstFive = projects.first(5);
const last = projects.last();

// Exclude specific IDs
const filtered = projects.without(1, 2, 3);

// Get min/max
const oldest = projects.min('created_at');
const newest = projects.max('created_at');

Testing & Checking

// Check if empty
if (projects.isEmpty()) {
  console.log('No projects');
}

// Test all
const allActive = projects.every(p => !p.is_invalidated);

// Test any
const hasUtc = projects.some(p => p.timezone === 'UTC');

// Contains
const hasTest = projects.contains(p => p.name.includes('test'));

Advanced Operations

// Reduce to a value
const totalProjects = projects.reduce((sum, p) => sum + 1, 0);

// Get unique (remove duplicates)
const unique = projects.uniq();

// Initial/Rest (slice operations)
const allButLast = projects.initial(1);
const allButFirst = projects.rest(1);

Collection Methods Reference

Data Access

  • models - Get all models array
  • length - Number of models in collection
  • get(id) - Get model by ID (O(1) lookup)
  • at(index) - Get model at array index
  • has(id) - Check if model exists by ID
  • toArray() - Convert to plain array
  • toJSON() - Convert to JSON

Basic Iteration

  • forEach(callback) - Iterate over models
  • map(callback) - Map over models
  • filter(callback) - Filter models
  • find(callback) - Find first matching model

Underscore.js-Style Methods

  • pluck(key) - Extract property from all models
  • where(properties) - Filter by matching properties
  • findWhere(properties) - Find first by matching properties
  • sortBy(iteratee) - Sort by property or function
  • groupBy(iteratee) - Group by property or function
  • countBy(iteratee) - Count by property or function
  • indexBy(iteratee) - Index by property or function
  • partition(predicate) - Split into [matches, non-matches]
  • sample(n?) - Get random model(s)
  • shuffle() - Shuffle models
  • first(n?) - Get first n models
  • last(n?) - Get last n models
  • initial(n) - All except last n
  • rest(n) - All except first n
  • without(...ids) - Exclude models by ID
  • reject(predicate) - Opposite of filter
  • every(predicate) - Test if all pass
  • some(predicate) - Test if any pass
  • contains(predicate) - Check if any match
  • max(iteratee) - Find max by property/function
  • min(iteratee) - Find min by property/function
  • reduce(callback, initial) - Reduce to value
  • uniq() - Remove duplicates
  • isEmpty() - Check if empty
  • size() - Get collection size

Data Management

  • add(model | models) - Add model(s) to collection
  • remove(id) - Remove model by ID
  • reset(models) - Replace all models
  • clear() - Remove all models

Fetching

  • fetch(params, config) - Fetch first page
  • fetchNext(config) - Fetch and append next page
  • fetchAll(config) - Fetch all pages

Pagination

  • hasMore - Boolean indicating if more pages exist
  • meta - Full pagination metadata