Loiane Groner
Java + JavaScript/HTML5 developer • Web/Angular GDE • Microsoft MVP • author @PacktPub More decks available at: https://www.slideshare.net/loianeg
Loiane Groner
Java, JavaScript + HTML5, Sencha, Cordova/Ionic, Angular, RxJS + all things reactive
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"loiane.angular-extension-pack",
"loiane.ts-extension-pack"
]
}
extensions.json
TSLint, snippets, tests, debug, TS utilities, file formatting
{
"editor.wordWrap": "off",
"editor.minimap.enabled": false,
"editor.codeActionsOnSave": { "source.fixAll.tslint": true },
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 3000,
"prettier.tabWidth": 2,
"prettier.singleQuote": true,
"prettier.useTabs": false,
"html.format.wrapAttributes": "auto",
"html.format.wrapLineLength": 0,
"html.format.unformatted": "a, abbr, acronym, b, bdo, big, br, ...",
"workbench.editor.enablePreview": false,
"auto-rename-tag.activationOnLanguage": ["html", "xml"]
}
settings.json
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome with ng serve",
"url": "http://localhost:4200",
"webRoot": "${workspaceRoot}"
},
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome with ng test",
"url": "http://localhost:9876/debug.html",
"webRoot": "${workspaceRoot}"
},
{
"type": "chrome",
"request": "attach",
"name": "Attach to Chrome",
"port": 9222,
"webRoot": "${workspaceRoot}"
}
]
}
launch.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "npm",
"script": "build",
"presentation": {
"reveal": "always"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$tsc"]
},
{
"label": "test",
"type": "npm",
"script": "test",
"presentation": {
"reveal": "always"
},
"group": {
"kind": "test",
"isDefault": true
}
}
]
}
tasks.json
// Place your key bindings in this file to overwrite the defaults
[
// Tests
{
"key": "ctrl+shift+t",
"command": "workbench.action.tasks.test"
},
// Lint
{
"key": "ctrl+shift+l",
"command": "workbench.action.tasks.runTask",
"args": "lint"
},
// Serve app
{
"key": "ctrl+shift+s",
"command": "workbench.action.tasks.runTask",
"args": "serve"
}
]
keybindings-win.json
Back-end code
Angular prototype
Back-end integration
Prod
build
"scripts": {
"ng": "ng",
"dev": "ng serve --aot --configuration=dev",
"start": "ng serve --proxy-config proxy.conf.js",
"build": "ng build --prod --aot --build-optimizer -op ../webapps/app"
},
package.json
Local development
Development with back-end
Production
export const environment = {
production: false,
baseUrl: '/'
};
environment.dev.ts
export const environment = {
production: false,
baseUrl: '/api/'
};
environment.ts
export const environment = {
production: true,
baseUrl: '../'
};
environment.prod.ts
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
...
},
"dev": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
]
}
...
}
angular.json
import { environment } from '@env/environment';
export class API {
static readonly BASE_URL = environment.baseUrl;
// LISTA DE CLIENTES
static readonly CLIENTS = `${API.BASE_URL}clients`;
}
// para usar essa API:
// this.http.get(API.CLIENTES)
API.ts
{
"author" : [ {
"id": 1,
"name" : "Loiane"
},
{
"id": 2,
"name" : "abcdef"
}
],
"client" : []
}
myDb.json
npm install -g json-server
json-server --watch myDb.json
Even with 'provideIn' services, Core modules are great to organize your code!
const routes: Routes = [
{
path: '',
redirectTo: 'module-1',
pathMatch: 'full'
},
{
path: 'module-1',
component: Module1Component
},
{
path: 'module-2',
loadChildren: './module-2/module-2.module#Module2Module',
data: { preload: true } // pre-load - background
},
{
path: 'module-3',
loadChildren: './feature-3/module-3.module#Module3Module'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
preloadingStrategy: AppCustomPreloader
})], // our custom logic
exports: [RouterModule],
providers: [AppCustomPreloader]
})
export class AppRoutingModule { }
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
export class AppCustomPreloader implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.preload ? load() : of(null);
}
}
size matters!
<mat-list>
<app-task-item *ngFor="let task of tasks$ | async"
[task]="task"
(remove)="onRemove(task)"
(edit)="onUpdate(task, $event)">
</app-task-item>
</mat-list>
tasks-list.component.html
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TasksListComponent {
@Input('tasks') tasks$: Observable<Task[]>;
@Output() remove: EventEmitter<any> = new EventEmitter(false);
@Output() edit: EventEmitter<any> = new EventEmitter(false);
onRemove(task) {
this.remove.emit(task);
}
onUpdate(task, changes) {
this.edit.emit({task: task, updates: changes});
}
}
tasks-list.component.ts
<mat-card>
<app-task-form (createTask)="onCreateTask($event)"></app-task-form>
<mat-spinner *ngIf="isLoading$ | async; else taskList"></mat-spinner>
<ng-template #taskList>
<app-tasks-list
[tasks]="tasks$"
(remove)="onRemoveTask($event)"
(edit)="onUpdateTask($event)">
</app-tasks-list>
</ng-template>
<div class="error-msg" *ngIf="error$ | async as error">
<p>{{ error }}</p>
</div>
</mat-card>
tasks.component.html
export class TasksComponent implements OnInit {
tasks$: Observable<Task[]>;
isLoading$: Observable<boolean>;
error$: Observable<string>;
constructor(private taskStoreService: TaskStoreService) {}
ngOnInit() {
this.taskStoreService.dispatchLoadAction();
this.tasks$ = this.taskStoreService.getTasks();
this.isLoading$ = this.taskStoreService.getIsLoading();
this.error$ = this.taskStoreService.getError();
}
onCreateTask(title) {
this.taskStoreService.dispatchCreateAction(new Task(title));
}
onRemoveTask(task) {
this.taskStoreService.dispatchRemoveAction(task);
}
onUpdateTask(event) {
this.taskStoreService.dispatchUpdateAction(event.updates);
}
}
tasks.component.ts
<form (ngSubmit)="onSubmit()" novalidate>
<mat-input-container class="example-full-width">
<input matInput placeholder="What needs to be done?" type="text"
name="title"
[(ngModel)]="title"
(keyup.escape)="clear()"
autocomplete="off"
autofocus>
</mat-input-container>
</form>
component1.html
<mat-list-item class="list-item">
<mat-checkbox color="primary" type="checkbox" [name]="task.id" [(ngModel)]="task.completed" (change)="onEdit()">
<span [class.task-completed]="task.completed">{{task.title}}</span>
</mat-checkbox>
<span class="fill-remaining-space"></span>
<span>
<button mat-mini-fab (click)="onRemove()">
<mat-icon>delete_forever</mat-icon>
</button>
<button mat-mini-fab (click)="openEditDialog()" [disabled]="task.completed" color="primary">
<mat-icon>mode_edit</mat-icon>
</button>
</span>
</mat-list-item>
component2.html
<mat-list>
<app-task-item *ngFor="let task of tasks$ | async"
[task]="task"
(remove)="onRemove(task)"
(edit)="onUpdate(task, $event)">
</app-task-item>
</mat-list>
component3.html
<mat-card>
<form (ngSubmit)="onSubmit()" novalidate>
<mat-input-container class="example-full-width">
<input matInput placeholder="What needs to be done?" type="text"
name="title"
[(ngModel)]="title"
(keyup.escape)="clear()"
autocomplete="off"
autofocus>
</mat-input-container>
</form>
<mat-spinner *ngIf="isLoading$ | async; else taskList" style="margin:0 auto;"></mat-spinner>
<ng-template #taskList>
<mat-list>
<mat-list-item class="list-item" *ngFor="let task of tasks$ | async">
<mat-checkbox color="primary" type="checkbox" [name]="task.id" [(ngModel)]="task.completed" (change)="onEdit()">
<span [class.task-completed]="task.completed">{{task.title}}</span>
</mat-checkbox>
<span class="fill-remaining-space"></span>
<span>
<button mat-mini-fab (click)="onRemove()">
<mat-icon>delete_forever</mat-icon>
</button>
<button mat-mini-fab (click)="openEditDialog()" [disabled]="task.completed" color="primary">
<mat-icon>mode_edit</mat-icon>
</button>
</span>
</mat-list-item>
</mat-list>
</ng-template>
<div class="error-msg" *ngIf="error$ | async as error">
<p>{{ error }}</p>
</div>
</mat-card>
component1.html
component3.html
@Injectable()
export class ApiService {
constructor(public http: HttpClient) { }
getRequest<T>(url: string, params?: any) {
return this.http.get<ServerResponse<T>>(url, {
params: this.getQueryParams(params)
})
.take(1); // as simple Ajax call, we only need 1 value
}
postRequest(url: string, body: any) {
// POST
}
private getQueryParams(params: any) {
// logic to create the query params for http call
}
downloadFile(url: string, params?: any) {
// logic for file download
}
uploadFile(file: AppFile | AppFile[], body?: any, params?: any) {
// logic for file upload
}
}
api.service.ts
@Injectable()
export class TasksService {
constructor(public http: ApiService) { }
getAllTasksWithPaging(start = 0, limit = 100) {
return this.http
.getRequest<Task[]>(API.READ_TASKS, {start: start, limit: limit});
}
getById(id: number) {
return this.http.getRequest<Task>(`${API.READ_TASKS}/${id}`);
}
}
tasks.service.ts
export interface ServerResponse<T> {
data: T[];
total: number;
success?: boolean;
errorMsg?: string;
}
Response
export abstract class BaseFormComponent implements IFormCanDeactivate {
protected formSubmitAttempt: boolean;
protected validateDirty = true;
form: FormGroup;
constructor() {
this.formSubmitAttempt = false;
}
isFieldInvalid(field: string) {
// some logic here
}
onSubmit() {
this.formSubmitAttempt = true;
// generic logic for all forms
}
onReset() { }
canDeactivate(): Observable<boolean> {
return this.modalService.showConfirm(
'DoYouWantToLeavePage',
'DoYouWantToLeavePageMsg'
);
}
onCancel() {
this.location.back();
}
}
@Injectable()
export class CRUDService<T> {
constructor(public http: HttpClient, private API_URL) {}
load() {
return this.http.get<T[]>(this.API_URL);
}
create(record: T) {
return this.http.post<Task>(this.API_URL, record);
}
update(record: T) {
return this.http.put<Task>(`${this.API_URL}/${record.id}`, record);
}
remove(id: string) {
return this.http.delete<T>(`${this.API_URL}/${id}`);
}
}
@Injectable()
export class TasksService extends CRUDService<Task> {
constructor(public http: HttpClient) {
super(http, API.TASKS_URL)
}
}
import { AuthService } from '../../../core/security/auth/auth.service';
import { environment } from '../../../../environments/environment';
import { AuthService } from '@my-project/core';
import { environment } from '@env/environment';
import { AuthService } from '../../../core/security/auth/auth.service';
import { environment } from '../../../../environments/environment';
import { AuthService } from '@my-project/core';
import { environment } from '@env/environment';
{
"extends": "../tsconfig.json",
"compilerOptions": {
...
"paths": {
"@my-project/*": ["src/app/*"],
"@env/*": ["src/environments/*"]
}
}
}
tsconfig.app.json
"scripts": {
"ng": "ng",
"build": "ng build --prod --aot --build-optimizer -op ../webapps/app"
},
package.json
ng build --prod --source-map --vendor-source-map
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
...
}
...
}
https://docs.oracle.com/en/cloud/iaas/container-cloud/index.html
ng generate library my-lib --prefix lib
ng generate component my-component --project=my-lib
ng build my-lib
import { MyLibModule } from 'my-lib';
@NgModule({
imports: [
BrowserModule,
MyLibModule
],
...
})
export class AppModule { }
app.module.ts
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
</div>
<lib-my-component></lib-my-component>
app.component.html
"paths": {
"my-lib": [
"dist/my-lib"
],
"my-lib/*": [
"dist/my-lib/*"
]
}
tsconfig
By Loiane Groner
Are you working on Angular projects and still have some questions about the structure of your project? How to organize the modules and components of an application to facilitate data flow and project maintenance? How to make the whole team follow the same standards and improve productivity? Is there any easier way to work with prototyping even by integrating the front end with the server? How do I decrease the impact of migrations from one version to another? In this talk I share some of the experiences and decisions about code-sharing improvements and components I've learned by developing angular projects that are in production.
Java + JavaScript/HTML5 developer • Web/Angular GDE • Microsoft MVP • author @PacktPub More decks available at: https://www.slideshare.net/loianeg