or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

application-engine.mdcomponents.mdcontrollers.mddata-structures.mddebugging-development.mddestroyables-cleanup.mdindex.mdmodifiers.mdobject-model.mdreactivity-tracking.mdrouting.mdservices.mdtemplates-rendering.mdtesting.mdutilities.md
tile.json

controllers.mddocs/

Controllers

Controller layer for handling user interactions and managing component state in classic Ember applications.

Capabilities

Controller Class

Base controller class for managing route-specific state and handling user actions.

/**
 * Base controller class for managing route-specific state and handling user actions
 */
class Controller {
  /**
   * Create new controller class with additional properties
   * @param properties - Controller properties and methods
   * @returns New Controller subclass
   */
  static extend(properties?: object): typeof Controller;
  
  /**
   * Create controller instance
   * @param properties - Initial properties
   * @returns Controller instance
   */
  static create(properties?: object): Controller;
  
  /** Model data provided by the route */
  model: any;
  
  /** Target for action bubbling (usually the route) */
  target: any;
  
  /** Query parameters for this controller */
  queryParams: string[] | QueryParamConfig[];
  
  /**
   * Send action to target or handle locally
   * @param actionName - Name of action to send
   * @param context - Context data to pass with action
   */
  send(actionName: string, ...context: any[]): void;
}

Usage Examples:

import Controller from "@ember/controller";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";

export default class UsersController extends Controller {
  @tracked searchTerm = '';
  @tracked selectedUser = null;
  
  // Query parameters
  queryParams = ['searchTerm', 'page'];
  @tracked page = 1;
  
  get filteredUsers() {
    if (!this.searchTerm) return this.model;
    
    return this.model.filter(user => 
      user.name.toLowerCase().includes(this.searchTerm.toLowerCase())
    );
  }
  
  @action
  selectUser(user) {
    this.selectedUser = user;
  }
  
  @action
  searchUsers(term) {
    this.searchTerm = term;
    this.page = 1; // Reset to first page
  }
  
  @action
  deleteUser(user) {
    // Action bubbles to route if not handled here
    this.send('deleteUser', user);
  }
}

// Classic syntax
export default Controller.extend({
  searchTerm: '',
  selectedUser: null,
  
  queryParams: ['searchTerm'],
  
  filteredUsers: computed('model.[]', 'searchTerm', function() {
    const term = this.searchTerm;
    if (!term) return this.model;
    
    return this.model.filter(user => 
      user.name.toLowerCase().includes(term.toLowerCase())
    );
  }),
  
  actions: {
    selectUser(user) {
      this.set('selectedUser', user);
    },
    
    searchUsers(term) {
      this.set('searchTerm', term);
    }
  }
});

Controller Injection

System for injecting controllers into other objects.

/**
 * Controller injection service
 */
interface ControllerInjection {
  /**
   * Inject controller by name
   * @param controllerName - Name of controller to inject
   * @returns Injected controller property
   */
  (controllerName?: string): any;
}

/** Controller injection helper */
declare const inject: {
  controller: ControllerInjection;
};

Usage Examples:

import Controller, { inject as controller } from "@ember/controller";
import { action } from "@ember/object";

export default class ApplicationController extends Controller {
  // Inject other controllers
  @controller('session') sessionController;
  @controller('notifications') notificationsController;
  
  @action
  logout() {
    this.sessionController.logout();
    this.notificationsController.success('Logged out successfully');
  }
}

// Legacy injection syntax
export default Controller.extend({
  sessionController: controller('session'),
  notificationsController: controller('notifications'),
  
  actions: {
    logout() {
      this.sessionController.logout();
      this.notificationsController.success('Logged out successfully');
    }
  }
});

Query Parameters

System for managing URL query parameters through controllers.

/**
 * Query parameter configuration object
 */
interface QueryParamConfig {
  /** Controller property name */
  as?: string;
  
  /** URL query parameter name */
  scope?: 'controller' | 'model';
  
  /** Whether to replace or push URL history */
  replace?: boolean;
  
  /** Whether to refresh model when parameter changes */
  refreshModel?: boolean;
}

/**
 * Query parameter descriptor for detailed configuration
 */
interface QueryParamDescriptor {
  /** Query parameter name */
  [key: string]: QueryParamConfig;
}

Usage Examples:

import Controller from "@ember/controller";
import { tracked } from "@glimmer/tracking";

export default class ProductsController extends Controller {
  // Simple query params
  queryParams = ['category', 'sortBy', 'page'];
  
  @tracked category = 'all';
  @tracked sortBy = 'name';
  @tracked page = 1;
  
  // Advanced query param configuration
  queryParams = [
    'category',
    { sortBy: { replace: true } }, // Replace instead of push
    { page: { replace: true } },
    { search: { refreshModel: true } } // Refresh route model when changed
  ];
  
  @tracked search = '';
  
  get filteredProducts() {
    let products = this.model;
    
    if (this.category !== 'all') {
      products = products.filter(p => p.category === this.category);
    }
    
    return products.sort((a, b) => {
      const field = this.sortBy;
      return a[field] > b[field] ? 1 : -1;
    });
  }
}

Controller Lifecycle

Controller lifecycle hooks and management.

/**
 * Controller lifecycle hooks
 */
interface ControllerLifecycle {
  /**
   * Called when controller is created
   */
  init(): void;
  
  /**
   * Called when controller is being destroyed
   */
  willDestroy(): void;
  
  /**
   * Called after controller is destroyed
   */
  destroy(): void;
}

Action Handling

System for handling user actions in controllers.

/**
 * Actions hash for classic controller action handling
 */
interface ActionsHash {
  /** Action methods mapped by name */
  [actionName: string]: (...args: any[]) => any;
}

Usage Examples:

import Controller from "@ember/controller";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";

export default class PostController extends Controller {
  @tracked isEditing = false;
  
  @action
  editPost() {
    this.isEditing = true;
  }
  
  @action
  savePost(post) {
    return post.save().then(() => {
      this.isEditing = false;
      this.notifications.success('Post saved!');
    }).catch((error) => {
      this.notifications.error('Failed to save post');
    });
  }
  
  @action
  cancelEdit() {
    this.model.rollbackAttributes();
    this.isEditing = false;
  }
  
  @action
  deletePost() {
    if (confirm('Are you sure?')) {
      this.model.destroyRecord().then(() => {
        this.router.transitionTo('posts');
      });
    }
  }
}

// Template usage:
// <button {{on "click" this.editPost}}>Edit</button>
// <button {{on "click" (fn this.savePost this.model)}}>Save</button>

Types

interface ControllerDefinition {
  /** Controller class constructor */
  new (): Controller;
}

interface QueryParamOptions {
  /** URL parameter name override */
  as?: string;
  
  /** Scope for parameter */
  scope?: 'controller' | 'model';
  
  /** URL update strategy */
  replace?: boolean;
  
  /** Whether to refresh route model */
  refreshModel?: boolean;
}

interface ActionContext {
  /** Original event that triggered action */
  originalEvent?: Event;
  
  /** Additional context data */
  [key: string]: any;
}