Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit 218d2e5

Browse files
author
Matt Goo
authored
feat(auto-init): initialize components once with multiple mdc.autoInit() calls (#4691)
1 parent 63ef3e2 commit 218d2e5

File tree

4 files changed

+64
-19
lines changed

4 files changed

+64
-19
lines changed

packages/mdc-auto-init/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ property on that element.
6666
document.querySelector('.mdc-text-field').MDCTextField.disabled = true;
6767
```
6868

69+
#### Calling subsequent `mdc.autoInit()`
70+
71+
If you decide to add new components into the DOM after the initial `mdc.autoInit()`, you can make subsequent calls to `mdc.autoInit()`. This will not reinitialize existing components. This works since mdc-auto-init will add the `data-mdc-auto-init-state="initialized"` attribute, which tracks if the component has already been initialized. After calling `mdc.autoInit()` your component will then look like:
72+
73+
```html
74+
<div class="mdc-text-field" data-mdc-auto-init="MDCTextField" data-mdc-auto-init-state="initialized">
75+
...
76+
</div>
77+
```
78+
6979
### Using as a standalone module
7080

7181
#### Registering Components

packages/mdc-auto-init/constants.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
export const strings = {
25+
AUTO_INIT_ATTR: 'data-mdc-auto-init',
26+
AUTO_INIT_STATE_ATTR: 'data-mdc-auto-init-state',
27+
INITIALIZED_STATE: 'initialized',
28+
};

packages/mdc-auto-init/index.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
import {MDCComponent} from '@material/base/component';
2727
import {MDCFoundation} from '@material/base/foundation';
2828

29+
import {strings} from './constants';
30+
31+
const {AUTO_INIT_ATTR, AUTO_INIT_STATE_ATTR, INITIALIZED_STATE} = strings;
32+
2933
export interface MDCAttachable {
3034
new<F extends MDCFoundation>(root: Element, foundation?: F, ...args: Array<unknown>): MDCComponent<F>;
3135

@@ -60,12 +64,13 @@ function emit<T extends object>(evtType: string, evtData: T, shouldBubble = fals
6064
/**
6165
* Auto-initializes all MDC components on a page.
6266
*/
63-
export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
67+
export function mdcAutoInit(root = document) {
6468
const components = [];
65-
const nodes: Element[] = [].slice.call(root.querySelectorAll('[data-mdc-auto-init]'));
69+
let nodes: Element[] = [].slice.call(root.querySelectorAll(`[${AUTO_INIT_ATTR}]`));
70+
nodes = nodes.filter((node) => node.getAttribute(AUTO_INIT_STATE_ATTR) !== INITIALIZED_STATE);
6671

6772
for (const node of nodes) {
68-
const ctorName = node.getAttribute('data-mdc-auto-init');
73+
const ctorName = node.getAttribute(AUTO_INIT_ATTR);
6974
if (!ctorName) {
7075
throw new Error('(mdc-auto-init) Constructor name must be given.');
7176
}
@@ -76,11 +81,6 @@ export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
7681
`(mdc-auto-init) Could not find constructor in registry for ${ctorName}`);
7782
}
7883

79-
if (Object.getOwnPropertyDescriptor(node, ctorName)) {
80-
warn(`(mdc-auto-init) Component already initialized for ${node}. Skipping...`);
81-
continue;
82-
}
83-
8484
// TODO: Should we make an eslint rule for an attachTo() static method?
8585
// See https://github.com/Microsoft/TypeScript/issues/14600 for discussion of static interface support in TS
8686
const component = Constructor.attachTo(node);
@@ -91,6 +91,7 @@ export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
9191
writable: false,
9292
});
9393
components.push(component);
94+
node.setAttribute(AUTO_INIT_STATE_ATTR, INITIALIZED_STATE);
9495
}
9596

9697
emit('MDCAutoInit:End', {});

test/unit/mdc-auto-init/mdc-auto-init.test.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,6 @@ test('throws when constructor is not registered', () => {
9696
assert.throws(() => mdcAutoInit(root));
9797
});
9898

99-
test('warns when autoInit called multiple times on a node', () => {
100-
const root = setupTest();
101-
const warn = td.func('warn');
102-
const {contains} = td.matchers;
103-
104-
mdcAutoInit(root, warn);
105-
mdcAutoInit(root, warn);
106-
107-
td.verify(warn(contains('(mdc-auto-init) Component already initialized')));
108-
});
109-
11099
test('#register throws when Ctor is not a function', () => {
111100
assert.throws(() => mdcAutoInit.register('not-a-function', 'Not a function'));
112101
});
@@ -170,3 +159,20 @@ test('#returns the initialized components', () => {
170159
assert.equal(components.length, 1);
171160
assert.isOk(components[0] instanceof FakeComponent);
172161
});
162+
163+
test('does not register any components if element has data-mdc-auto-init-state="initialized"', () => {
164+
const root = setupTest();
165+
root.querySelector('[data-mdc-auto-init]').setAttribute('data-mdc-auto-init-state', 'initialized');
166+
mdcAutoInit(root);
167+
168+
assert.isFalse(root.querySelector('.mdc-fake').FakeComponent instanceof FakeComponent);
169+
});
170+
171+
test('does not return any new components after calling autoInit a second time', () => {
172+
const root = setupTest();
173+
174+
let components = mdcAutoInit(root);
175+
assert.equal(components.length, 1);
176+
components = mdcAutoInit(root);
177+
assert.equal(components.length, 0);
178+
});

0 commit comments

Comments
 (0)