http://mbranko.github.io/webkurs
Ovo je deo web kursa
sudo npm install -g @angular/cli
ng new hello-world
cd hello-world
ng serve
# --> http://localhost:4200
sudo npm install -g @angular/cli
ng
ng help
za početak
ng new moja-aplikacija
moja-aplikacija
sa fajlovima
cd moja-aplikacija
ng serve
AppComponent
- korenska komponenta koja sadrži sve ostale
<!-- src/index.html -->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>HelloWorld</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<-- komponenta koja predstavlja našu aplikaciju -->
<app-root>Loading...</app-root>
</body>
</html>
$ ng generate component moja-komponenta
installing component
create src/app/moja-komponenta/moja-komponenta.component.css
create src/app/moja-komponenta/moja-komponenta.component.html
create src/app/moja-komponenta/moja-komponenta.component.spec.ts
create src/app/moja-komponenta/moja-komponenta.component.ts
$ ng generate component folder/komponenta
installing component
create src/app/folder/komponenta/komponenta.component.css
create src/app/folder/komponenta/komponenta.component.html
create src/app/folder/komponenta/komponenta.component.spec.ts
create src/app/folder/komponenta/komponenta.component.ts
@Component
dekoratorom// src/app/hello-world/hello-world.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
templateUrl
ili template
: prikaz komponente
<!-- src/app/hello-world/hello-world.component.html -->
<p>
hello-world works!
</p>
<!-- src/app/app.component.html -->
<h1>
{{title}}
<app-hello-world></app-hello-world>
</h1>
ng generate component user-item
<!-- src/app/app.component.html -->
<h1>
{{title}}
<app-hello-world></app-hello-world>
<app-user-item></app-user-item>
</h1>
// src/app/user-item/user-item.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
name: string; // dodali smo atribut
constructor() {
this.name = 'Žika'; // i inicijalizovali ga
}
ngOnInit() {
}
}
<!-- src/app/user-item/user-item.component.html -->
<p>
Hello {{ name }}
</p>
$ ng new wine-cellar
$ cd wine-cellar
core
$ ng g component core/header
$ ng g component core/navbar
wine
$ ng g component wine/wine-list
$ ng g component wine/edit-wine
WineListComponent
se deli naSearchFormComponent
TableComponent
PaginationComponent
wine
$ ng g component wine/search-form
$ ng g component wine/table
$ ng g component wine/pagination
app-
u selektoru promeniti u wc-
AppComponent
preraditi tako da HeaderComponent
NavbarComponent
WineListComponent
WineListComponent
preraditi tako da prikaže h1
naslov sa tekstom "WineListComponent" a ostatak podeliti na tri reda:AppComponent
preraditi tako da SearchFormComponent
TableComponent
PaginationComponent
bootstrap.css
i bootstrap.css.map
u src/assets/css
.angular-cli.json
tako da styles
sadrži i Bootstrap-ov CSS:
"styles": [
"styles.css",
"assets/css/bootstrap.css"
]
AppComponent
div
dodati klasu container-fluid
WineListComponent
container-fluid
(već je u korenskoj komponenti)wine.jpg
staviti u src/assets/images
AppModule
Vrednost izraza 2+2: {{2+2}}
private title: string = "primer";
private a: number = 5;
private b: number = 3;
{{title}}
{{a}} + {{b}} = {{a+b}}
Naslov sadrži {{title.length}} znakova.
{{ a > 0 ? 'positive' : 'negative' }}
subtract(x: number, y: number): number {
return x - y;
}
{{a}} - {{b}} = {{subtract(a, b)}}
ng new hello-world
cd hello world
ng g component user-list
<!-- src/app/app.component.html -->
<h1>
{{title}}
<app-hello-world></app-hello-world>
<app-user-list></app-user-list>
</h1>
// src/app/user-list/user-list.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
names: string[];
constructor() {
this.names = ['Žika', 'Pera', 'Mika', 'Laza'];
}
ngOnInit() {
}
}
<!-- src/app/user-item/user-list.component.html -->
<ul>
<li *ngFor="let name of names">Hello {{ name }}</li>
</ul>
ng g component user-item
<!-- src/app/user-list/user-list.component.html -->
<ul>
<li *ngFor="let name of names">
<app-user-item></app-user-item>
</li>
</ul>
ups...
// src/app/user-item/user-item.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
@Input() name: string; // dodali smo dekorator
constructor() {
this.name = 'Žika';
}
ngOnInit() {
}
}
<!-- src/app/user-list/user-list.component.html -->
<ul>
<li *ngFor="let name of names">
<app-user-item [name]="name"></app-user-item>
</li>
</ul>
// src/app/app.module.ts
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent, // moramo deklarisati
UserItemComponent, // komponente pre upotrebe
UserListComponent
], imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [], // injektovane stvari
bootstrap: [AppComponent] // glavna komponenta
})
export class AppModule { }
ng new angular-reddit
Iskopirajte fajlove iz reddit-files.zip u src
folder.
<form class="ui large form segment">
<h3 class="ui header">Dodaj link</h3>
<div class="field">
<label for="title">Naslov:</label>
<input name="title"/>
</div>
<div class="field">
<label for="link">Link:</label>
<input name="link"/>
</div>
</form>
export class AppComponent {
title = 'app works!';
addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean {
console.log(`Dodajemo članak sa naslovom: ${title.value} i linkom: ${link.value}`);
return false;
}
}
<form class="ui large form segment">
<h3 class="ui header">Dodaj Link</h3>
<div class="field">
<label for="title">Naslov:</label>
<input name="title" #newtitle /> <!-- dodeli tag lokalnoj promenljivoj -->
</div>
<div class="field">
<label for="link">Link:</label>
<input name="link" #newlink /> <!-- dodeli tag lokalnoj promenljivoj -->
</div>
<!-- dodato dugme, koristimo promenljive! -->
<button (click)="addArticle(newtitle, newlink)"
class="ui positive right floated button">
Pošalji link
</button>
</form>
<input name="title" #newtitle>
#newtitle
sintaksa zove se resolvenewtitle
će biti dostupna u templejtuHTMLInputElement
jer predstavlja čvor u DOM stablu
<button (click)="addArticle(newtitle, newlink)">
addArticle
je funkcija u komponentinewtitle
je definisana kao resolvenewlink
je definisana kao resolve
ng generate component article
ArticleComponent
u templejtuArticleComponent
u klasinewlink
je definisana kao resolve
<div class="four wide column center aligned votes">
<div class="ui statistic">
<div class="value">{{ votes }}</div>
<div class="label">Points</div>
</div>
</div>
<div class="twelve wide column">
<a class="ui large header" href="{{ link }}">{{ title }} </a>
<ul class="ui big horizontal list voters">
<li class="item">
<a href (click)="voteUp()">
<i class="arrow up icon"></i> upvote
</a>
</li>
<li class="item">
<a href (click)="voteDown()">
<i class="arrow down icon"></i> downvote
</a>
</li>
</ul>
</div>
import { Component, OnInit, HostBinding } from '@angular/core';
export class ArticleComponent implements OnInit {
// dodajemo atribut na host element, tj. element koji
// predstavlja ovu komponentu u templejtima
@HostBinding('attr.class') cssClass = 'row';
votes: number; // suma glasova
title: string; // naslov clanka
link: string; // link na clanak
constructor() {
this.title = 'Angular 2';
this.link = 'http://angular.io';
this.votes = 10;
}
voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
}
ngOnInit() { }
}
Ispod form
elementa treba dodati:
<div class="ui grid posts">
<app-article>
</app-article>
</div>
Angular CLI je dodao novu komponentu u deklaracije prilikom poziva ng generate component
. Bez toga tag app-article
ne bi bio prepoznat.
@NgModule({
declarations: [
AppComponent,
ArticleComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Klik na linkove "glas više" / "glas manje" izaziva page reload.
click
događaj svim roditeljskim komponentamafalse
da bismo sprečili propagiranje događaja prema roditeljima
voteUp() {
this.votes += 1;
return false;
}
voteDown() {
this.votes -= 1;
return false;
}
Article
ng generate class article/Article
export class Article {
title: string;
link: string;
votes: number;
constructor(title: string, link: string, votes?: number) {
this.title = title;
this.link = link;
this.votes = votes || 0;
}
voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
}
}
import { Component, OnInit, HostBinding } from '@angular/core';
import { Article } from './article';
export class ArticleComponent implements OnInit {
@HostBinding('attr.class') cssClass = 'row';
article: Article;
constructor() {
this.article = new Article(
'Angular 2',
'http://angular.io',
10);
}
voteUp() {
this.article.voteUp();
return false;
}
voteDown() {
this.article.voteDown();
return false;
}
...
}
<div class="four wide column center aligned votes">
<div class="ui statistic">
<div class="value">{{ article.votes }}</div>
<div class="label">Poena</div>
</div>
</div>
<div class="twelve wide column">
<a class="ui large header" href="{{ article.link }}">{{ article.title }} </a>
<ul class="ui big horizontal list voters">
<li class="item">
<a href (click)="voteUp()">
<i class="arrow up icon"></i> glas više
</a>
</li>
<li class="item">
<a href (click)="voteDown()">
<i class="arrow down icon"></i> glas manje
</a>
</li>
</ul>
</div>
Article[]
) možemo čuvati u AppComponent
ArticleComponent
tako da prima Article
kao ulazni parametarAppComponent
tako da prima više puta prikaže ArticleComponent
import { Component } from '@angular/core';
import { Article } from './article/article';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
articles: Article[];
constructor() {
this.articles = [
new Article('Angular 2', 'http://angular.io', 3),
new Article('Fullstack', 'http://fullstack.io', 2),
new Article('TypeScript', 'http://typescriptlang.org', 1),
];
}
}
import { Component, OnInit, HostBinding, Input } from '@angular/core';
import { Article } from './article';
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
@HostBinding('attr.class') cssClass = 'row';
@Input() article: Article; // dodali smo dekorator
constructor() {
// article se popunjava @Input() injekcijom
}
...
}
<div class="ui grid posts">
<app-article *ngFor="let article of articles" [article]="article">
</app-article>
</div>
addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean {
console.log(`Dodajem članak: ${title.value} i link: ${link.value}`);
this.articles.push(new Article(title.value, link.value, 0));
title.value = '';
link.value = '';
return false;
}
domain(): string {
try {
// 1. skini http://
const domainAndPath: string = this.link.split('//')[1];
// 2. skini sve iza prve /
return domainAndPath.split('/')[0];
} catch (err) {
return null;
}
}
<a class="ui large header" href="{{ article.link }}">{{ article.title }}</a>
<!-- dodato vvv-->
<div class="meta">({{ article.domain() }})</div>
<!-- dodato ^^^-->
<ul class="ui big horizontal list voters">
...
</ul>
sort
za nizove
export class AppComponent {
...
sortedArticles(): Article[] {
return this.articles.sort((a: Article, b: Article) => b.votes - a.votes);
}
}
<div class="ui grid posts">
<app-article *ngFor="let article of sortedArticles()" [article]="article">
</app-article>
</div>
[uglaste]
zagrade(obične)
zagrade
<div class="inventory-app">
<products-list
[productList]="products"
(onProductSelected)="productWasSelected($event)">
</products-list>
</div>
export class ProductsListComponent {
@Input() productList: Product[]; // ulaz
@Output() onProductSelected: EventEmitter<Product>; // izlaz
clicked(product: Product): void {
this.currentProduct = product;
this.onProductSelected.emit(product); // emitujemo na izlaz
}
isSelected(product: Product): boolean {
if (!product || !this.currentProduct) {
return false;
}
return product.sku === this.currentProduct.sku;
}
...
}
let ee = new EventEmitter();
ee.subscribe((name:string) => console.log(`Hello ${name}`));
ee.emit("Žika");
// -> "Hello Žika"
EventEmitter
za izlaz, Angular će ga automatski pretplatiti
<div class="ui items">
<product-row
<!-- iteracija kroz niz -->
*ngFor="let myProduct of productList"
<!-- prenos ulaznog parametra -->
[product]="myProduct"
<!-- pretplata na događaj (klik mišem) -->
(click)='clicked(myProduct)'
<!-- uključi/isključi CSS klasu selected zavisno od uslova -->
[class.selected]="isSelected(myProduct)">
</product-row>
</div>
<div *ngIf="false"></div>
<div *ngIf="a > b"></div>
<div *ngIf="str == 'yes'"></div>
<div *ngIf="myFunc()"></div>
<div class="container" [ngSwitch]="myVar">
<div *ngSwitchCase="'A'">Var is A</div>
<div *ngSwitchCase="'B'">Var is B</div>
<div *ngSwitchCase="'C'">Var is C</div>
<div *ngSwitchDefault>Var is something else</div>
</div>
<div [style.background-color]="'yellow'"> ...
</div>
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}"> ...
</div>
.bordered {
border: 1px dashed black; background-color: #eee;
}
<div [ngClass]="{bordered: false}">This is never bordered</div>
<div [ngClass]="{bordered: true}">This is always bordered</div>
HeaderComponent
i NavbarComponent
WineListComponent
i EditWineComponent
WineListComponent
EditWineComponent
index.html
dodati <base href="/">
WineListComponent
EditWineComponent
const routes: Routes = [
{path: 'wines', component: WineListComponent},
{path: 'wines/add', component: EditWineComponent}
]
RouterModule
<router-outlet>
Routes
app.module.ts
:routerLink
import { Router } from '@angular/router';
@Component({
selector: 'app-edit-wine',
templateUrl: './edit-wine.component.html',
styleUrls: ['./edit-wine.component.css']
})
export class EditWineComponent implements OnInit {
constructor(private router: Router) { ... }
}
...
this.router.navigate(['wines/'])
AppRoutingModule
app-routing.module.ts
$ ng g module app-routing
AppRoutingModule
importovati RouterModule
AppRoutingModule
-a
import { RouterModule } from '@angular/router'; // ovde
@NgModule({
imports: [
CommonModule,
RouterModule // ovde
],
exports: [
RouterModule // ovde
],
declarations: []
})
export class AppRoutingModule { }
src/app/app-routing/routes.ts
RouterModule
AppRoutingModule
routes
konstantuRouterModule
-u kao parametar forRoot
funkcije
import { RouterModule } from '@angular/router';
import { routes } from './routes'; // ovde
@NgModule({
imports: [
CommonModule,
RouterModule.forRoot(routes) // ovde
],
exports: [
RouterModule
],
declarations: []
})
export class AppRoutingModule { }
<form>...</form>
<input>
<textarea>
<button>
<select>
<element [(ngModel)]="objekat"/>
name
ngModel
direktive mora se importovati FormsModule
AppModule
dodamo:
import { FormsModule } from '@angular/forms';
...
@NgModule({
declarations: [
AppComponent,
RegisterUserComponent
],
imports: [
BrowserModule,
FormsModule, // ovde
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
form
element dodati direktivu ngSubmit
novalidate
da bi se isključila HTML5 validacija - da ne ulazi u koliziju sa Angularom!
<form novalidate>
<input #varijabla="ngModel">
touched
- ako je element imao fokusvalid
- ako je sadržaj validanerrors
- null ako nema grešaka, inače objekat koji opisuje greškeform
elementu eksportujemo ngForm
direktivu u lokalnu promenljivu
<form #varijabla="ngForm">
pristine
, errors
... - polja koja se odnose na celu formungModel
i name
name
)FormsModule
i ReactiveFormsModule
AppModule
dodamo:
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // ovde
...
@NgModule({
declarations: [
AppComponent,
RegisterUserComponent
],
imports: [
BrowserModule,
FormsModule, // ovde
ReactiveFormsModule // ovde
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
RegisterUserComponent
dodajemo polje registerUserForm
koje predstavlja model forme
import { FormsGroup, FormBuilder } from '@angular/forms'; // ovde
...
@Component({
selector: 'app-register-user',
templateUrl: './register-user.component.html',
styleUrls: ['./register-user.component.css']
})
export class RegisterUserComponent implements OnInit {
registerUserForm: FormGroup; // ovde
...
}
FormBuilder
<form>
element za model formeregisterUserForm.value
)Employee
_id
, name
, surname
, jmbg
, email
EditEmployeeComponent
dodajemo dva atributaemployee
- model podatakaemployeeForm
- model formesetValue
- dodeli vrednosti svim kontrolama forme odjednompatchValue
- ažuriraj podskup kontrola formeValidators
iz @angular/forms
FormBuildera
forma.controls.ime_kontrole.errors?.ime_greske
polje | opis |
value |
vrednost unesena u kontrolu |
valid |
true ako je kontrola validna |
status |
VALID , INVALID , PENDING , DISABLED |
pristine dirty |
pristine je true ako korisnik nije menjao vrednost kontrole, dirty obrnuto |
touched untouched |
untouched je true ako korisnik nije nijednom fokusirao kontrolu, touched obrnuto |
WineListComponent
EditWineComponent
da bismo dodali novo vino u listuWineService
getAll()
- vraća celu listu vinaadd(newWine: Wine)
- dodaje novo vino u listuWineListComponent
:WineEditComponent
:
ng g service wine/services/wine
WineService
u fajlu wine/services/wine.service.ts
WineService
čuvamo listu vina i dodajemo metode za manipulaciju listomWineListComponent
:WineService
getAll
za dobavljanje liste vinaproviders
deklaraciji modulangOnInit()
metodu definisanu u OnInit
interfejsu nakon što prvi put prikaže templejt komponenteEditWineComponent
EditWineComponent
-i koje vino je izabrano u WineListComponent
-iRouter
servis može da prenosi parametre između komponenti<router-outlet>
je putem URL-a/wines/:id
:id
će biti zamenjen konkretnom vrednošću/wines/1
gde 1
predstavlja route parametarEditWineComponent
će preuzeti vrednost id
parametra direktno iz putanje i znaće da treba da prikaže vino sa tom vrednošću polja _id
src/app/app-routing/routes.ts
)routerLink
direktive:
[routerLink]="['/wines/', wine._id]"
navigate
metode:
this.router.navigate(['wines/', wine._id]);
ActivatedRoute
definisanog u @angular/router
ActivatedRoute
iz @angular/router
this.route.snapshot.paramMap.get('id');
npm install
npm start
count
: broj vinaresults
: niz vinaid
-jemget
, post
, put
, delete
Observable
count
- ukupan broj picaresults
- niz picaPizza
PizzaSearchResult
count
i pizza
(odgovara atributu results
)PizzaSearchResult
:pizzas
je niz Pizza
objekataobj.results
je niz JavaScript objekatamap
koju ćemo upotrebiti za konverzijuHttpClientModule
PizzaService
ng g service pizza/services/pizza
providers
našeg modula AppModule
map
funkciji kod JavaScript nizaObservable<Response>
je niz vrednosti tipa Response
map
će konvertovati Observable<Response>
u Observable<PizzaSearchResult>
PizzaListComponent
PizzaService.getAll()
ngOnInit
EditPizzaComponent
koja je napunjenja trenutno izabranom picomHttpClient
-a koje treba iskoristiti su.post(url, newPizza)
.put(url, editedPizza)
.delete(url)
sort=property_name
: ime polja po kome se sortirasortDirection=desc
: desc - opadajuće; sve drugo - rastućehttp://localhost:3000/api/pizzas?sort=grade
http://localhost:3000/api/pizzas?sort=grade&sortDirection=desc
filter[property_name]=value
property_name
imaju value
http://localhost:3000/api/pizzas?filter[name]=mar
page=page_no
- koju stranicu tabele prikazujemopageSize=max_entities
- max broj redova u tabelihttp://localhost:3000/api/wines?page=2&pageSize=5
http://localhost:3000/api/pizzas?filter[name]=mar&sort=name&page=2&pageSize=5
count
- ukupan broj pica koje odgovaraju kriterijumuresults
- pice koje treba prikazati na stranici