This project was generated with Angular CLI version 18.0.1.
- Clone only the remote primary HEAD (default: origin/master)
git clone <url> --single-branch- Only specific branch
git clone <url> --branch <branch> --single-branch [<folder>]git clone <url> --branch <branch>-
Cloning repositories using degit
- main branch is default.
npx degit github:user/repo#branch-name <folder-name>- Cloning this project with skeleton
git clone https://github.com/actionanand/ng-lifecycle.git --branch 1-skeleton angular-proj-namenpx degit github:actionanand/ng-lifecycle#1-skeleton angular-proj-name- Install the compatible node version
nvm install v20.13.1-
Install and Configure Prettier
- Install prettier as below:
yarn add prettier -D
- Create a
.prettierrcfile and write down the format as below: - online ref
trailingComma: 'all' tabWidth: 2 useTabs: false semi: true singleQuote: true bracketSpacing: true bracketSameLine: true arrowParens: 'avoid' printWidth: 120 overrides: - files: - '*.js' - '*.jsx' options: bracketSpacing: true jsxSingleQuote: true semi: true singleQuote: true tabWidth: 2 useTabs: false - files: - '*.ts' options: tabWidth: 2
- Create a
.prettierignorefile and write as below(sample)
# Ignore artifacts: build coverage e2e node_modules dist dest reports # Ignore files *.lock package-lock.json yarn.lock
-
Install
Es Lint, if not installed
ng add @angular-eslint/schematicsif error comes, use the below command
ng add @angular-eslint/schematics@next- Configure pre-commit hooks
Pre-commit hooks are a nice way to run certain checks to ensure clean code. This can be used to format staged files if for some reason they weren’t automatically formatted during editing. husky can be used to easily configure git hooks to prevent bad commits. We will use this along with pretty-quick to run Prettier on our changed files. Install these packages, along with npm-run-all, which will make it easier for us to run npm scripts:
yarn add husky pretty-quick npm-run-all -DTo configure the pre-commit hook, simply add a precommit npm script. We want to first run Prettier, then run TSLint on the formatted files. To make our scripts cleaner, I am using the npm-run-all package, which gives you two commands, run-s to run scripts in sequence, and run-p to run scripts in parallel:
"precommit": "run-s format:fix lint",
"format:fix": "pretty-quick --staged",
"format:check": "prettier --config ./.prettierrc --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\"",
"format:all": "prettier --config ./.prettierrc --write \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\"",
"lint": "ng lint",-
Initialize husky
- Run it once
npm pkg set scripts.prepare="husky install" npm run prepare
- Add a hook
npx husky add .husky/pre-commit "yarn run precommit" npx husky add .husky/pre-commit "yarn test" git add .husky/pre-commit
- Make a commit
git commit -m "Keep calm and commit" # `yarn run precommit and yarn test` will run every time you commit
-
How to skip prettier format only in particular file
- JS
matrix(1, 0, 0, 0, 1, 0, 0, 0, 1); // prettier-ignore matrix( 1, 0, 0, 0, 1, 0, 0, 0, 1 )
- JSX
<div> {/* prettier-ignore */} <span ugly format='' /> </div>
- HTML
<!-- prettier-ignore --> <div class="x" >hello world</div > <!-- prettier-ignore-attribute --> <div (mousedown)=" onStart ( ) " (mouseup)=" onEnd ( ) "></div> <!-- prettier-ignore-attribute (mouseup) --> <div (mousedown)="onStart()" (mouseup)=" onEnd ( ) "></div>
- CSS
/* prettier-ignore */ .my ugly rule { }
- Markdown
<!-- prettier-ignore --> Do not format this- YAML
# prettier-ignore key : value hello: world
- For more, please check
Sometimes it’s necessary to use browser-only APIs to manually read or write the DOM. This can be challenging to do with the lifecycle events above, as they will also run during server-side rendering and pre-rendering. For this purpose, Angular provides afterRender and afterNextRender. These functions can be used unconditionally, but will only have an effect on the browser. Both functions accept a callback that will run after the next change detection cycle (including any nested cycles) has completed.
Both hooks must be invoked within the injection context (inside constructor) and can accept an injector if there’s a need to run them outside of this context:
@Component()
export class MyCmp {
private injector = inject(Injector);
onClick() {
afterNextRender(
() => {
// Do something
},
{ injector: this.injector },
);
}
}These 2 hooks are right, If you need to perform DOM manipulations after the view is fully rendered and stable.
The afterNextRender hook, albeit not the most descriptive name, takes a callback function that runs once after the subsequent change detection cycle. Typically, afterNextRender is ideal for performing one-time initializations, such as integrating third-party libraries or utilizing browser-specific APIs.
The afterNextRender callback executes once per tick and then destroys itself. This implies that while we're not restricted to calling it only once, each invocation ensures it runs just once after the subsequent tick.
@Component()
export class MyCmp {
constructor() {
afterNextRender(() => {
console.log('run once');
});
setTimeout(() => {
afterNextRender(() => {
console.log('run once');
});
}, 5000);
}
}Generally, if you need to manually read or write any layout data, such as size or location, you should use afterRender. The purpose of this function is to synchronise with the DOM and it is called after every change detection cycle that follows.
@Component({
selector: 'my-cmp',
template: `<span #content></span>`,
})
export class MyComponent {
content = viewChild.required<ElementRef>('content');
constructor() {
afterRender(() => {
console.log(this.content().nativeElement.scrollHeight);
});
}
}When employing afterRender or afterNextRender, you have the option to specify a phase, offering precise control over the sequencing of DOM operations. This capability allows you to arrange write operations before read operations, thereby minimizing layout thrashing.
@Component({...})
export class ExampleComponent {
private elementWidth = 0;
private elementRef = inject(ElementRef);
constructor() {
const nativeElement = this.elementRef.nativeElement;
// Write phase: Adjusting width.
afterNextRender(() => {
nativeElement.style.width = computeWidth();
}, { phase: AfterRenderPhase.Write });
// Read phase: Retrieving final width after adjustments.
afterNextRender(() => {
this.elementWidth = nativeElement.getBoundingClientRect().width;
}, { phase: AfterRenderPhase.Read });
}
}-
The phases operate in a predetermined sequence:
EarlyRead: This phase is ideal for reading any layout-affecting DOM properties and styles necessary for subsequent calculations. If possible, minimize usage of this phase, favoring the Write and Read phases.MixedReadWrite: This phase serves as the default option. It’s suitable for operations requiring both read and write access to layout-affecting properties and styles. However, it’s advisable to use the explicit Write and Read phases whenever possible.Write: Opt for this phase when setting layout-affecting DOM properties and styles.Read: Utilize this phase for reading layout-affecting DOM properties.
-
Source - Exploring Angular’s afterRender and afterNextRender Hooks
we can understand the lifecycle hooks by splitting the process into two steps, first-time hooks, and in every change detection cycle hooks.
- first-time hooks, the triggered hooks are:
- onChanges
- onInit
- doCheck
- afterContentInit
- afterContentChecked
- afterViewInit
- afterViewChecked
- in every change detection cycle hooks, the triggered hooks are:
- onChanges
- doCheck
- afterContentChecked
- afterViewChecked