0
# Component Management
1
2
Core system for managing Material Design Lite component lifecycle, registration, and upgrades. The component handler provides centralized control over when and how MDL components are initialized and managed.
3
4
## Capabilities
5
6
### Component Handler
7
8
The global `componentHandler` object provides the main API for component lifecycle management.
9
10
```javascript { .api }
11
/**
12
* Global component handler for managing MDL components
13
* Available as window.componentHandler
14
*/
15
interface ComponentHandler {
16
/**
17
* Searches existing DOM for elements of specified component type and upgrades them
18
* @param optJsClass - Optional programmatic name of the element class to upgrade
19
* @param optCssClass - Optional CSS class name of elements to upgrade
20
*/
21
upgradeDom(optJsClass?: string, optCssClass?: string): void;
22
23
/**
24
* Upgrades a specific element rather than all in the DOM
25
* @param element - The element to upgrade
26
* @param optJsClass - Optional name of the class to upgrade the element to
27
*/
28
upgradeElement(element: Element, optJsClass?: string): void;
29
30
/**
31
* Upgrades a specific list of elements rather than all in the DOM
32
* @param elements - The elements to upgrade
33
*/
34
upgradeElements(elements: Element[] | NodeList | HTMLCollection): void;
35
36
/**
37
* Upgrades all registered components found in the current DOM
38
* Automatically called on window load
39
*/
40
upgradeAllRegistered(): void;
41
42
/**
43
* Allows user to be alerted to any upgrades performed for a given component type
44
* @param jsClass - The class name of the MDL component to hook into
45
* @param callback - Function to call upon upgrade, receives HTMLElement parameter
46
*/
47
registerUpgradedCallback(jsClass: string, callback: (element: HTMLElement) => void): void;
48
49
/**
50
* Registers a class for future use and attempts to upgrade existing DOM
51
* @param config - The registration configuration
52
*/
53
register(config: ComponentConfigPublic): void;
54
55
/**
56
* Downgrade either a given node, an array of nodes, or a NodeList
57
* @param nodes - The nodes to downgrade
58
*/
59
downgradeElements(nodes: Node[] | NodeList): void;
60
}
61
```
62
63
**Usage Examples:**
64
65
```javascript
66
// Upgrade all MDL components in the DOM
67
componentHandler.upgradeAllRegistered();
68
69
// Upgrade specific component type
70
componentHandler.upgradeDom('MaterialButton', 'mdl-js-button');
71
72
// Upgrade a single element
73
const newButton = document.querySelector('.mdl-js-button');
74
componentHandler.upgradeElement(newButton);
75
76
// Upgrade multiple elements
77
const buttons = document.querySelectorAll('.mdl-js-button');
78
componentHandler.upgradeElements(buttons);
79
80
// Register callback for component upgrades
81
componentHandler.registerUpgradedCallback('MaterialButton', (element) => {
82
console.log('Button upgraded:', element);
83
});
84
85
// Clean up components when removing elements
86
const elementToRemove = document.querySelector('.mdl-js-button');
87
componentHandler.downgradeElements([elementToRemove]);
88
elementToRemove.parentNode.removeChild(elementToRemove);
89
```
90
91
### Component Registration
92
93
Register new component types with the component handler.
94
95
```javascript { .api }
96
/**
97
* Configuration for registering a new component type
98
*/
99
interface ComponentConfigPublic {
100
/** Constructor function for the component */
101
constructor: Function;
102
/** String representation of the class name */
103
classAsString: string;
104
/** CSS class that identifies elements of this component type */
105
cssClass: string;
106
/** Whether the component should be accessible as a widget (default: true) */
107
widget?: boolean;
108
}
109
110
/**
111
* Internal configuration structure (for reference)
112
*/
113
interface ComponentConfig {
114
classConstructor: Function;
115
className: string;
116
cssClass: string;
117
widget: boolean;
118
callbacks: Array<(element: HTMLElement) => void>;
119
}
120
```
121
122
**Usage Examples:**
123
124
```javascript
125
// Register a custom component
126
function MyCustomComponent(element) {
127
this.element_ = element;
128
this.init();
129
}
130
131
MyCustomComponent.prototype.init = function() {
132
// Component initialization logic
133
};
134
135
MyCustomComponent.prototype.myMethod = function() {
136
// Custom component method
137
};
138
139
// Register with component handler
140
componentHandler.register({
141
constructor: MyCustomComponent,
142
classAsString: 'MyCustomComponent',
143
cssClass: 'my-js-component',
144
widget: true
145
});
146
147
// Now elements with 'my-js-component' class will be automatically upgraded
148
```
149
150
### Component Events
151
152
Components emit events during their lifecycle that can be listened to for custom behavior.
153
154
```javascript { .api }
155
/**
156
* Event fired before a component is upgraded
157
* Can be cancelled by calling preventDefault()
158
*/
159
interface MDLComponentUpgradingEvent extends CustomEvent {
160
type: 'mdl-componentupgrading';
161
bubbles: true;
162
cancelable: true;
163
}
164
165
/**
166
* Event fired after a component has been upgraded
167
*/
168
interface MDLComponentUpgradedEvent extends CustomEvent {
169
type: 'mdl-componentupgraded';
170
bubbles: true;
171
cancelable: false;
172
}
173
174
/**
175
* Event fired after a component has been downgraded
176
*/
177
interface MDLComponentDowngradedEvent extends CustomEvent {
178
type: 'mdl-componentdowngraded';
179
bubbles: true;
180
cancelable: false;
181
}
182
```
183
184
**Usage Examples:**
185
186
```javascript
187
// Listen for component upgrade events
188
document.addEventListener('mdl-componentupgrading', (event) => {
189
console.log('Component about to upgrade:', event.target);
190
191
// Cancel upgrade if needed
192
if (shouldCancelUpgrade(event.target)) {
193
event.preventDefault();
194
}
195
});
196
197
document.addEventListener('mdl-componentupgraded', (event) => {
198
console.log('Component upgraded:', event.target);
199
200
// Perform post-upgrade actions
201
setupCustomBehavior(event.target);
202
});
203
204
document.addEventListener('mdl-componentdowngraded', (event) => {
205
console.log('Component downgraded:', event.target);
206
207
// Clean up any custom behavior
208
cleanupCustomBehavior(event.target);
209
});
210
```
211
212
### Component Instance Management
213
214
Access and manage component instances on upgraded elements.
215
216
```javascript { .api }
217
/**
218
* Component instance structure (for widget components)
219
*/
220
interface Component {
221
/** The DOM element this component is attached to */
222
element_: HTMLElement;
223
/** The component class name */
224
className: string;
225
/** String representation of the class */
226
classAsString: string;
227
/** CSS class that identifies this component type */
228
cssClass: string;
229
/** Widget identifier */
230
widget: string;
231
}
232
```
233
234
**Usage Examples:**
235
236
```javascript
237
// Access widget component instances
238
const buttonElement = document.querySelector('.mdl-js-button');
239
const buttonInstance = buttonElement.MaterialButton;
240
241
if (buttonInstance) {
242
// Use component methods
243
buttonInstance.disable();
244
}
245
246
// Check if element has been upgraded
247
const textfieldElement = document.querySelector('.mdl-js-textfield');
248
if (textfieldElement.MaterialTextfield) {
249
console.log('Textfield is already upgraded');
250
textfieldElement.MaterialTextfield.checkValidity();
251
} else {
252
console.log('Textfield needs to be upgraded');
253
componentHandler.upgradeElement(textfieldElement);
254
}
255
256
// List all upgraded components on an element
257
const upgradedList = element.getAttribute('data-upgraded');
258
console.log('Upgraded components:', upgradedList ? upgradedList.split(',') : []);
259
```
260
261
### Error Handling
262
263
Handle errors that may occur during component management operations.
264
265
```javascript
266
// Component upgrade errors
267
try {
268
componentHandler.upgradeElement(invalidElement);
269
} catch (error) {
270
if (error.message.includes('Invalid argument provided')) {
271
console.error('Element is not a valid DOM element');
272
} else if (error.message.includes('Unable to find a registered component')) {
273
console.error('Component class not registered');
274
} else {
275
console.error('Unknown upgrade error:', error);
276
}
277
}
278
279
// Registration errors
280
try {
281
componentHandler.register({
282
constructor: MyComponent,
283
classAsString: 'ExistingComponent', // This will throw if already registered
284
cssClass: 'my-component'
285
});
286
} catch (error) {
287
if (error.message.includes('already been registered')) {
288
console.error('Component already registered');
289
}
290
}
291
```
292
293
## Best Practices
294
295
### Dynamic Content
296
297
When adding MDL components dynamically:
298
299
```javascript
300
// Create element with MDL classes
301
const dynamicElement = document.createElement('button');
302
dynamicElement.className = 'mdl-button mdl-js-button mdl-button--raised';
303
dynamicElement.textContent = 'Dynamic Button';
304
305
// Add to DOM first
306
document.body.appendChild(dynamicElement);
307
308
// Then upgrade
309
componentHandler.upgradeElement(dynamicElement);
310
311
// For multiple elements
312
const elements = [element1, element2, element3];
313
elements.forEach(el => document.body.appendChild(el));
314
componentHandler.upgradeElements(elements);
315
```
316
317
### Memory Management
318
319
Clean up components when removing elements:
320
321
```javascript
322
// Before removing elements from DOM
323
const elementToRemove = document.querySelector('.mdl-js-button');
324
componentHandler.downgradeElements([elementToRemove]);
325
elementToRemove.parentNode.removeChild(elementToRemove);
326
327
// For single page applications
328
function cleanupPage() {
329
const allMDLElements = document.querySelectorAll('[data-upgraded]');
330
componentHandler.downgradeElements(allMDLElements);
331
}
332
```
333
334
### Performance
335
336
Optimize component upgrades:
337
338
```javascript
339
// Batch upgrades when possible
340
const newElements = [];
341
for (let i = 0; i < 10; i++) {
342
const el = createMDLElement();
343
container.appendChild(el);
344
newElements.push(el);
345
}
346
componentHandler.upgradeElements(newElements);
347
348
// Use specific component types when known
349
componentHandler.upgradeDom('MaterialButton', 'mdl-js-button');
350
```