Skip to content

Commit 2fc5b70

Browse files
alan-agius4dylhunn
authored andcommitted
fix(platform-server): insert transfer state script before other script tags (#48868)
Previously, the state `script` was always appended as the last item in the `body` tag. This can result in the state not being available when the Angular application is bootstrap. A workaround for this was to delay the bootstrapping of the application until by using the `DOMContentLoaded` event listener. ```ts const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule); document.addEventListener('DOMContentLoaded', bootstrap); ``` With this change the above workaround is no longer necessary as the state `script` tag is now added prior of any other `script` which guarantees that the state is present prior of the Angular application is bootstrapped. PR Close #48868
1 parent 03f47ac commit 2fc5b70

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

packages/platform-server/src/transfer_state.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ function serializeTransferStateFactory(doc: Document, appId: string, transferSto
3535
script.id = appId + '-state';
3636
script.setAttribute('type', 'application/json');
3737
script.textContent = escapeHtml(content);
38-
doc.body.appendChild(script);
38+
const existingScript = doc.body.querySelector('script');
39+
if (existingScript) {
40+
// Insert the state script before any script so that the the script is available
41+
// before Angular is bootstrapped as otherwise this can causes the state not to be present.
42+
existingScript.before(script);
43+
} else {
44+
doc.body.appendChild(script);
45+
}
3946
};
4047
}
4148

packages/platform-server/test/transfer_state_spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,36 @@ describe('transfer_state', () => {
8383
const output = await renderModule(TransferStoreModule, {document: '<app></app>'});
8484
expect(output).toBe(defaultExpectedOutput);
8585
});
86+
87+
it('adds transfer script tag before any existing script tags', async () => {
88+
const STATE_KEY = makeStateKey<number>('test');
89+
90+
@Component({selector: 'app', template: 'Works!'})
91+
class TransferComponent {
92+
constructor(private transferStore: TransferState) {
93+
this.transferStore.onSerialize(STATE_KEY, () => 10);
94+
}
95+
}
96+
97+
@NgModule({
98+
bootstrap: [TransferComponent],
99+
declarations: [TransferComponent],
100+
imports: [BrowserModule.withServerTransition({appId: 'transfer'}), ServerModule],
101+
})
102+
class TransferStoreModule {
103+
}
104+
105+
const output = await renderModule(TransferStoreModule, {
106+
document: '<app></app><script src="polyfills.js"></script><script src="main.js"></script>'
107+
});
108+
expect(output).toContain(
109+
'<body>' +
110+
'<app ng-version="0.0.0-PLACEHOLDER" ng-server-context="other">Works!</app>' +
111+
'<script id="transfer-state" type="application/json">' +
112+
'{&q;test&q;:10}' +
113+
'</script>' +
114+
'<script src="polyfills.js"></script>' +
115+
'<script src="main.js"></script>' +
116+
'</body>');
117+
});
86118
});

0 commit comments

Comments
 (0)