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