Controller layer for handling user interactions and managing component state in classic Ember applications.
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);
}
}
});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');
}
}
});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 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;
}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>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;
}