ngx NoCodeSmell iFour Consultancy Respect to principles and quality
https://www.ifourtechnolab.com/
What is Code Smell ? ● In computer programming, a code smell is any characteristic in the source code of a program that possibly indicates a deeper problem. ● One way to look at smells is with respect to principles and quality. ● Why no code smell require? 1. Code smells are usually not bugs. 2. They are not technically incorrect and do not prevent the program from functioning. 3. Instead, they indicate weaknesses in design that may slow down development or increase the risk of bugs or failures in the future. 4. Bad code smells can be an indicator of factors that contribute to technical debt.
https://www.ifourtechnolab.com/
Topics
● ● ● ● ● ● ● ●
Single Responsibility Application Structure and NgModules Naming and Coding Conventions Components Directives Services Data Services Life-cycle hooks
https://www.ifourtechnolab.com/
Single responsibility ⚫ ⚫
We should apply the single responsibility principle (SRP) to all components, services, and other symbols. This helps make the app cleaner, easier to read and maintain, and more testable.
⚫
Rule 1 ⚫ Do > define one thing, such as a service or component, per file. ⚫ Consider > limiting files to 400 lines of code.
⚫
Rule 2 ⚫ Do > define small functions ⚫ Consider > limiting to no more than 75 lines.
https://www.ifourtechnolab.com/
Application structure and NgModules ⚫ ⚫
⚫
All code goes in src folder. Use only one asset folder for contents. Follow the LIFT structure ⚫
Locate (: LIFT) ⚫ ⚫ ⚫ ⚫
Make locating code simple and fast. Find files quickly, especially when you don’t know the file names. Keep related files together. Keep descriptive folder structure.
https://www.ifourtechnolab.com/
LIFT
Application structure and NgModules
login.module.ts login-routing.module.ts login.component.ts
app.module.ts app-routing.module.ts app.component.ts
employee.module.ts employee-routing.module.ts teacher > account > staff > shared > // for employee
core.module.ts auth > auth.interceptor.ts > auth.guard.ts > azure.service.ts
student.module.ts student-routing.module.ts add-student.component.ts edit-student.component.ts list-student.component.ts shared // for student
shared.module.ts services > pipes > models > ts >
https://www.ifourtechnolab.com/
src/assets > css > i18n > fonts > pdfs > imgs
Application structure and NgModules
core.module.ts auth > auth.interceptor.ts > auth.guard.ts > azure.service.ts
login.module.ts login-routing.module.ts login.component.ts
app.module.ts app-routing.module.ts app.component.ts
student.module.ts student-routing.module.ts add-student.component.ts edit-student.component.ts list-student.component.ts shared // for student
shared.module.ts services > pipes > models > ts >
https://www.ifourtechnolab.com/
employee.module.ts employee-routing.module.ts teacher > > account-details account > add-account > list-account > account-details > shared staff > shared > // for employee
src/assets > css > i18n > fonts > pdfs > imgs
Application structure and NgModules contd. ⚫
Identify (: LIFT) ⚫ ⚫ ⚫ ⚫ ⚫
Name file such that you can easily find it’s content. Keep descriptive file name and it’s content. Avoid files with multiple components, services, or mixture. Spend less time for hunting the code, to become more efficient. Longer file names are far better than short abbreviated names.
https://www.ifourtechnolab.com/
LIFT
Application structure and NgModules contd. ⚫
Flat (: LIFT) ⚫ Keep flat folder structure as long as possible. ⚫ Consider creating sub-folders when a folder reaches seven or more files. ⚫ Configuring the IDE to hide distracting, irrelevant files such as generated .js and .js.map files. ⚫ A flat structure is easy to scan.
https://www.ifourtechnolab.com/
Compliant folder and file structure: <project root> - src -app -core -compo1 -comp1-shared -compo2 -shared
Application structure and NgModules contd. ⚫
T-DRY (Try to be DRY) (: LIFT) ⚫ Do be DRY (Don't Repeat Yourself). ⚫ Avoid being so DRY that you sacrifice readability. ⚫ In programming principle, It is aimed at reducing repetition of information of all kinds, and is especially useful in multi-tier architectures. ⚫ If you don’t use these principles, think of your work as WET. It Wastes Everyone’s Time when you Write Everything Twice.
https://www.ifourtechnolab.com/
WET
Folders-by-feature structure ⚫
⚫ ⚫ ⚫ ⚫
Do create folders named for the feature area they represent. The structure is as flat as it can be and there are no repetitive or redundant names. Do create a NgModule for each feature area. NgModules make it easy to lazy load routable features. App root module ⚫ Do create a NgModule in the app's root folder, for example, in /src/app. ⚫ Every app requires at least one root NgModule. ⚫ Consider naming the root module app.module.ts.
https://www.ifourtechnolab.com/
Folders-by-feature structure contd. ⚫
Shared feature module ⚫ To create a feature module named SharedModule in a shared folder ⚫ There are two types of modules A. Feature Level B. Application Level
⚫
Core feature module ⚫ Consider collecting single-use classes inside a core module to simplify the flat structure of a feature module. ⚫ Import required modules in the CoreModule (e.g. CommonModule, FormsModule, AzureModule ). https://www.ifourtechnolab.com/
Folders-by-feature structure contd. ⚫
⚫ ⚫
⚫
Prevent re-import of the core module Always create module-import-guard.ts (guard logic). Only the root AppModule should import the CoreModule. Do put the contents of lazy loaded features in a lazy loaded folder.
https://www.ifourtechnolab.com/
Naming Naming conventions are hugely important to maintainability and readability. Recommends naming conventions for the file name and the symbol name.
Rule 1 ⚫ Use consistent names for all symbols. (valid name) ⚫ Follow a pattern that describes the symbol's feature then its type. ⚫ The recommended pattern is feature.type.ts. ⚫ The naming conventions should simply help find desired code faster and make it easier to understand. ⚫ Names of folders and files should clearly convey their intent. ⚫ For example, app/heroes/hero-list.component.ts may contain a component that manages a list of heroes. https://www.ifourtechnolab.com/
Naming contd. Rule 2 ⚫
⚫ ⚫
⚫
Use dash to separate words in the descriptive name and dots to separate the descriptive name from its type.( eg. hero-list.component.ts) Don’t use Abbreviations such as .srv, .svc, and .serv as it can be confusing. Type names provide a consistent way to quickly identify what is in the file. Type names make it easy to find a specific file type using an editor or IDE's fuzzy search techniques.
https://www.ifourtechnolab.com/
Naming contd. 1. Find the valid name? A. user-component.ts B. user-list-component.ts C. user-list.components.ts D. user.component.ts
2. Find the valid name for generating component? A. ng g c userlist B. ng g c user-list C. ng g c userlistcomponent D. ng g c userlist.component
https://www.ifourtechnolab.com/
Naming contd. Find the valid name? A. user-component.ts B. user-list-component.ts C. user-list.components.ts D. user.component.ts
Find the valid name for generating component? A. ng g c userlist B. ng g c user-list C. ng g c userlistcomponent D. ng g c userlist.component
https://www.ifourtechnolab.com/
Naming contd. Rule 3 ⚫
⚫ ⚫
Use upper camel case for class names. Match the name of the symbol to the name of the file. Use consistent names for all assets named after what they represent. CLASS NAME
FILE NAME
@Component({ ... }) export class AppComponent { }
app.component.ts
@Component({ ... }) export class HeroListComponent { }
hero-list.component.ts
@Directive({ ... }) export class ValidationDirective { }
validation.directive.ts
@NgModule({ ... }) export class AppModule
app.module.ts
@Pipe({ name: 'initCaps' }) export class InitCapsPipe implements PipeTransform { }
init-caps.pipe.ts
https://www.ifourtechnolab.com/
Naming contd. Rule 4 ⚫ ⚫
Use consistent names for all services named after their feature. Suffix a service class name with Service. (Ex HeroService) FILE NAME
SERVICE CLASS
@Injectable() export class HeroDataService { }
hero-data.service.ts
@Injectable() export class CreditService { }
credit.service.ts
@Injectable() export class LogService { }
log.service.ts
https://www.ifourtechnolab.com/
Naming contd. 1. Find the valid component class name? A. export class userListComponent { } B. export class userlistcomponent { } C. export class UserListComponent { } D. export class user-List-Component { } 2. Find the valid service class name? A. export class UserListService { } B. export class userListService { } C. export class userlistService { } D. export class userlistservice { }
https://www.ifourtechnolab.com/
Naming contd. Find the valid component class name? A. export class userListComponent { } B. export class userlistcomponent { } C. export class UserListComponent { } D. export class user-List-Component { } Find the valid service class name? A. export class UserListService { } B. export class userListService { } C. export class userlistService { } D. export class userlistservice { } https://www.ifourtechnolab.com/
Naming contd. Rule 5 â&#x161;Ť Put bootstrapping and platform logic for the app in a file named main.ts. (Do) â&#x161;Ť Putting error handling and the bootstrapping logic in service or other instead of main.ts is not good practice. (Avoid)
// main.ts file import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule) .then(success => console.log(`Bootstrap success`)) .catch(err => console.error(err));
https://www.ifourtechnolab.com/
Naming contd. Rule 6 ⚫ You should declare the environment variable in a separate env file instead of declaring them in your main.ts file ⚫ This is important for disabling development in main.ts file Create environment variable for production mode //environment.prod.ts export const environment = { production: true endPoint : ’’ };
/* Do like this */ // main.ts import { environment } from
'./environments/environment'; if (environment.production) {
enableProdMode(); } https://www.ifourtechnolab.com/
Naming contd. Rule 7 â&#x161;Ť
Use a hyphenated, lowercase value for declaring routing files as shown (Ex toh-hero-button , admin-users).
1. Which is the valid command for generating a routing file? A.
B. C. D.
ng g module user --routing ng g module user-routing --routing ng g r user ng g r userrouting https://www.ifourtechnolab.com/
Naming contd. A. B. C. D.
ng g module user --routing ng g module user-routing --routing ng g r user ng g r userrouting
https://www.ifourtechnolab.com/
Components ⚫
Components as elements ⚫ Consider giving components an element selector, as opposed to attribute selectors. ⚫ Avoid <div tohHeroButton></div> (Attribute) ⚫ Do <toh-hero-button></toh-hero-button> (Element - Easy to Recognize)
⚫
Extract templates and styles to their own files ⚫ Extract templates and styles into a separate file, when more than 3 lines.
⚫
Decorate input and output properties ⚫ Use the @Input() and @Output() class decorators instead of the inputs and outputs properties of the @Directive and @Component metadata: https://www.ifourtechnolab.com/
Components contd. Which one is best way to use input and output ?
@Component({ selector: 'toh-hero-button', template: `<button></button>`, inputs: [ 'label' ], outputs: [ 'change' ] }) export class HeroButtonComponent { change = new EventEmitter<any>(); label: string; }
@Component({ selector: 'toh-hero-button', template: `<button>{{label}}</button>` }) export class HeroButtonComponent { @Output() change = new EventEmitter<any>(); @Input() label: string; }
https://www.ifourtechnolab.com/
Components contd. /* Avoid */
/* Do like this */
@Component({ selector: 'toh-hero-button', template: `<button></button>`, inputs: [ 'label' ], outputs: [ 'change' ] }) export class HeroButtonComponent { change = new EventEmitter<any>(); label: string; }
@Component({ selector: 'toh-hero-button', template: `<button>{{label}}</button>` }) export class HeroButtonComponent { @Output() change = new EventEmitter<any>(); @Input() label: string; }
- Shorter code, Easily Readable And Modify in single place. https://www.ifourtechnolab.com/
Components contd. ⚫
Avoid aliasing inputs and outputs ⚫ Avoid @Input('labelAttribute') label: string; (important purpose) ⚫ Do @Input() label: string; // No aliases
⚫
Member sequence ⚫ Place properties at top followed by methods. ⚫ Place private members after public members. export class HeroButtonComponent { private message : string; private hideLabel() {}
export class HeroButtonComponent { label: string; private message : string;
label: string; public showLabel() {} }
public hideLabel() {} private showLabel() {} } https://www.ifourtechnolab.com/
Components contd.
export class HeroButtonComponent { private message : string; label: string;
export class HeroButtonComponent { label: string; private message : string;
private hideLabel() {} public showLabel() {} }
public hideLabel() {} private showLabel() {} }
https://www.ifourtechnolab.com/
Components contd. ⚫
Delegate complex component logic to services ⚫ Limit logic in a component that required for the view. All other logic should be delegated to services. ⚫ Move reusable logic to services and keep components simple and focused on their intended purpose. /* Avoid */ getHeroes() { this.heroes = []; this.http.get(heroesUrl).pipe(map((response: Response) => <Hero[]>response.json().data), catchError(this.catchBadResponse), finalize(() => this.hideSpinner())).subscribe((heroes: Hero[]) => this.heroes = heroes); } https://www.ifourtechnolab.com/
// Do like this // getHeroes() { this.heroes = []; this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); }
Components contd. ⚫
Don't prefix output properties ⚫ Do - name events without the prefix on. <toh-hero (SavedTheDay)="SavedTheDay($event)"></toh-hero> ⚫
Do - name event handler methods with the prefix on followed by the event name. <toh-hero (savedTheDay)="onSavedTheDay($event)"></toh-hero>
⚫
Put presentation logic in the component class ⚫ Put presentation logic in the component class, and not in the template with interpolation{{ }}.
https://www.ifourtechnolab.com/
Directives
â&#x161;Ť
HostListener/HostBinding decorators versus host metadata â&#x161;Ť Consider preferring the @HostListener and @HostBinding to the host property of the @Directive and @Component decorators. â&#x161;Ť If you use the host metadata property, you must modify both the property/method declaration in the directive's class and the metadata in the decorator associated with the directive.
https://www.ifourtechnolab.com/
Directives contd. Which is standard way to use ? @Directive({ selector: '[tohValidator]', host: { '[attr.role]': 'role', '(mouseenter)': 'onMouseEnter()' } }) export class ValidatorDirective { role = 'button'; onMouseEnter() { }}
@Directive({ selector: '[tohValidator]' }) export class ValidatorDirective { @HostBinding('attr.role') role = 'button'; @HostListener('mouseenter') onMouseEnter() {
} }
https://www.ifourtechnolab.com/
Directives contd. /* Avoid */ @Directive({ selector: '[tohValidator]', host: { '[attr.role]': 'role', '(mouseenter)': 'onMouseEnter()' } }) export class ValidatorDirective { role = 'button'; onMouseEnter() { // do work }}
/* Do like this */ @Directive({ selector: '[tohValidator]' }) export class ValidatorDirective { @HostBinding('attr.role') role = 'button'; @HostListener('mouseenter') onMouseEnter() { // do work } }
- Shorter code, Easily Readable And Modify in single place. https://www.ifourtechnolab.com/
Services ⚫
Services are singletons ⚫ Use service as singleton within same injector. Use them for sharing data and functionality. ⚫ Services are ideal for sharing methods across a feature area or an app.
⚫
Single responsibility ⚫ Create services with single responsibility that is encapsulated by its context. ⚫ Create a new service once the service begins to exceed that singular purpose. ⚫ When a service has multiple responsibilities, it becomes difficult to test.
https://www.ifourtechnolab.com/
Services contd.
export class HeroService { constructor(private http: Http) { } getHeroes() { return this.http.get('api/heroes') .pipe( map((response: Response) => <Hero[]>response.json())); } }
https://www.ifourtechnolab.com/
Services contd. ⚫
Providing a service ⚫ Do provide a service with the app root injector in the @Injectable decorator of the service. ⚫ The Angular injector is hierarchical.
⚫
Use the @Injectable() class decorator ⚫ Use the @Injectable() class decorator instead of the @Inject parameter decorator. ⚫ The Angular Dependency Injection (DI) mechanism resolves a service's own dependencies based on the declared types of that service's constructor parameters.
https://www.ifourtechnolab.com/
Services contd. Find the standard way to write service ?
export class HeroArena { constructor( @Inject(HeroService) private heroService: HeroService, @Inject(Http) private http: Http) {} }
@Injectable() export class HeroArena { constructor( private heroService: HeroService, private http: Http) {} }
https://www.ifourtechnolab.com/
Services contd. /* Avoid */
/* Do like this */
export class HeroArena { constructor( @Inject(HeroService) private heroService: HeroService, @Inject(Http) private http: Http) {} }
@Injectable() export class HeroArena { constructor( private heroService: HeroService, private http: Http) {} }
https://www.ifourtechnolab.com/
Data Services ⚫
⚫ ⚫
Do refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations and logic. Use http interceptor for multiple http call.
https://www.ifourtechnolab.com/
Data Services Find the standard way to write service ?
getUsersList(): Observable<User[]> {
return this.http.get<UserList>(`/users`); }
getUsersList(): Observable<User[]> { return this.http.get<UserList>(`/users`) .pipe(map(res => res.result.users));
}
https://www.ifourtechnolab.com/
Data Services getUsersList(): Observable<User[]> {
return this.http.get<UserList>(`/users`); }
getUsersList(): Observable<User[]> { return this.http.get<UserList>(`/users`).pipe(map(res => res.result.users)); }
https://www.ifourtechnolab.com/
Lifecycle hooks
⚫ ⚫
⚫
⚫
Use Lifecycle hooks to tap into important events exposed by Angular. Do implement the lifecycle hook interfaces. Lifecycle interfaces prescribe typed method signatures. Use those signatures to flag spelling and syntax mistakes. Angular calls the lifecycle hook methods in the following sequence at specific moments like,
https://www.ifourtechnolab.com/
1. 2. 3. 4. 5. 6. 7. 8.
ngOnChanges() ngOnInit() ngDoCheck() ngAfterContentInit() ngAfterContentChecked() ngAfterViewInit() ngAfterViewChecked() ngOnDestroy()
Lifecycle hooks Find out which one is best way to implement life cycle method ?
@Component({ ... }) export class MyButtonComponent { onInit() { console.log('Init...'); } }
@Component({ ... }) export class MyButtonComponent implements OnInit { ngOnInit() { console.log('Init...'); } }
https://www.ifourtechnolab.com/
Lifecycle hooks /* Avoid */ @Component({ ... }) export class MyButtonComponent { onInit() { // misspelled console.log('Init...'); } }
@Component({ ... }) export class MyButtonComponent implements OnInit { ngOnInit() { console.log('Init...'); } }
https://www.ifourtechnolab.com/
Thank You
https://www.ifourtechnolab.com/