1. რა არის Angular-ი ?
Angular არის, კორპორაცია Google-ს მიერ შექმინლი, Typescript-ზე დაფუძნებული ვებ-ფრეიმვორკი ღია წყაროთი, რომელიც გამოიყენება ერთგვერდიანი აპლიკაციების (SPA - Single Page Aplication) შესაქმნელად.

ერთგვერდიანი აპლიკაციის უკან მოიაზრება აპლიკაცია, რომელიც ტვირთავს მხოლოდ ერთ HTML ფაილს და მომხმარებლის ქმედებიდან გამომდინარე დინამიურად ცვლის ამ გვერდის შიგთავსს, თანაც ისე, რომ არ ხდება გვერდის ხელახალი ჩატვირთვა ანუ ე.წ 'დარეფრეშება' (Angular-ით აწყობილი ერთგვერდიანი ვებ-გვერდების ნიმუშები შეგიძლიათ იხილოთ აქ).

ისტორია

Angular-ის პირველ ვერსიას ეწოდებოდა AngularJS. AngularJS-ის ხელახალი გადაწერის შედეგად შეიქმნა Angular 2, რომლის ოფიციალური წარდგენაც 2016 წლის სექტემბერში მოხდა.

Angular-ის განვითარების პროცესი მიმდინარეობს ე.წ MonoRepo (MonoRepository ანუ ერთი საცავი) სტრატეგიით.

***

ვერსიათა კონტროლის სისტემებში MonoRepo წარმოადგენს პროგრამული უზრუნველყოფის წარმოების სტრატეგიას, რომლის მიხედვითაც სხვადასხვა პროექტების კოდები ინახება ერთ საერთო საცავში ('რეპოზიტორში').

***

მოხდა ისე, რომ Angular-ის, როგორც საერთო პროექტის ერთ-ერთი კომპონენტი, კერძოდ მარშრუტიზატორი (@angular/router), ვერსიონირების თვალსაზრისით, სხვა კომპონენტებთან შედარებით აღმოჩნდა ერთი საფეხურით წინ :
  • @angular/core - 2.*
  • @angular/compiler - 2.*
  • @angular/compiler-cli - 2.*
  • @angular/http - 2.*
  • @angular/router - 3.*
Angular-ის შემქმნელებმა გადაწყვიტეს, რომ ვერსიათა ეს აცდენა დაბნეულობას გამოიწვევდა ვებ-მწარმოებლებს შორის და Angular-ის ყველა კომპონენტი გააერთიანეს მე-4-ე ვერსიის ქვეშ, რომლის წარდგენაც 2016 წლის დეკემბერში მოხდა. ასე და ამგვარად მე-3-ე ვერსია გამოტოვებულ იქნა :))

ახალი ვერსია გამოდის ყოველ ექვს თვეში ერთხელ . Angular-ის მე-8-ე ვერსია, რომლის შესწავლასაც ამ კურსში ვაპირებთ, 2019 წლის მაისში გამოვიდა.

2. პროექტის გამართვა, ჩვენი პირველი აპლიკაცია

რა არის Node.js ?

Node ანუ Node.js — არის V8 (ავტ. Google) ძრავზე დაფუძნებული პროგრამული პლატფორმა, რომელიც JavaScript-ს, როგორც სპეციალიზირებულ ენას, გარდაქმნის პროგრამირების სრულფასოვან და საერთო დანიშნულების ენად. ამ პლატფორმაზე დაფუძნებით ჯს შეგვიძლია გამოვიყენოთ სერვერული მხარის სამუშაოებში.

Node.js ინსტალაცია

Node.js ის გადმოწერა შესაძლებელია ოფიციალური ვებ-გვერდიდან. ინსტალაცია მიმდინარეობს სტანდარტულად, ყოველგვარი სირთულეების გარეშე. იმისათვის რათა დავრწმუნდეთ, რომ ინსტალაციამ წარმატებით ჩაიარა, ბრძანებათა ველიდან (CMD) გავუშვათ შემდეგი ბრძანება : node –v

Angular CLI

Angular CLI (command-line interface) არის ბრძანებათა ველების ინტერფეისი, რომელიც გამოიყენება Angular პროექტების ინიციალიზაციისათვის და ასევე სხვადასხვა კომპონენტების შესაქმნელად მუშაობის პროცესში.

Angular CLI ინსტალაცია Windows-ში

გავხსნათ ბრძანებათა ველების კონსოლი როგორც ადმინისტრატორმა და გავუშვათ შემდეგი ბრძანება : npm install -g @angular/cli
  • npm - Node.js Package Manager ანუ Node.js პაკეტების მენეჯერი. პაკეტის მიღმა იგულისხმება იმ ფაილების ნაკრები, რომლებიც გვჭირდება კონკრეტული მოდულის შესაქმნელად, ხოლო თავად მოდული არის ჯს-ის ბიბლიოთეკა, რომლის გამოყენებაც გვსურს პროექტში.
  • -g - CLI სისტემაში დაინსტალირდება, როგორც გლობალური პაკეტი, რაც იმას ნიშნავს, რომ CLI-სთან წვდომა შეგვიძლია ჩვენი ნებისმიერი პროექტიდან.

ახალი პროექტის ინსტალაცია

გადავინაცვლოთ იმ საქაღალდეში სადაც გვსურს პროექტის დაინსტალირება და გავუშვათ შემდეგი ბრძანება : ng new my-first-app ბრძანების გაშვების შემდეგ სისტემა მოგვცემს რამოდენიმე შეკითხვას, პირველი იქნება - 'გვსურს თუ არა სისტემაში დავამატოთ Angular მარშრუტიზატორი ?' , ამ ეტაპზე ჩვვენი პასუხია - 'არა', ამისათვის ან პიდაპირ ენტერს დავაწვეთ, ან ავკრიფოთ ლათინური ასო 'N' და შემდეგ დავაწვეთ ენტერს (პირდაპირ ენტერზე დაწოლაც გულისხმობს არას).

შემდეგი კითხვა იქნება იმის შესახებ თუ სტილიზაციის რომელი ფორმატის გამოყენება გვსურს, ნაგულისხმევად არჩეული იქნება CSS, თუ ეს ჩვენთვის ხელსაყრელია დავაწევეთ ენტერს, თუ არა და ავირჩიოთ სასურველი ფორმატი და შემდეგ დავაწვეთ ენტერს.

ამის შემდეგ დაიწყება პროექტის ინსტალაცია. ახალდაინსტალირებულ პროექტს ექნება შემდეგი სახე : ამის შემდეგ გადავინაცვლოთ პროექტის საქაღალდეში - my-first-app და გავუშვათ შემდეგი ბრძანება :
ng serve
ეს ბრძანება მოახდენს Angular CLI-ს მეშვეობით დაინსტალირებული პროექტის მომართვას, ამოქოქვას :)) ახლა შევიდეთ შემდეგ მისამართზე
http://localhost:4200/
ვიხილავთ ამდაგვარ სურათს :



გილოცავთ ! ჩვენ პირველი სერიოზული ნაბიჯი გადავდგით Angular-ის შესაწავლის საქმეში ! :)
3. პროექტის სტრუქტურა

აპლიკაციის კონფიგურაციული ფაილები და საქაღალდეები

დასახელება დანიშნულება
editorconfig აქ აღწერილია კონფიგურაციული პარამეტრები ტექსტური ედიტორებისათვის. დაწვრილებითი ინფორმაცია შეგიძლიათ იხილოთ აქ.
.gitignore აქ განსაზღრულია იმ ფაილთა დასახელებები რომლებიც არ იქნება გათვალისწინებული ვერსიათა კონტროლის სისტემა Git ის მიერ.
README.md პროექტის დოკუმენტაცია
angular.json CLI-ის მეშვეობით დაინსტალირებული პროექტის კონფიგურაციული პარამეტრები.
package.json აქ აღწერილია npm-ის იმ პაკეტების დამოკიდებულებები, რომლებიც ხელმისაწვდომია სამუშაო გარემოში.
package-lock.json აქ ინახება ინფორმაცია node_modules საქაღალდეში დაინსტალირებული პაკეტების ვერსიების შესახებ.
src ამ საქაღალდეში ინახება პროექტის ძირითადი ფაილები, ფაქტიურად აქაა მოქცეული პროექტის ბირთვი.
node_modules/ ამ საქაღალდეში ინსტალირდება npm პაკეტები.
tsconfig.json აქ აღწერილია TypeScript-თან სამუშაო პარამეტრების ნაგულისხმები მნიშვნელობები.
tslint.json TSLint-თან სამუშაო კონფიგურაციული პარამეტრები.
browserslist ამ ფაილში ხდება სამიზნე ბრაუზერებისა და Node.js-ის სხვადასხვა ვერსიების ერთდროული გამოყენების კონფიგურირება.
karma.conf.js აპლიკაციის Karma კონფიგურაციული პარამეტრები. Karma-ს საშუალებით ხდება კონკრეტული ფრაგმენტების გაშვება 'unit ტესტირებისას'.
tsconfig.app.json TypeScript-ის კონფიგურაცია კონკრეტული პროექტისათვის.
tsconfig.spec.json TypeScript-ის კონფიგურაცია პროექტის ტესტირებისათვის.
tslint.json TSLint კონფიგურაცია.

src/ საქაღალდე

ამ საქაღალდეში მოქცეულია ჩვენი ძირითადი სამუშაო გარემო, პროექტის ჩონჩხი, კარკასი.
ფაილი/საქაღალდე დანიშნულება
app ამ საქაღალდეში არსებულ ფაილებშია აღწერილი ჩვენი პროექტის ძირითადი ლოგიკა.
assets/ აპლიკაციის დამხმარე ფაილები, ისეთები როგორებიცაა მაგალითად ფოტოები.
environments/ შეიცავს კონკრეტული სამუშაო გარემოს შესაბამის კონფიგურაციულ პარამერტრებს.
favicon.ico ხატულა, რომელიც გამოჩნდება თუ აპლიკაციას ჩავნიშნვის (bookmark) შემდეგ ბრაუზერის შესაბამის ველში.
index.html ძირითადი ფაილი, რომელიც იტვირტება აპლიკაციის გახსნისას.
main.ts აპლიკაციის შესავალი წერტილი. აქ ხდება აპლიკაციის კომპილირება JIT კომპილატორის მეშვეობით. აქვე ხდება აპლიკაციის ძირითადი მოდულის (AppModule) წინასწარჩატვირთვა.
polyfills.ts

პოლიფილი

ვებ-პროგრამირებაში პოლიფილი არის კოდის ფრაგმენტი, რომელიც ახდენს კონკრეტული ფუნქციონალის ინტეგრაციას ბრაუზერში, მაშინ, როდესაც ბრაუზერი ნაგულისხმევად არ უჭერს მხარს ამ ფუნქციონალს.

ამ საქაღალდეში აღწერილია ის პოლიფილები, რომლებიც ესაჭიროება Angular-ს და რომლებსაც იგი ტვირთავს აპლიკაციის გაშვებამდე.

test.ts ძირითადი შესავალი წერტილი ე.წ 'unit ტესტირებისათვის'

unit ტესტირება

ვებ-პროგრამირებაში unit ტესტირება არის პროგრამული უზრუნველყოფის გატესთვის მეთოდი, რომლის გამოყენების შემთხვევაშიც შესაძლებელია გაიტესტოს კოდის კონკრეტული ფრაგმენტები, მოდულები, კომპონენტები და ა.შ

src/app/ საქაღალდე

src/app/ ფაილი დანიშნულება
app/app.component.ts ამ ფაილში აღწერილია აპლიკაციის ძირითადი კომპონენტის - AppComponent-ის ლოგიკა.
app/app.component.html აქ აღიწერება HTML შაბლონი რომელიც დაკავშირებულია ძირითად კომპონენტთან - AppComponent.
app/app.component.css CSS სტილები AppComponent-ისათვის.
app/app.component.spec.ts 'unit ტესტირების' ფაილი AppComponent-ისათვის.
app/app.module.ts აქ აღიწერება ძირითადი მოდული სახელად AppModule, აქ ხდება საჭირო კომპონენტების დეკლარირება, საწყის ეტაპზე დეკლარირებუკლია მხოლოდ AppComponent.

e2e საქაღალდე

e2e საქაღალდეში განთავსებულია გამჭოლ ტესტირებასთან (ინგ: 'End to End testing') დაკავშირებული ფაილები.
4. მცირე ცვლილებები საწყის პროექტში
Angular-ის ინსტალაციის შემდეგ თუ შევამოწმებთ src/app საქაღალდეს, ვნახავთ იმ ფაილებს, რომელთა მეშვეობითაც გენერირდება წარმოდგენა, რომელსაც ვხედავთ http://localhost:4200/ მისამართზე შესვლისას. ბუნებრივია ჩვენ გვჭირდება ჩვენი საკუთარი შაბლონი, შევქმნათ იგი და ასევე ჩვენი პროექტი დავაკავშიროთ Bootstrap ფრეიმვორკთან.

პირველ რიგში გავასუფთავოთ src/app/app.component.html ფაილი.

Bootstrap 3 - ის ინსტალაციისათვის კი გავუშვათ შემდეგი ბრძანება :

npm install --save bootstrap@3 ინსტალაციის შემდეგ მოგვიწევს მცირე ცვლილებები, /angular.json ფაილში, რომელშიც, როგორც ვთქვით, აღწერილია CLI-ის მეშვეობით დაინსტალირებული პროექტის კონფიგურაციული პარამეტრები.



კოდის მითითებულ ფრაგმენტში ხდება იმ გლობალური წყაროებისა და ფაილების მისამართების განსაზღვრა, რომელთა მეშვეობითაც შესაძლებელი იქნება, CSS სტილების მითითება ნებისმიერი HTML ელემენტისათვის ჩვენს პროექტში.

როგორც ვიცით, npm install... ბრძანება ახალ პაკეტებს აინსტალირებს node_modules საქაღალდეში, აქ დაინსტალირდებოდა Bootstrap-იც, შესაბამისად ჩავასწოროთ /angular.json ფაილც :

...

"styles": [
    "node_modules/bootstrap/dist/css/bootstrap.min.css",
    "src/styles.css"
],
     
...              
                




იმისათვის, რათა აისახოს ახალდამატებული კონფიგურაციული პარამეტრი და შესაბამისად ჩაიტვირთოს ახალი პაკეტიც, საჭიროა ng serve ბრძანების ხელახლა გაშვება.
5. სცენის მიღმა პროცესები პროექტის ჩატვირთვისას

შენიშნვა :

დავრწმუნდეთ, რომ ბრძანებათა კონსოლში გაშვებული გვაქვს ng serve ბრძანება.
src/app/app.component.html ფაილში ჩავწეროთ შემდეგი კოდი : <h2>გამარჯობა !</h2> შევინახოთ ცვლილება. ალბათ შეამჩნიეთ, რომ აღნიშნული ცვლილება ავტომატურად აისახა ბრაუზერში.

შეიძლება ვიფიქროთ თითქოს სერვერმა პირდაპირ ჩატვირთა, src/app/app.component.html ფაილში აღწერილი კოდი, მაგრამ ეს ასე არ არის ! სინამდვილეში სერვერმა მიაკითხა src/index.html ფაილს.

თუ ამ ფაილის კოდს გადავამოწმებთ შევამჩნევთ ერთ საინტერესო ფრაგმენტს :
...
<app-root></app-root>
...   
                
ასევე გავსხნათ src/app/app.component.ts ფაილი :
import { Component } from '@angular/core';

@Component({
	selector: 'app-root',
  	templateUrl: './app.component.html',
 	styleUrls: ['./app.component.css']
})

export class AppComponent {
	
}
                
@Component სექციას ეწოდება დეკორატორი (კომპონენტებისა და მათი სტრუქტურების შესახებ ვისაუბრებთ ოდნავ მოგვიანებით). როგორც ვხედავთ ამ დეკორატორში განსაზღვრულია selector: 'app-root' პარამეტრი. სწორედ ამ პარამეტრის დახმარებით გებულობს ანგულარი src/index.html ფაილის რომელი ელემენტის შიგთავსი უნდა განაახლოს გვერდის ჩატვირთვისას იმ შიგთავსით, რომელიც ამ ფაილთა მეშვეობით დაგენერირდება :



თუ ბრაუზერში გადავამოწმებთ ჩვენი პროექტის კოდს ვნახავთ შემდეგ სურათს :



როგორც ვხედავთ ხდება javascript-ის კონკრეტულ ფაილებთან დაკავშირება, არადა ეს ფაილები არ არის აღწერილი src/index.html ფაილში.

ng serve ბრძანება ავტომატურად ამატებს javascript-ის ფაილთა ამ დაჯგუფებას. პირველი რაც პროექტის ჩატვირთვისას ხდება ისაა, რომ სრულდება ამ ფაილებში აღწერილი კოდი.

ამის შემდეგ სისტემა აკითხავს src/main.ts ფაილს. გავსნათ ეს ფაილი :
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
                
მივაქციოთ ყურადრება ამ კოდის შემდეგ ფრაგმენტს : platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err)); bootstrapModule მეთოდის მეშვეობით ხდება პროექტის ჩატვირთვა. მას პარამეტრად გადაეცემა მოდულის დასახელება - AppModule.



გავხსნათ src/app/app.module.ts ფაილი :
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
	declarations: [
            AppComponent
 	],
    imports: [
    	BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }
                
ინგ : Bootstrapping - თვითრეგულირება, თვითაწყობა, თვითჩატვირთვა, თვითუზრუნველყოფა უხეშად, რომ ვთქვათ bootstrap პარამეტრის მეშვეობით ხდება იმის განსაზღვრა, თუ რომელი კომპონენტია ჩვენი პროექტის 'მთავარი' კომპონენტი, სწორედ მისი მეშვეობით გებულობს Angular-ი თუ რომელ კომპონენტს უნდა მიაკითხოს პროექტის ჩატვირთვისას, ამ შემთხვევაში ეს კომპონენტია AppComponent.

მაშ ასე, თუ გავაერთიანებთ ყოველივე ზემოთ თქმულს, მივიღებთ მოქმედებათა შემდეგ ჯაჭვს :





პროცესთა ამ ჯაჭვის შესრულების შემდეგ Angular-მა უკვე იცის, რომ უნდა მოახდინოს src/app/app.component.ts ფაილის ანალიზი. ანალიზის შემდეგ იგი დაადგენს თუ რომელ ელემენტში უნდა ჩასვას საჭირო კომპონენტის მიერ დაგენერირებული ახალი შიგთავსი, ეს ელემენტი, როგორც ვთქვით არის <app-root></app-root>, ახალი შიგთავსი კი გენერირდება src/app/app.component.html ფაილში : <h2>გამარჯობა !</h2>

6. კომპონენტები
უვხო სიტყვათა ლექსიკონი : კომპონენტი - რისამე შემადგენელი ნაწილი. ნებისმიერი ვებ-საიტი შეიძლება განხილულ იქნას, როგორც კონკრეტული ფუნქციონალების, ფრაგმენტების, კომპონენტების ერთობლიობა. კომპონენტის უკან შეიძლება მოიაზრებოდეს მაგალითად საიტის ქუდი (header), პროდუქტის ჩამონათვალის გვერდი და ა.შ. რა თქმა უნდა საჭიროა, რომ სადღაც აღიწეროს კონკრეტული ინსტრუქციები, კონკრეტული ლოგიკა, რომელიც დააგენერირებს საჭირო შიგთავსს, ზემოთ ჩამოთვლილი თითოეული ფრაგმენტისათვის.

კომპონენტი არის Typescript ფაილი, რომელში აღწერილი კლასის მეშვეობითაც იქმნება აპლიკაციის კონკრეტრული ფრაგმენტები.

ახალი კომპონენტის შექმნა

დავუშვათ გვინდა მომხმარებელს ვაჩვენოთ ინფორმაცია სერვერის შესახებ. შევქმნათ შესაბამისი კომპონენტი.

src/app საქაღალდეში შევქმნათ ახალი საქაღალდე server, მასში კი შევქმნათ ფაილი server.component.ts შემდეგი კოდით :

export class ServerComponent 
{
    
}
                
როგორც ვხედავთ, ჯერჯერობით, ეს არის Typescript-ის ჩვეულებრივი კლასი. საჭიროა ინსტრუქციები, რომელიც Angular-ს ეტყვის, რომ ეს არის კომპონენტი და არა ჩვეულებრივი კლასი. ამისათვის, პირველ რიგში, უნდა მოვახდინოთ კომპონენტებთან სამუშაო გლობალური გარემოს იმპორტი Angular-ის ძირითადი განშტოებიდან :
import { Component } from '@angular/core';

export class ServerComponent 
{
    
}
                
ახლა საჭიროა, რომ ჩვენი კომპონენტისათვის განვსაზღვროთ მეტაინფორმაცია ანუ ერთგვარი 'კონფიგურაციული პარამეტრები'. ამისათვის გამოიყენება Component დეკორატორი. დეკორატორს ინფორმაცია გადაეცემა ჩვეულებრივი ობიექტის სახით.
import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent 
{
    
}
                
კომპონენტში აუცილებლად უნდა მოხდეს მინიმუმ ამ ორი პარამეტრის განსაზღვრა :
  • selector - პარამეტრი, რომელიც განსაზღვრავს ელემენტს, რომლის შიგთავსის განახლებაც უნდა მოვახდინოთ საჭირო მომენტში.
  • templateUrl - იმ შაბლონის მისამართი, რომელიც უნდა გამოვიყენოთ ახალი შიგთავსის დასაგენერირებლად საჭირო მომენტში.
ახლა server საქაღალდეში შევქმნათ წარმოდგენის ფაილი server.component.html და შევიტანოთ მასში რაიმე კოდი :
<h1>სერვერის კომპონენტი</h1>
                

ახალი კომპონენტი შევქმენით, მაგრამ, უხეშად რომ ვთქვათ, Angular-მა ჯერ 'არ იცის' ამის შესახებ, საჭიროა 'გავაგებინოთ' მას ეს, ამისათვის აუცილებელია მოვახდინოთ კომპონენტის იმპორტი და დეკლარირება app.module.ts ფაილში.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component';

@NgModule({
    declarations: [
        AppComponent,
        ServerComponent
    ],
    imports: [
    	BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }

                
მოდულების შესახებ დაწვრილებით ვისაუბრებთ ოდნავ მოგვიანებით.

ახლა უკვე შეგვიძლია ბრაუზერშიც ვიხილოთ ჩვენი ახალი კომპონენტის მუშაობის შედეგი. ამისათვის უნდა გავხსნათ src/app/app.component.html ფაილი და მასში ჩავამატოთ ის ელემენტი, რომლის შიგთავსიც უნდა განახლდეს ახალი კომპონენტის მეშვეობით :

<h2>გამარჯობა !!!</h2>
<server-root></server-root>
                

კომპონენტის შექმნა CLI ბრძანების მეშვეობით

შენიშნვა :

დავრწმუნდეთ, რომ ბრძანებათა კონსოლში გაშვებული გვაქვს ng serve ბრძანება.
ახალი კომპონენტის შესაქმნელი CLI ბრძანების სინტაქსი ასეთია : ng generate component კომპონენტის_დასახელება შემოკლებული ვარიანტი : ng g c კომპონენტის_დასახელება დავუშვათ გვინდა ახალი კომპონენტის შექმნა, რომლის სახელიცაა servers. გავხსნათ ჩვენი ოპერაციული სისტემის ბრძანებათა კონსოლის ახალი ფანჯარა, გადავინაცვლოთ პროექტის საქაღალდეში და გავუშვათ შემდეგი ბრძანება : ng g c servers ბრძანების გაშვების შემდეგ src/app საქაღალდეში შეიქმნება ახალი საქაღალდე servers, რომელშიც დაგხვდება კომპონენტთან სამუშაო შემდეგი ფაილები :



servers.component.spec ფაილი გამოიყენება ტესტირებისათვის და ამ ეტაპზე შეგვიძლია არ მივაქციოთ ყურადღება, ხოლო თუ გვინდა, რომ იგი საერთოდ არ შექიქმნას, მაშინ კომპონენტის შექმნის ბრძანება უნდა გავუშვათ ასეთი სახით : ng g c servers --skipTests

ასევე აღსანიშნავია, რომ ავტომატურად განახლდებოდა src/app/app.module.ts ფაილიც :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component';
import { ServersComponent } from './servers/servers.component';

@NgModule({
    declarations: [
        AppComponent,
        ServerComponent,
        ServersComponent
    ],
    imports: [
    	BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }

                
7. ინფორმაციის ტრანსფერი (Databinding)
აქამდე ბრაუზერში გამოგვქონდა მხოლოდ სტატიკური ტექსტები. შეიძლება დაგვჭირდეს, რომ მომხმარებელს ვაჩვენოთ სერვერიდან მიღებული დინამიური ინფორმაცია ან რაიმე გამოთვლების შედეგი. ასეთ შემთხვევაში ბიზნეს-ლოგიკა და გამოთვლები უნდა აღიწეროს და ჩატარდეს კომპონენტის მთავარ კლასში, შედეგად მიღებული ინფორმაცია კი უნდა მიემაგროს (ინგ: Bind - მიბმა; დამაგრება) წარმოდგენის HTML შაბლონს. ინფორმაციის მიმაგრების უკან შეიძლება მოვიაზროთ კომპონენტის ძირითადი კლასისა და HTML შაბლონის ერთგვარი კომუნიკაცია. არსებობს ამ კომუნიკაციის რამოდენიმე ვარიანტი :



ინფორმაციის გაცვლის ცალმხრივი გზები

მიმართულება Typescript --> HTML

ინტერპოლაცია

უვხო სიტყვათა ლექსიკონი : ინტერპოლაცია - რაიმე ნაწარმოების ტექსტში გვიანდელი ჩანართი, რომელიც ავტორს არ ეკუთვნის. წარმოდგენის შაბლონზე ინფორმაციის მიმაგრების ყველაზე მარტივი საშუალებაა ინტერპოლაცია. ამ მეთოდით ინფორმაციის მიმაგრებისას გამოიყენება ორმაგი ფიგურული ფრჩხილები : {{ name }} ხშირად, ფიგურულ ფრჩხილებში მოქცეულია ხოლმე კომპონენტის კლასის კონკრეტული მეთოდის ან თვისების დასახელება. გავხსნათ src/app/server/server.component.ts ფაილი და შევიტანოთ შემდეგი კოდი :
import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    serverID = 10543534534;
    serverStatus = 'ჩართული';	

    getServerStatus()
    {
        return this.serverStatus;
    }  

}
                
src/app/server/server.component.html ფაილში კი ავკრიფოთ შემდეგი კოდი :
<h1>სერვერის id : {{ serverID }}</h1>
<h1>სერვერის სტატუსი : {{ getServerStatus() }}</h1>
                
ბრაუზერში ვიხილავთ შეაბამის შედეგს ;)

HTML ელემენტების თვისებების მართვა (Property binding)

src/app/server/server.component.html ფაილში შევქმნათ img ელემენტი და მისი src ატრიბუტის მნიშვნელობა განვსზაღვროთ ინტერპოლაციის გზით :
<img src="{{ img }}">
                
ბუნებრივია ServerComponent კლასშიც უნდა აღვწეროთ img თვისება :
import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    img = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/512px-Angular_full_color_logo.svg.png";
}
                
ServerComponent კლასის კონსტრუქტორში აღვწეროთ კოდი, რომელიც ფოტოს src ატრიბუტის მნიშვნელობას შეცვლის 3 წამში :
import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    img = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/512px-Angular_full_color_logo.svg.png";

    constructor()
    {
        setTimeout(() => {

            this.img = "http://arbo.com.ve/wp-content/uploads/2016/01/laravel-logo.png";

        }, 3000);

    }
}
                
შესაძლებელია, რომ იგივე შედეგი რაც ზემოთ მივიღეთ ცოტა სხვაგვარი ხერხითაც მივიღოთ, შევცვალოთ მხოლოდ შაბლონი :
<img [src]="img">
                
როდესაც რომელიღაც ატრიბუტი მოქცეულია მართკუთხა ფრჩხილებში - '[]', ეს იმას ნიშნავს, რომ საქმე გვაქვს ინფორმაციის მიმაგრების, კიდევ ერთ ცალმხრივ მეთოდთან (One way binding) და ატრიბუტის მნიშვნელობის განსაზღვრა ხდება კლასში.

მიმართულება HTML --> Typescript

რეაგირება მომხმარებლის ქმედებებზე ანუ მოვლენებზე (Event binding)

src/app/server/server.component.html ფაილში ავკრიფოთ შემდეგი კოდი :
<button (click)="onCreateServer()" [disabled]="!disabled">დამატება</button>

<p>{{ serverCreationStatus }}</p>
                
src/app/server/server.component.ts ფაილში კი ავკრიფოთ შემდეგი კოდი :
import { Component } from '@angular/core';

@Component({
	selector: 'server-root',
  	templateUrl: './server.component.html',
})

export class ServerComponent
{
    disabled = true;
    serverCreationStatus = 'სერვერი ჯერ არ შექმნილა';

    onCreateServer()
    {
        this.serverCreationStatus = "სერვერი შეიქმნა";
    }

}
                
ამ კოდში "disabled" ატრიბუტთან დაკავშირებით უკვე ყველაფერი გარკვეული გვაქვს, (click)="onCreateServer()" ჩანაწერის სინტაქსი ჩვენთვის კი ახალია.

როდესაც Angular-ი ხედავს მრგვალ ფრჩხილებს, იგი ხვდება, რომ Typescript კლასსა და წარმოდგენის შაბლონს შორის ინფორმაციის გაცვლა უნდა მოხდეს მომხმარებლის ქმედებიდან გამომდინარე (Event binding). ამ შემთხვევაში ვრეაგირებთ ღილაკზე მაუსის დაჭერის მოვლენაზე და ინფორმაცია მიემართება შაბლონიდან კლასისაკენ : HTML --> Typescript. უფრო კონკრეტულად : სისტემას ვეუბებით რომ, როდესაც მომხმარებელი დააწვება ღილაკს, შეცვალოს კლასის serverCreationStatus თვისების მნიშვნელობა.

$event

ნებისმიერი მოვლენა, რომელიც შეიძლება დაფიქსირდეს მომხმარებლის მხარეს : რაიმე ელემენტზე მაუსის დაჭერა, ტექსტური ტიპის ელემენტში ტექსტის აკრეფა და ა.შ, შეიძლება დაკავშირებულ იქნას Typescript კლასში აღწერილი რაიმე ფუნქციონალთან. მაგალითად კონკრეტულ ველში ტექსტის აკრეფისას, აკრეფილი ტექსტი განოვიტანოთ პარაგრაფში.

src/app/server/server.component.html ფაილში ავკრიფოთ შემდეგი კოდი :

<input type="text" (input)="onInput($event)">

<p>{{ inputValue }}</p>

                
src/app/server/server.component.ts ფაილში კი ავკრიფოთ შემდეგი კოდი :
import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    inputValue = '';

    onInput(event)
    {
        this.inputValue = event.target.value;
    }

}
                
$event არის რეზერვირებული სიტყვა, რომელიც ერთმანეთთან აკავშირებს მომხმარებლის მხარის მოვლენებსა და Angular-ს.

განხილულ მაგალითებში ინფორმაციამ 'იმოძრავა' ერთი მიმართულებით : კლასიდან --> HTML შაბლონისაკენ, ან პირიქით - HTML შაბლონიდან --> კლასისკენ. სწორედ ამიტომ ეწოდება ინფორმაციის მიმაგრების ამ ხერხებს ცალმხრივი.

ინფორმაციის გაცვლის ორმხრივი გზები

დავუშვათ შაბლონში და კლასში გვაქვს ამდაგვარი სიტუაცია :

src/app/server/server.component.html :

<input type="text" [value]="inputValue">
                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    inputValue = 'ტექსტი';
}
                
როგორც ვიცით, ეს არის ინფორმაციის მიმაგრების ცალმხრივი გზა და ინფორმაცია მიემართება კლასიდან --> შაბლონისაკენ.

ახლა მოვიქცეთ ასე :

src/app/server/server.component.html :

<input type="text" [value]="inputValue" (input)="onInput($event)">
                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    inputValue = 'ტექსტი';

    onInput(event)
    {
        this.inputValue = event.target.value;
    }
}
                
როგორც ვხედავთ გვერდის ჩატვირთვისას ტექსტური ველის მნიშვნელობის განსაზღვრა ხდება კლასიდან: inputValue = 'ტექსტი', ანუ ინფორმაცია მიემართება კლასიდან --> შაბლონისაკენ, ხოლო მას შემდეგ რაც ველში რაიმეს ავკრეფთ იცვლება inputValue თვისების მნიშვნელობაც, ანუ ამჯერად ინფორმაცია მიემართება შაბლონიდან --> კლასისაკენ.

ეს არის ინფორმაციის მიმაგრების ორმხრივი გზის გამოყენების მაგალითი, თუმცა Angular-ში არსებობს შედარებით მარტივი ხერხი იგივე შედეგის მისაღებად, უბრალოდ მოგვიწევს, რომ src/app/app.module.ts ფაილში აღწერილ NgModule დირექტივაში მოვახდინოთ FormsModule მოდულის იმპორტირება (დირექტივებისა და მოდულების შესახებ ვისაუბრებთ ოდნავ მოგვიანებით) :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component';
import { ServersComponent } from './servers/servers.component';
import { FormsModule } from '@angular/forms';

@NgModule({
    declarations: [
        AppComponent,
        ServerComponent,
        ServersComponent
    ],
    imports: [
    	BrowserModule,
    	FormsModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }
                
როგორც ვიცით ინფორმაციის ცალმხრივი მიმართულებით გაცვლისას, კონტროლერიდან ინფორმაციის მისაღებად ვიყენებთ მართკუთხა ფრჩხილებს - '[]', უკუმიმართულებით კი ჩვეულებრივ ფრჩხილებს - '()', ლოგიკურია, რომ ორმხრივი გაცვლისას დაგვჭირდება ამ ფრჩხილების კომბინაციის გამოყენება :

src/app/server/server.component.html :

<input type="text" [(ngModel)]="inputValue">
                
8. დირექტივები
Angular-ში დირექტივა არის ერთგვარი ინსტრუქცია, რომლის მეშვეობითაც მარტივდება დოკუმენტის ობიექტური მოდელის (DOM) ელემენტებით მანიპულირება.



დირექტივების კლასიფიცირება ხდება სამი ძირითადი კატეგორიის მიხედვით :
  • კომპონენტური დირექტივები
  • სტრუქტურული დირექტივები
  • ატრიბუტული დირექტივები

კომპონენტური დირექტივები

კომპონენტური დირექტივის შესახებ ვისაუბრებთ ოდნავ მოგვიანებით

სტრუქტურული დირექტივები

სტრუქტურული დირექტივების მეშვეობით ხდება HTML შაბლონის გენერირება, დომ (DOM) სტრუქტურის ფორმირება, HTML ელემენტების წაშლა ან დამატება. სტრუქტურული დირექტივის დასახელებას წინ ერთვის '*' სიმბოლო.

*ngIf

*ngIf დირექტივის მეშვეობით შესაძლებელია დომ (DOM) ელემენტის წაშლა ან დამატბა. როგორც დასახელებიდან ჩანს ამ დირექტივის გამოყენებისას საქმე გვაქვს კონკრეტული პირობის შესრულება/არშესრულებასთან, შესაბამისად ბრუნდება ლოგიკური ტიპის ინფორმაცია და ამ ინფორმაციიდან გამომდინარე ხდება დომ (DOM) სტრუქტურის ცვლილება.

src/app/server/server.component.html :

<div *ngIf="haveDiv">
    გვაქვს div ელემენტი
</div>
                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    haveDiv = true;
}
                
თუ არსებობს if, მაშინ იქვეა else-ც :)) :
<div *ngIf="haveDiv; else noDiv">
    გვაქვს div ელემენტი
</div>
<ng-template #noDiv>
	არ გვაქვს div ელემენტი
</ng-template>
                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    haveDiv = false;
}
                

*ngFor

ამ დირექტივის მეშვეობით შესაძლებელია დავაგენერიროთ HTML შაბლონი კოლექციის თითოეული ელემენტისათვის. დირექტივის გამოყენების ზოგადი სინტაქსი ასეთია : <elem *ngFor="let item of items;"> .... </elem> let სიტყვაგასაღების მეშვეობით ხდება კოლექციის კონკრეტული ელემენტის ინიციალიზაცია, item და items დასახელებები კი პირობითია და წარმოადგენენ შესაბამისად - კოლექციის კონკრეტული ელემენტისა და კოლექციის დასახელებებს.

დავუშვათ კომპონენტის კლასში გვაქვს აღწერილი მასივი, რომელშიც შენახულია რამოდენიმე რიცხვი ფიბონაჩის მიმდევრობიდან :

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
    styleUrls: ['./server.component.css']
})

export class ServerComponent implements OnInit 
{
    fibonachi = [1,1,2,3,5,8];
}
                
და გვსურს, რომ თითოეული ამ რიცხვისათვის დავაგენერიროთ ახალი li ელემენტი HTML ul ელემენტში :
<ul>
    <li *ngFor="let f of fibonachi">
        {{ f }}
    </li>
</ul>
                
რა თქმა უნდა შესაძლებელია, რომ ციკლის მუშაობისას დავაფიქსიროთ მიმდინარე ელემენტის ინდექსი კოლექციაში, ამისათვის გამოიყენება index სიტყვაგასაღები :
<ul>
    <li *ngFor="let f of fibonachi; let i=index">
        {{ i }}	{{ f }}
    </li>
</ul>
                

ატრიბუტული დირექტივები

სტრუქტურული დირექტივებისაგან განსხვავებით ატრიბუტული დირექტივების მეშვეობით არ ხდება HTML ელემენტების წაშლა/დამატება. მათი მეშვეობით უბრალოდ ვმუშაობთ უკვე შექმნილ ელემენტებთან, მაგალითად ვცლით მათ იერსახეს...

ngStyle

ngStyle დირექტივა გამოიყენება არსებული ელემენტების CSS სტილების განსაზღვრისათვის. დირექტივას ინფორმაცია გადაეცემა ერთმანეთისაგან მძიმით გამოყოფილი 'გასაღები : მნიშვნელობა' წყვილების სახით. გასაღები არის CSS თვისების დასახელება, მნიშვნელობა - ამ თვისების შესაბამისი მნიშვნელობა : <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>

src/app/server/server.component.html :

<div [ngStyle]="{
width: '100px', 
height: '100px',
borderRadius: '5px',
border: '1px solid red'
}">

</div>
                
ამის შემდეგ თუ ბრაუზერში გადავამოწმებთ პროექტის კოდს, ვნახავთ, რომ დაგენერირებულია ჩვეულებრივი ხაზშიდა (inline) სტილი :
<div style="..."></div>
                
ბუნებრივია ჩნდება კითხვა : მაშინ რაღა საჭიროა ამ დირექტივის გამოყენება ? საქმე იმაშია, რომ ამ დირექტივაში შესაძლებელია CSS სტილების დინამიურად განსაზღვრა Typescript კლასში აღწერილი ინფორმაციის მიხედვით :

src/app/server/server.component.html :

<button (click)="backgroundToggle = !backgroundToggle">ჩარჩოს ფერის შეცვლა</button>

<div [ngStyle]="{
width: '100px', 
height: '100px',
borderRadius: '5px',
border: '1px solid red',
background : backgroundToggle ? 'red' : 'blue'
}">
	
</div>

                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    backgroundToggle = false;
}
                

ngClass

ამ დირექტივის მეშვეობით შესაძლებელია, რომ HTML ელემენტებს კლასი განვუსაზღვროთ დინამიურად.

src/app/server/server.component.html :

<button (click)="backgroundToggle = !backgroundToggle">ჩარჩოს ფერის შეცვლა</button>

<p [ngClass]="{
	red: backgroundToggle,
	blue: !backgroundToggle
}">პარაგრაფი</p>
                

src/app/server/server.component.ts :

import { Component } from '@angular/core';

@Component({
    selector: 'server-root',
    templateUrl: './server.component.html',
})

export class ServerComponent
{
    backgroundToggle = false;
}
                
შევქმნათ ახალი ფაილი : src/app/server/server.component.css და მასში აღვწეროთ ორი კლასი : red და blue :
.red {
    background: red;
}

.blue {
    background: blue;
}
                
იგივეს გაკეთება შესაძლებელია ასეც :
<button (click)="backgroundToggle = !backgroundToggle">ჩარჩოს ფერის შეცვლა</button>

<p [class.blue]="backgroundToggle" [class.red]="!backgroundToggle">პარაგრაფი</p>
                
9. მოდელები
დავუშვათ გვაქვს საიტი სადაც განთავსებულია სხვადასხვა სტატიები. ბუნებრივია თითოეულ სტატიას ექნება კონკრეტული მახასიათებლები - უმარტივეს შემთხვევაში სათაური და ტექსტი :
[
    {  
        title: "პირველი სიახლე", 
        text: "პირველი სიახლის ტექსტი",
    },
    {  
        title: "მეორე სიახლე", 
        text: "მეორე სიახლის ტექსტი",
    }
]
                
რა თქმა უნდა შესაძლებელია, რომ მოხდეს ამ მახასიათებლების ერთ კონკრეტულ გარემოში აღწერა, გარემოში რომელიც იქნება კონკრეტული სიახლეების განზოგადებული, აბსტრაქტული სახე და რომლის მეშვეობითაც მოვახდენთ ამ სტატიებით მანიპულირებას : შევქმნით, ჩავასწორებთ, წავშლით მათ. ამ გარემოს ეწოდება მოდელი, რომელიც Angular-ში წარმოადგენს ჩვეულებრივი Typescript კლასს.

რადგანაც კლასი ვახსენეთ, ოოპ-ის ტერმინებით ჩამოვაყალიბოთ ყოველივე ის, რაც ზემოთ ვთქვით : შეგვიძლია შევქმნათ კლასი Post, რომელშიც აღიწერება კონკრეტული სტატიებისათვისს დამახასიათებელი ყველა თვისება, თავად სტატიები კი იქნებიან ამ კლასის ეგზემპლიარი ობიექტები.

პირველ რიგში შევქმნათ სიახლეებთან სამუშაო კომპონენტები, დავიწყოთ კომპონენტით, რომელიც ბრაუზერში გამოიტანს სიახლის დასამატებელ ფორმას: ng g c post-form --skipTests ახლა შევქმნათ კომპონენტი, რომელიც უზრუნველყოფს უშუალოდ სიახლეების გამოტანას : ng g c post --skipTests

ახლა შევქმნასთ პოსტების მოდელი, გავაკეთოთ ფაილი src/app/post/post.model.ts :

export class Post
{
    public title: string; // სათაური
    public text: string; // ტექსტი

    constructor(title: string, text: string)
    {
        this.title = title;
        this.text = text;
    }
}
                
აქვე აღვნიშნოთ, რომ იგივეს გაკეთება შესაძლებელია შემდეგნაირადაც :
export class Post
{
    constructor(public title: string,  public text: string)
    {
        
    }
}
                
ახლა ეს კლასი დავაკავშიროთ სიახლეების კომპონენტის მთავარ კლასთან, შევიტანოთ შესაბამისი ცვლილებები src/app/post/post.component.ts ფაილში :
import { Component, OnInit } from '@angular/core';
import { Post } from './post.model';

@Component({
    selector: 'app-post',
    templateUrl: './post.component.html',
    styleUrls: ['./post.component.css']
})

export class PostComponent implements OnInit 
{
    posts: Post[] = [
        new Post('პირველი სიახლე','პირველი სიახლის ტექსტი'),
        new Post('მეორე სიახლე','მეორე სიახლის ტექსტი')
    ];
}
                
მივაქციოთ ყურადღება კოდის შემდეგ ფრაგმენტს :
posts: Post[] = [
    new Post('პირველი სიახლე','პირველი სიახლის ტექსტი'),
    new Post('მეორე სიახლე','მეორე სიახლის ტექსტი')
];
                
კლასში განვსაზღვრეთ თვისება 'posts', რომლის ტიპიც არის Post[] ანუ ამ თვისებაში შეინახება სიახლეების ობიექტების მასივი.

ახლა შეგვიძლია ეს სიახლეები გამოვიტანოთ წარმოდგენის ფაილში - src/app/post/post.component.html :

<div *ngFor="let post of posts">
    <h3>{{ post.title }}</h3>
    <p>{{ post.text }}<p>
</div>
                
ლურჯი ფერით მონიშნულია საკვანძო წერტილები შაბლონში, თითოეულ მათგანში აღწერილი ინსტრუქციები ჩვენთვის უკვე ნაცნობია და ამიტომ აღარ დავწვრილმანდეთ.

ამ ყველაფრის შედეგად უნდა ვიხილოთ ამდაგვარი სურათი :