A JavaScript framework for creating ambitious web applications
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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;
}Install with Tessl CLI
npx tessl i tessl/npm-ember-source