Quick Start

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Flash Persistence - Quick Start

Get started with auto-persistent signals in less than 5 minutes.


Installation

Flash Persistence is part of the @storage-system library, which is already installed in the monorepo.

# Already available in your NX workspace
import { autoFlashState } from '@storage-system';

Step 1: App Configuration

Add Flash Persistence to your app configuration:

// apps/your-app/src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideFlashPersistence } from '@storage-system';

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    
    provideFlashPersistence({
      // Optional: enable MongoDB sync
      enableMongoDBSync: false, // Set to true if you want cloud sync
      
      // Optional: customize debounce
      debounceMs: 300, // Wait 300ms before persisting changes
      
      // Optional: pre-load specific keys
      preloadKeys: ['user-preferences', 'app-state']
    })
  ]
};

That's it for setup! Flash Persistence is now ready to use.


Step 2: Create Your First Persistent Signal

Basic Example

// apps/your-app/src/app/components/settings.component.ts
import { Component } from '@angular/core';
import { autoFlashState } from '@storage-system';

@Component({
  selector: 'app-settings',
  standalone: true,
  template: `
    <div class="settings">
      <h2>Settings</h2>
      
      <label>
        Theme:
        <select [value]="userPrefs().theme" 
                (change)="updateTheme($event)">
          <option value="light">Light</option>
          <option value="dark">Dark</option>
        </select>
      </label>
      
      <label>
        Language:
        <select [value]="userPrefs().language"
                (change)="updateLanguage($event)">
          <option value="en">English</option>
          <option value="es">Español</option>
        </select>
      </label>
      
      <p>Current theme: {{ userPrefs().theme }}</p>
      <p>Current language: {{ userPrefs().language }}</p>
    </div>
  `
})
export class SettingsComponent {
  // ✅ Create persistent signal
  userPrefs = autoFlashState({
    key: 'user-preferences',
    initialValue: {
      theme: 'light',
      language: 'en'
    }
  });
  
  updateTheme(event: Event) {
    const theme = (event.target as HTMLSelectElement).value;
    this.userPrefs.update(prefs => ({ ...prefs, theme }));
    // ✅ Automatically persisted!
  }
  
  updateLanguage(event: Event) {
    const language = (event.target as HTMLSelectElement).value;
    this.userPrefs.update(prefs => ({ ...prefs, language }));
    // ✅ Automatically persisted!
  }
}

Try it:

  1. Change the theme
  2. Refresh the page
  3. 🎉 Your selection is preserved!

Step 3: Use in Multiple Components

Shared State Pattern

// apps/your-app/src/app/services/app-state.service.ts
import { Injectable, signal } from '@angular/core';
import { autoFlashState } from '@storage-system';

export interface AppState {
  sidebarOpen: boolean;
  currentView: 'list' | 'grid';
  filters: string[];
}

@Injectable({ providedIn: 'root' })
export class AppStateService {
  // Persistent app state
  state = autoFlashState<AppState>({
    key: 'app-state',
    initialValue: {
      sidebarOpen: true,
      currentView: 'list',
      filters: []
    }
  });
  
  // Computed signals
  sidebarOpen = () => this.state().sidebarOpen;
  currentView = () => this.state().currentView;
  filters = () => this.state().filters;
  
  // Actions
  toggleSidebar() {
    this.state.update(s => ({ ...s, sidebarOpen: !s.sidebarOpen }));
  }
  
  setView(view: 'list' | 'grid') {
    this.state.update(s => ({ ...s, currentView: view }));
  }
  
  addFilter(filter: string) {
    this.state.update(s => ({
      ...s,
      filters: [...s.filters, filter]
    }));
  }
  
  removeFilter(filter: string) {
    this.state.update(s => ({
      ...s,
      filters: s.filters.filter(f => f !== filter)
    }));
  }
}

Use in Component

// apps/your-app/src/app/components/sidebar.component.ts
import { Component, inject } from '@angular/core';
import { AppStateService } from '../services/app-state.service';

@Component({
  selector: 'app-sidebar',
  standalone: true,
  template: `
    <aside [class.open]="appState.sidebarOpen()">
      <button (click)="appState.toggleSidebar()">
        Toggle Sidebar
      </button>
      
      <!-- Sidebar content -->
    </aside>
  `
})
export class SidebarComponent {
  appState = inject(AppStateService);
}

Common Patterns

1. Form Auto-Save

import { Component } from '@angular/core';
import { autoFlashState } from '@storage-system';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-draft-editor',
  standalone: true,
  imports: [FormsModule],
  template: `
    <div class="editor">
      <h2>Draft Editor</h2>
      <p class="autosave-indicator">
        {{ draft() ? 'Draft saved ✓' : 'No draft' }}
      </p>
      
      <textarea 
        [(ngModel)]="draftContent"
        (ngModelChange)="updateDraft($event)"
        placeholder="Start typing...">
      </textarea>
      
      <button (click)="clearDraft()">Clear Draft</button>
    </div>
  `
})
export class DraftEditorComponent {
  draft = autoFlashState({
    key: 'draft',
    initialValue: ''
  });
  
  draftContent = this.draft();
  
  updateDraft(content: string) {
    this.draft.set(content);
    // ✅ Auto-saved with 300ms debounce
  }
  
  clearDraft() {
    this.draft.set('');
    this.draftContent = '';
  }
}

2. Shopping Cart

export interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

@Injectable({ providedIn: 'root' })
export class CartService {
  cart = autoFlashState<CartItem[]>({
    key: 'shopping-cart',
    database: 'ecommerce',
    collection: 'cart',
    initialValue: []
  });
  
  // Computed
  itemCount = computed(() => 
    this.cart().reduce((sum, item) => sum + item.quantity, 0)
  );
  
  total = computed(() =>
    this.cart().reduce((sum, item) => sum + (item.price * item.quantity), 0)
  );
  
  // Actions
  addItem(item: CartItem) {
    const existing = this.cart().find(i => i.id === item.id);
    
    if (existing) {
      this.updateQuantity(item.id, existing.quantity + 1);
    } else {
      this.cart.update(items => [...items, item]);
    }
  }
  
  removeItem(id: string) {
    this.cart.update(items => items.filter(i => i.id !== id));
  }
  
  updateQuantity(id: string, quantity: number) {
    this.cart.update(items =>
      items.map(i => i.id === id ? { ...i, quantity } : i)
    );
  }
  
  clear() {
    this.cart.set([]);
  }
}

3. Recent Items

@Injectable({ providedIn: 'root' })
export class RecentItemsService {
  recentItems = autoFlashState<string[]>({
    key: 'recent-items',
    initialValue: [],
    ttl: 7 * 24 * 60 * 60 * 1000 // 7 days
  });
  
  addRecentItem(itemId: string) {
    this.recentItems.update(items => {
      // Remove if exists, add to front
      const filtered = items.filter(id => id !== itemId);
      const updated = [itemId, ...filtered];
      
      // Keep only last 10
      return updated.slice(0, 10);
    });
  }
  
  clearRecent() {
    this.recentItems.set([]);
  }
}

MongoDB Sync (Optional)

Enable Cloud Sync

// app.config.ts
import { provideFlashPersistence, provideMongoDBSync } from '@storage-system';

export const appConfig: ApplicationConfig = {
  providers: [
    provideFlashPersistence(),
    
    // Add MongoDB sync
    provideMongoDBSync({
      connectionString: environment.mongoDbConnectionString,
      database: 'my-app',
      defaultStrategy: {
        mode: 'offline-first',
        priority: 'normal',
        conflictResolution: 'last-write-wins'
      }
    })
  ]
};

Use with Sync

// Now your signals sync to MongoDB!
const syncedPrefs = autoFlashState({
  key: 'preferences',
  database: 'my-app',
  collection: 'user-settings',
  initialValue: { theme: 'dark' },
  priority: 'high' // Sync every 5 minutes
});

// Works offline - syncs when back online
syncedPrefs.set({ theme: 'light' });
// ✅ Local: Instant (0.3ms)
// ✅ MongoDB: Background sync (non-blocking)

Next Steps

Learn More

Examples


Troubleshooting

Q: My signal isn't persisting

A: Check that you've called provideFlashPersistence() in your app.config.ts.

Q: Values aren't loading on page refresh

A: Flash Persistence pre-loads during app initialization. Make sure:

  1. provideFlashPersistence() is in your config
  2. You're using autoFlashState (not regular signal)
  3. The key is unique and consistent
  4. The hydrator service is being initialized properly

Q: Performance seems slow

A: Flash Persistence is designed for 0.3ms reads. If you're seeing slower performance:

  1. Check if you're bypassing the cache
  2. Verify the cache budget isn't exhausted
  3. Use computed signals for derived values

Q: How do I clear all persisted data?

A:

import { inject } from '@angular/core';
import { GgStorage } from '@storage-system';

const storage = inject(GgStorage);
await storage.clear(); // Clears all storage

Questions? Check the API Reference or GitHub Discussions.