tastejs / angular-movies Goto Github PK
View Code? Open in Web Editor NEWA Angular Movies App Optimized for Performance
Home Page: https://angular-movies-a12d3.web.app/list/category/popular
License: MIT License
A Angular Movies App Optimized for Performance
Home Page: https://angular-movies-a12d3.web.app/list/category/popular
License: MIT License
In list detail page we can see that all posters have broken images, that's because srcset is wrong:
srcset="
https://image.tmdb.org/t/p/w355/g4a5YLWwi6OCp8TcvxsUNrXMbN5.jpg 1x,
https://image.tmdb.org/t/p/w710/g4a5YLWwi6OCp8TcvxsUNrXMbN5.jpg 2x"
Actually available sizes from TMDB are
Poster Sizes:
Backdrop Sizes:
Profile Sizes:
Still Sizes:
Temporary fix done in #256
We want to experiment with the following features:
Setup story book for components to tests
I came across this issue related to Angular Universal in combination with a zoneless app while working on performance improvements in my own app. I wanted to look for a solution here, but found the same problem to occur in angular-movies.
Even though API calls are done by the server and saved into TransferState, those API calls are made by the client again.
Just have a look into the network tab when loading the live deployment of the application at https://angular-movies-a12d3.web.app/list/category/popular
I did some debugging and found the issue to be in TransferHttpCacheInterceptor. When appRef.isStable
emits the first time, the cache is set to inactive. In an app that is zoneless, the timing of this emit seems to differ from an app that utilizes zone.js.
The app will be stable before any HTTP request is processed, thus the cache will never be used. If you implement this exact HttpInterceptor yourself and use that instead of HttpTransferCacheModule
and also for example just comment out the isStable
subscription in the constructor, caching will work fine.
I did not find out what causes the zoneless app to be considered stable earlier. Gotta dig into how isStable
works. Maybe you know better :-)
The following screens could be updated on the desktop:
If there are many PRs the preview channel deploy fails
Deploying to Firebase preview channel
/opt/hostedtoolcache/node/18.16.1/x64/bin/npx firebase-tools hosting:channel:deploy --expires 7d --project angular-movies-a12d3 --json
***
"status": "error",
"error": "channelID is currently required"
***
***
"status": "error",
"error": "channelID is currently required"
***
The process '/opt/hostedtoolcache/node/18.16.1/x64/bin/npx' failed with exit code 1
Retrying deploy with the --debug flag for better error output
/opt/hostedtoolcache/node/18.16.1/x64/bin/npx firebase-tools hosting:channel:deploy --expires 7d --project angular-movies-a12d3 --debug
[2023-07-13T10:38:30.069Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
[2023-07-13T10:38:30.189Z] [iam] checking project angular-movies-a12d3 for permissions ["firebase.projects.get","firebasehosting.sites.update"]
[2023-07-13T10:38:30.190Z] >>> [apiv2][query] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions [none]
[2023-07-13T10:38:30.191Z] >>> [apiv2][(partial)header] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions x-goog-quota-user=projects/angular-movies-a12d3
[2023-07-13T10:38:30.191Z] >>> [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions ***"permissions":["firebase.projects.get","firebasehosting.sites.update"]***
[2023-07-13T10:38:30.304Z] <<< [apiv2][status] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions 200
[2023-07-13T10:38:30.304Z] <<< [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions ***"permissions":["firebase.projects.get","firebasehosting.sites.update"]***
[2023-07-13T10:38:30.306Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 [none]
[2023-07-13T10:38:30.505Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 200
[2023-07-13T10:38:30.505Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 ***"projectId":"angular-movies-a12d3","projectNumber":"453188470[76](https://github.com/tastejs/angular-movies/actions/runs/5542079633/jobs/10116298444#step:6:79)1","displayName":"angular-movies","name":"projects/angular-movies-a12d3","resources":***"hostingSite":"angular-movies-a12d3","storageBucket":"angular-movies-a12d3.appspot.com","locationId":"us-central"***,"state":"ACTIVE","etag":"1_d8f3b634-5217-42f6-94f3-1de87149e3d2"***
[2023-07-13T10:38:30.506Z] Expires TTL: 604800000
Error: channelID is currently required
[2023-07-13T10:38:30.069Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
[2023-07-13T10:38:30.189Z] [iam] checking project angular-movies-a12d3 for permissions ["firebase.projects.get","firebasehosting.sites.update"]
[2023-07-13T10:38:30.190Z] >>> [apiv2][query] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions [none]
[2023-07-13T10:38:30.191Z] >>> [apiv2][(partial)header] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions x-goog-quota-user=projects/angular-movies-a12d3
[2023-07-13T10:38:30.191Z] >>> [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions ***"permissions":["firebase.projects.get","firebasehosting.sites.update"]***
[2023-07-13T10:38:30.304Z] <<< [apiv2][status] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions 200
[2023-07-13T10:38:30.304Z] <<< [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/angular-movies-a12d3:testIamPermissions ***"permissions":["firebase.projects.get","firebasehosting.sites.update"]***
[2023-07-13T10:38:30.306Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 [none]
[2023-07-13T10:38:30.505Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 200
[2023-07-13T10:38:30.505Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/angular-movies-a12d3 ***"projectId":"angular-movies-a12d3","projectNumber":"453188470761","displayName":"angular-movies","name":"projects/angular-movies-a12d3","resources":***"hostingSite":"angular-movies-a12d3","storageBucket":"angular-movies-a12d3.appspot.com","locationId":"us-central"***,"state":"ACTIVE","etag":"1_d8f3b634-5217-42f6-94f3-1de87149e3d2"***
[2023-07-13T10:38:30.506Z] Expires TTL: 604[80](https://github.com/tastejs/angular-movies/actions/runs/5542079633/jobs/10116298444#step:6:83)0000
Error: channelID is currently required
The process '/opt/hostedtoolcache/node/18.16.1/x64/bin/npx' failed with exit code 1
Error: The process '/opt/hostedtoolcache/node/18.16.1/x64/bin/npx' failed with exit code 1
***
conclusion: 'failure',
output: ***
title: 'Deploy preview failed',
summary: "Error: The process '/opt/hostedtoolcache/node/18.16.1/x64/bin/npx' failed with exit code 1"
***
***
##[debug]Node Action run completed with exit code 1
##[debug]Finishing: Deploy firebase live hosting
Dynamic pages are hard to optimize for the LCP as we can't determine the LCP before executing the page.
It is important to optimize the LCP because it is part of the core web vitals and has a direct impact on user experience.
If we could know the LCP we could use preload and fetch priority to increase the network priority and load the LCP image much faster.
This can be applied to pre-rendered pages.
e.g. inject
<img src="img.png" fetchpriority="high" />
<link rel="preload" as="image" href="img.png" fetchpriority="high" />
ATM preloading of images is done manually.
An example is manually adding the LCP image in the index.html. This can only improve situations where the LCP is static.
For pre-rendered pages we can run analyzing tools against the page e.g. lighthouse and determine the LCP and if it is an image.
Here is a detailed definition LCP.
In our case the following content is relevant:
<img>
elements<image>
elements inside an <svg>
element<video>
elements (the poster image is used)After identifying the LCP element and its content source we can insert a link tag using the preload rel attribute to improve the download prioritization of the content by the browser, thus overwriting the HTML file of the pre-rendered page.
As the code should also be executed in the CI pipeline we should create a CLI executable in the CI.
The CLI should maintain the following commands:
ngSrc
does not allow updating the input dynamically. An error is displayed in the console:
caught Error: NG02953: The NgOptimizedImage directive (activated on an <img> element with the `ngSrc="/nsxnMbkl9Lpc12AmYwKi9SmsJ7e.jpg"`) has detected that `ngSrc` was updated after initialization. The NgOptimizedImage directive will not react to this input change. Changing the `ngSrc` would have no effect on the underlying image element, because the resource loading has already occurred. To fix this, either switch `ngSrc` to a static value or wrap the image element in an *ngIf that is gated on the necessary value.
at postInitInputChangeError (common.mjs:6416:12)
at common.mjs:6435:19
at Array.forEach (<anonymous>)
at assertNoPostInitInputChange (common.mjs:6425:12)
at NgOptimizedImage.ngOnChanges (common.mjs:6143:13)
at NgOptimizedImage.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:2812:14)
at callHookInternal (core.mjs:3804:14)
at callHook (core.mjs:3835:9)
at callHooks (core.mjs:3786:17)
at executeCheckHooks (core.mjs:3717:5)
There is an open issue angular/angular#47813
The alternatives are:
NgOptimizedImage
again (see example in the angular issue above)<ng-template>
+ <ng-container>
OR a directive like https://github.com/ngehlert/ngx-rerender/blob/main/projects/ngx-rerender/src/lib/ngx-rerender.directive.tsngSrc
because it is for static imageI can make the PR if needed!
I've also checked with other demos and they work correctly.
/*
https://github.com/tastejs/angular-movies/blob/c72d7197677c3698a9cd0fcb22920b05460c1fbc/projects/movies/src/app/ui/component/dark-mode-toggle/dark-mode-toggle.component.ts
*/
<span class="toggle">
<input
*rxLet="isLightTheme$; let isLightTheme; strategy: 'immediate'"
class="toggle-track"
type="checkbox"
id="dark-mode"
[checked]="!isLightTheme"
(change)="setChecked(!isLightTheme)"
/>
<label style="color: transparent" for="dark-mode">
Toggle Switch
</label>
</span>
_button.scss
by importing it againdialog
element for overlay in list-remove.component.html
article
. Do we actually want that? For the most part we can get rid of the wrapper elements at all by just doing :host { display: block; }
onStable
patch (ATM it is a static timeout)axios
dist/projects/movies/browser/dist/tmp/projects/movies/server/routes.txt/index.html
output for prerender
firebase
, firebase-tools
and firebase-gh-actions
firebase.json#functions#ignore
& firebase.json#functions#ignore
)package.json
into dist to have it disconnected form the main package.json
. https://stackoverflow.com/questions/74944491/husky-prepare-script-failing-firebase-function-deploymenttsconfig.json
for ts-node.register
The SSR part is flaky ATM. A couple of resulted problems where:
This impacts the score significantly also in the CrUX report.
Due to this reasons I suggest a list of possible improve up for evaluation:
Tooling
Code structure
functions
folder, server.ts
outside of projects, main.ts
and main.server.ts
are very different in content, etc.) a better file organization is needed zone.js
) should be done with target configurations and the ng build process (ng build-ssr movies --configuration ssr
)functions
and hosting
should life in the functions folderDocumentation
npm run start:ssr:dev
locally to validate changes to Browser and Server versionsTesting
HTML
from the build process.ngSrc
Improvements
While trying to implement ISR ngx-isr I found out that it didn't work.
The reason is because the routing depends on zone.js implementation.
For ISR in order to get the route data, something like this is implemented:
this.router.events
.pipe(
filter((e) => e instanceof ChildActivationEnd),
map((event) => {
let snapshot = (event as ChildActivationEnd).snapshot;
while (snapshot.firstChild !== null) {
snapshot = snapshot.firstChild;
}
return snapshot.data;
}),
take(1)
)
.subscribe((data: any) => {
if (data?.['revalidate'] !== undefined) {
this.setRevalidate(data['revalidate']);
}
});
It listens to router events, and waits for it to emit, also, because in zoneless the isStable observable emits directly, ISR doesn't have time to listen to stuff on SSR and add the revalidate data to html.
Without finding a way to wait for the router stuff to finish but also for the api requests (that zone.js helps with), we can't have correct http transfer cache but also ISR features in zoneless apps.
Hello,
I cloned this repo and ran followed the steps in Readme. I have created my own tmdbApiKey
and tmdbApiReadAccessKey
. In Readme it says npm run build:dev
to run it locally but I don't see any script in package.json. I ran npm run start
assuming it's the start command but I get below -
$ npm run start
> [email protected] start
> nx serve movies:development
> NX Cannot find configuration for task movies:development
Pass --verbose to see the stacktrace.
Please provide me some hints here. Thanks.
Use input transforms wherever applicable.
example before:
@Input({required: true})
set lazy(component: RxInputType<Type<any>>) {
this.connect('component', coerceObservable(component));
}
example after:
@Input({required: true, transform: <T>(value: RxInputType<T>) => coerceObservable(value)})
set lazy(component: Observable<Type<any>>) {
this.connect('component', component);
}
or:
@Input({required: true, transform: coerceObservable<Type<any>>})
set lazy(component: Observable<Type<any>>) {
this.connect('component', component);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.