I am trying to add SSG to my application but now I am in the dead end after trying several things and it is still not working correctly. The whole application can be found here (also with dist to see prerendered result): https://github.com/radek-stasta/radek-stasta.github.io/tree/ssg. I have added @angular/ssr, disabled ssr since I am just trying to add ssg, added routes.txt for my two testing routes, that are correctly prerendered in dist. Problem is, that no matter what I try, deployed built application is still performing javascript code, loading files from assets. Shouldn’t it just use prerendered page instead on specified path? Or am I missing something? In fact when I refresh page on selected route, it displays prerendered page for just a moment and then javascript code overwrites it. Some relevant sources:
app.routes.ts:
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'articles/:articleName/:language',
component: ArticleViewerComponent,
},
];
routes.txt:
/articles/publishing-angular-app-to-github-pages/cz
/articles/publishing-angular-app-to-github-pages/en
ArticleViewerComponent:
@Component({
selector: 'app-article-viewer',
standalone: true,
imports: [TranslateModule, NgClass],
templateUrl: './article-viewer.component.html',
styleUrl: './article-viewer.component.sass',
animations: [
trigger('summaryState', [
state('up', style({ bottom: 'calc(100% - 6rem)' })),
state('down', style({ top: '4rem' })),
transition('up => down', [animate('0.25s')]),
transition('down => up', [animate('0.25s')]),
]),
],
})
export class ArticleViewerComponent
implements OnInit, OnDestroy, AfterViewInit
{
@Input() headerElement!: ElementRef;
@ViewChild('summaryPanelToggle', { static: false })
summaryPanelToggleElement!: ElementRef;
private _languageChangeSubscription: Subscription = new Subscription();
private _reloadArticleSubscription: Subscription = new Subscription();
protected articleHtml: SafeHtml = '';
protected summaryLines: string[] = [];
protected isSummaryCollapsed = true;
constructor(
private _fileReaderService: FileReaderService,
private _orgToHtmlConverterService: OrgToHtmlConverterService,
private _sanitizer: DomSanitizer,
private _dataService: DataService,
private _route: ActivatedRoute,
private _renderer: Renderer2,
private _el: ElementRef,
private _translateService: TranslateService,
@Inject(DOCUMENT) private _document: Document,
) {}
async ngOnInit() {
await this.reloadArticle();
}
ngAfterViewInit() {
const script = this._renderer.createElement('script');
script.defer = true;
script.async = true;
script.src = 'https://giscus.app/client.js';
script.setAttribute('crossorigin', 'anonymous');
script.setAttribute('data-repo', 'radek-stasta/radek-stasta.github.io');
script.setAttribute('data-repo-id', 'R_kgDOMVlNQw');
script.setAttribute('data-category', 'giscus');
script.setAttribute('data-category-id', 'DIC_kwDOMVlNQ84ChCZ1');
script.setAttribute('data-mapping', 'url');
script.setAttribute('data-strict', '0');
script.setAttribute('data-reactions-enabled', '1');
script.setAttribute('data-emit-metadata', '0');
script.setAttribute('data-input-position', 'bottom');
script.setAttribute('data-theme', 'light');
script.setAttribute('data-lang', 'en');
this._renderer.appendChild(this._el.nativeElement, script);
}
@HostListener('document:click', ['$event'])
public handleLinks(event: Event): void {
const target = event.target as HTMLElement;
if (
target.tagName === 'A' &&
target.getAttribute('href')?.startsWith('#')
) {
event.preventDefault();
const id = target.getAttribute('href')!.slice(1);
// Get the element
const element = this._document.getElementById(id);
const scrollableDiv = this._document.getElementById('main-div');
if (element && scrollableDiv) {
// Calculate the position to scroll to
scrollableDiv.scrollTop =
element.offsetTop -
this.summaryPanelToggleElement.nativeElement.clientHeight -
this._dataService.headerHeight;
this.isSummaryCollapsed = true;
}
}
}
@HostListener('window:resize', ['$event'])
onResize() {
this.isSummaryCollapsed = true;
}
async reloadArticle() {
const routeParams = this._route.snapshot.params;
// load article html
const articleResult = await this._fileReaderService.readFile(
`articles/${routeParams['articleName']}/${routeParams['language']}.txt`,
);
this.articleHtml = this._sanitizer.bypassSecurityTrustHtml(
this._orgToHtmlConverterService.convert(articleResult.text, [
{
placeholder: 'lastModified',
substitution: articleResult.lastModified,
},
]),
);
// construct summary lines
const serviceSummaryLines = this._orgToHtmlConverterService.summaryLines;
this.summaryLines = serviceSummaryLines.map((line) => {
const indentation = line.type === EHeadingType.h2 ? 'pl-8' : '';
return `<div class="${indentation}"><a href="#${line.id}">${line.text}</a></div>`;
});
// add comments link
this.summaryLines.push(
`<div class=""><a href="#comments">${this._translateService.instant('articles.comments')}</a></div>`,
);
}
toggleSummary() {
this.isSummaryCollapsed = !this.isSummaryCollapsed;
}
ngOnDestroy(): void {
this._languageChangeSubscription.unsubscribe();
this._reloadArticleSubscription.unsubscribe();
}
}
extract of article file:
#+title: Publishing an Angular Web Application to GitHub Pages
#+TAGS: Angular
#+author: Radek Šťasta
#+date: {{lastModified}}
* Introduction
When creating websites, a necessary requirement is to publish them on the internet so that they are accessible to the world
at a chosen web address. For these purposes, there are many web hosting providers that offer services both free and for a fee.
In earlier times, the classic scenario of creating a website was such that a person usually created a multi-page website
(i.e., a website consisting of individual HTML pages like Home, Contact, etc.), paid for a domain, and uploaded the
necessary files to this domain. Nowadays, it is common to have a single-page web application (created, for example, in
frameworks like Angular, React, Vue). Of course, it depends on the use case. Both approaches have their pros and cons.
Moreover, it is now common to have the source code backed up by a version control tool such as Git. The GitHub web service
offers this version control for free (for basic use) and also offers the GitHub Pages service, which this article focuses on.
prerendered index.html in dist/website-personal/browser/articles/publishing-angular-app-to-github-pages/en:
<!DOCTYPE html><html lang="en" data-critters-container><head>
<meta charset="utf-8">
<title>Radek Šťasta - personal website</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<style>*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}h1,h2{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}button{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button{text-transform:none}button{-webkit-appearance:button;background-color:transparent;background-image:none}h1,h2,pre{margin:0}button{cursor:pointer}img,svg{display:block;vertical-align:middle}img{max-width:100%;height:auto}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.z-10{z-index:10}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.my-8{margin-top:2rem;margin-bottom:2rem}.mb-4{margin-bottom:1rem}.flex{display:flex}.hidden{display:none}.size-fit{width:fit-content;height:fit-content}.h-16{height:4rem}.h-8{height:2rem}.h-dvh{height:100dvh}.h-full{height:100%}.w-1/3{width:33.333333%}.w-16{width:4rem}.w-8{width:2rem}.w-full{width:100%}.cursor-pointer{cursor:pointer}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.place-content-center{place-content:center}.place-content-end{place-content:end}.place-items-center{place-items:center}.items-center{align-items:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded-2xl{border-radius:1rem}.rounded-xl{border-radius:.75rem}.rounded-b-2xl{border-bottom-right-radius:1rem;border-bottom-left-radius:1rem}.rounded-b-full{border-bottom-right-radius:9999px;border-bottom-left-radius:9999px}.rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.border-2{border-width:2px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-indigo-500{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.bg-indigo-100{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity))}.bg-rose-100{--tw-bg-opacity: 1;background-color:rgb(255 228 230 / var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-indigo-500{--tw-gradient-from: #6366f1 var(--tw-gradient-from-position);--tw-gradient-to: rgb(99 102 241 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-rose-500{--tw-gradient-from: #f43f5e var(--tw-gradient-from-position);--tw-gradient-to: rgb(244 63 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-indigo-500{--tw-gradient-to: #6366f1 var(--tw-gradient-to-position)}.to-rose-500{--tw-gradient-to: #f43f5e var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-4{padding:1rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.pl-8{padding-left:2rem}.pt-10{padding-top:2.5rem}.text-center{text-align:center}.text-justify{text-align:justify}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.leading-relaxed{line-height:1.625}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity))}.text-rose-500{--tw-text-opacity: 1;color:rgb(244 63 94 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover:font-bold:hover{font-weight:700}.hover:text-rose-500:hover{--tw-text-opacity: 1;color:rgb(244 63 94 / var(--tw-text-opacity))}.hover:underline:hover{text-decoration-line:underline}@media (min-width: 640px){.sm:mx-0{margin-left:0;margin-right:0}.sm:text-2xl{font-size:1.5rem;line-height:2rem}.sm:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm:text-6xl{font-size:3.75rem;line-height:1}}@media (min-width: 1024px){.lg:relative{position:relative}.lg:my-4{margin-top:1rem;margin-bottom:1rem}.lg:block{display:block}.lg:hidden{display:none}.lg:w-1/3{width:33.333333%}.lg:w-2/3{width:66.666667%}.lg:pl-8{padding-left:2rem}}.hljs{background:#2e3440}.hljs{color:#d8dee9}.hljs-built_in{color:#8fbcbb}.hljs-keyword,.hljs-symbol{color:#81a1c1}.hljs-string{color:#a3be8c}.hljs-link:hover{text-decoration:underline}.hljs-comment{color:#4c566a}.hljs-meta{color:#5e81ac}
</style><link rel="stylesheet" href="styles-LSJSQAGH.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-LSJSQAGH.css"></noscript><style ng-app-id="ng">.main-div[_ngcontent-ng-c2041827018]{height:calc(100% - 4rem)}</style><style ng-app-id="ng">.summary-panel-up[_ngcontent-ng-c1678981558]{bottom:calc(100% - 6rem)}.summary-panel-down[_ngcontent-ng-c1678981558]{top:4rem}</style></head>
<body class="h-dvh overflow-hidden"><!--nghm-->
<app-root _nghost-ng-c2041827018 ng-version="18.1.3" ngh="1" ng-server-context="ssg"><div _ngcontent-ng-c2041827018 class="relative h-16 z-10 top-0 bg-gradient-to-r to-indigo-500 from-rose-500"><div _ngcontent-ng-c2041827018 class="flex flex-row text-2xl text-white"><button _ngcontent-ng-c2041827018 tabindex="0" class="w-1/3"><img _ngcontent-ng-c2041827018 ngsrc="/img/myself-round.png" alt="Radek Šťasta" width="489" height="489" class="w-16 p-1" loading="lazy" fetchpriority="auto" ng-img="true" src="/img/myself-round.png"></button><div _ngcontent-ng-c2041827018 class="flex w-1/3 place-content-center"><button _ngcontent-ng-c2041827018 tabindex="0" class="px-10 cursor-pointer h-full place-content-center hover:font-bold"> ARTICLES </button></div><div _ngcontent-ng-c2041827018 class="flex w-1/3 place-content-end items-center p-1 gap-2 text-gray-400"><button _ngcontent-ng-c2041827018 tabindex="0" class="cursor-pointer hover:font-bold"> cz </button><button _ngcontent-ng-c2041827018 tabindex="0" class="cursor-pointer hover:font-bold text-white"> en </button></div></div></div><!----><!----><div _ngcontent-ng-c2041827018 id="main-div" class="main-div overflow-auto"><router-outlet _ngcontent-ng-c2041827018></router-outlet><app-article-viewer _nghost-ng-c1678981558 class="ng-tns-c1678981558-0 ng-star-inserted" ngh="0"><div _ngcontent-ng-c1678981558 class="ng-tns-c1678981558-0"><div _ngcontent-ng-c1678981558 class="container mx-auto pt-10 ng-tns-c1678981558-0"><div _ngcontent-ng-c1678981558 class="flex flex-row text-xl ng-tns-c1678981558-0"><div _ngcontent-ng-c1678981558 class="w-full lg:w-2/3 leading-relaxed ng-tns-c1678981558-0"><div _ngcontent-ng-c1678981558 class="mx-4 sm:mx-0 ng-tns-c1678981558-0"><h1 class="text-4xl sm:text-6xl font-bold"> Publishing an Angular Web Application to GitHub Pages</h1>
<div class="flex flex-row gap-1 my-1"><div class="size-fit px-2 py-1 rounded-xl bg-rose-100">Angular</div></div>
<div class="size-fit px-2 py-1 my-1 rounded-xl bg-indigo-100"> Radek Šťasta</div>
<div class="size-fit px-2 py-1 my-1 rounded-xl bg-indigo-100"> null</div>
<div class="my-8 text-justify">
<h1 class="text-2xl sm:text-4xl my-8 font-bold" id="hTag0">Introduction</h1>
</div><div class="my-8 text-justify">
When creating websites, a necessary requirement is to publish them on the internet so that they are accessible to the world
at a chosen web address. For these purposes, there are many web hosting providers that offer services both free and for a fee.
In earlier times, the classic scenario of creating a website was such that a person usually created a multi-page website
(i.e., a website consisting of individual HTML pages like Home, Contact, etc.), paid for a domain, and uploaded the
necessary files to this domain. Nowadays, it is common to have a single-page web application (created, for example, in
frameworks like Angular, React, Vue). Of course, it depends on the use case. Both approaches have their pros and cons.
Moreover, it is now common to have the source code backed up by a version control tool such as Git. The GitHub web service
offers this version control for free (for basic use) and also offers the GitHub Pages service, which this article focuses on.
Every time I go to route /articles/publishing-angular-app-to-github-pages/en, it still calls this.reloadArticle() and performs everything inside. As I understand it, it should just display static prerendered index.html no? Or do I get it wrong? Please help. 🙂