Files
marco/.agents/skills/ember-best-practices/rules/service-shared-state.md

2.6 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use Services for Shared State MEDIUM-HIGH Better state management and reusability services, state-management, dependency-injection

Use Services for Shared State

Use services to manage shared state across components and routes instead of passing data through multiple layers or duplicating state.

Incorrect (prop drilling):

// app/routes/dashboard.gjs
export default class DashboardRoute extends Route {
  model() {
    return { currentTheme: 'dark' };
  }

  <template>
    <Header @theme={{@model.currentTheme}} />
    <Sidebar @theme={{@model.currentTheme}} />
    <MainContent @theme={{@model.currentTheme}} />
  </template>
}

Correct (using service):

// app/services/theme.js
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class ThemeService extends Service {
  @tracked currentTheme = 'dark';

  @action
  setTheme(theme) {
    this.currentTheme = theme;
    localStorage.setItem('theme', theme);
  }

  @action
  loadTheme() {
    this.currentTheme = localStorage.getItem('theme') || 'dark';
  }
}
// app/components/header.js
import Component from '@glimmer/component';
import { service } from '@ember/service';

class Header extends Component {
  @service theme;

  // Access theme.currentTheme directly
}
// app/components/sidebar.js
import Component from '@glimmer/component';
import { service } from '@ember/service';

class Sidebar extends Component {
  @service theme;

  // Access theme.currentTheme directly
}

Services provide centralized state management with automatic reactivity through tracked properties.

For complex state, consider using Ember Data or ember-orbit:

// app/services/cart.js
import Service from '@ember/service';
import { service } from '@ember/service';
import { TrackedArray } from 'tracked-built-ins';
import { cached } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class CartService extends Service {
  @service store;

  items = new TrackedArray([]);

  @cached
  get total() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }

  @cached
  get itemCount() {
    return this.items.length;
  }

  @action
  addItem(item) {
    this.items.push(item);
  }

  @action
  removeItem(item) {
    const index = this.items.indexOf(item);
    if (index > -1) {
      this.items.splice(index, 1);
    }
  }
}

Reference: Ember Services