Template1 Zawsze chciałem zrobić serię wpisów o Angularze i ASP.NET. W tym wpisie zobaczymy podstawowy szablon z Visual Studio, który ma łączyć świat Angular i ASP.NET CORE. Chociaż w tym wpisie będzie używał nazwy ASP.NET, ponieważ już niedługo słowo CORE nie będzie miało znaczenia wraz z pojawieniem się .NET 5.0, który scali środowiska. Jednak może zostajemy przy ASP.NET CORE. Słowa kluczowe w SEO to mój problem.
Tutaj też pojawia się moja obawa.
Taki wpis wymaga ode mnie pracy, a nigdy nie wiesz, kiedy coś się zmieni. Przynajmniej z tego, co widzę szablon Angulara z 2017 i z 2020 nie wiele się różnią poza tym, że szablon z 2020 roku został odchudzony.
To daje mi nadzieje, że jak czytasz to z przyszłości to ten wpis będzie użyteczny.
Świat ASP.NET się zmienia dosyć szybko, ale dobra wiadomość jest taka, że styl MVC wciąż w nim jest od 9 lat. Nie musisz się więc martwić, że ta wiedza pójdzie do kosza.
Co do Angulara? Zrobiłem oddzielny wpis na temat jego historii (https://cezarywalenciuk.pl/blog/programing/co-to-jest-angular-historia-do-2020), ponieważ byłem ciekaw czy on ma gwałtowne zmiany w swoim kodzie. Odpowiedź brzmi : nie jest tak źle. Największa zmiana dotyczyła paczki @http, a tak pojawiają się tylko usprawnienia.
Mamy więc stronę serwerową : ASP.NET i mam także stronę kliencką Angular. Sprawdzimy podstawowy szablon z Visual Studio i hej może już programowałeś w tych technologiach wcześniej i chciałbyś zobaczyć co się zmieniło.
Oto tematy w tym wpisie:
- Co jest w tym szablonie
- Jak wygląda back-end .NET CORE
- Angular, czyli jak wygląda front-end
Najpierw stwórzmy ten szablon. Wybieramy projekt : ASP.NET Core Web Application.
Później dajemy sensowną nazwę naszemu projektowi.
A na końcu wybieramy szablon Angular i zaznaczamy opcję Configure for HTTPS, bo to jest przyszłość.
Jak wygląda nasz szablon? Po pierwsze, jeżeli masz jakikolwiek doświadczenie z ASP.NET Core czy z Angular wiesz mi nie poczujesz się zagubiony.
Przeanalizujmy każdy folder w tym projekcie.
Dependencies to wirtualny folder, który zastąpił stary folder "Resources" . Zawiera on wszystkie biblioteki referencję potrzebne do zbudowania projektu przez język programowania C#. Żyjemy w 2020 więc wszystkie referencję projektowe to tak naprawdę paczki z systemu NuGet. NuGet to repozytorium paczek jak NPM dla Node.js.
W prawdziwym folderze Controllers siedzą kontrolery. Od czasu ASP.NET MVC z roku 2009 w tej kwestii nic się nie zmieniło.
W folderze Pages mam strony, które będziemy generować. W nich oprócz HTML możemy także używać specjalnej składni Razor, która łączy świat C# z HTML.
W kwestii .NET w projekcie mamy także następujące pliki. On deklarują ustawienia aplikacji. .gitignore mówi jakie pliki mają być zignorowany, gdybyśmy chcieli wrzuć nasz projekt na GitHub. appsettings.json zawiera zbiór konfiguracji dla .NET. Ty też możesz z niego skorzystać. Klasa Startup, Program zajmuję uruchomienie serwera i o tym napiszę później.
Teraz przejdźmy do części frontendowej. W folderze wwwroot będą ostatecznie znajdować pliki statyczne jak : HTML, JS, CSS, Obrazki, Fonty. Na razie jest tam tylko ikona strony.
W folderze ClientApp żyje Angular i jego menadżer paczek.
W folderze e2e znajduje się przykład testu end-to-end z frameworkiem Protractor.
W nim w folderze src znajdują się plik źródłowe Angulara. Jak widzisz mają one rozszerzenie .ts, czyli korzystają one z TypeScript. Transpilatora języka JavaScript.
ASP.NET CORE Back-end
Jeżeli programujesz w ASP.NET od jakiegoś czasu zapewne się zastanawiasz gdzie folder Views.
Odpowiedź jest prosta to jest szablon SPA czyli teraz budujemy Single-Page Application i skoro będziemy pracować na tylko jednej stronie to poco nam jest na ten folder.
W folderze Pages masz w sumie tylko stronę, która ma wyświetlić błąd po stronie C# przed uruchomieniem kodu po stronie Angulara. A strona, która nam się wyświetli ostatecznie znajduje się w "/ClientApp/src/folder/index.html"
Czym są Razor Pages? Istnieją one od ASP.NET CORE 2 i reprezentują one alternatywny styl pisania aplikacji inni niż MVC. Strona Error.cshtml jest razor page i skąd ja to wiem? Bo ma w swoim pliku załączony plik C#.
Jeśli pamiętasz ASP.NET Web Form to zapewne sobie przypominasz, że podobnie było z plikami aspx i aspx.cs.
Controllers
Okej skoro wszystko działa na Razor Pages to po co nam są kontrolery w folderze /Controller/
Powód jest prosty nie wszystkie kontrolery służą do renderowania widoków stron HTML. Kontrolery mogą także zwracać dane w formacie JSON, XML. Potrafią także zwracać pliki statyczne lub po prostu kod HTTP bez zwartości.
Jak widzisz obecnie znajduje się tylko jeden kontroler i jest on tak naprawdę API REST-owym.
Jeśli uruchomisz aplikację i wpiszesz adres tego kontrolera to otrzymasz dane w formacie JSON. Te API będzie używane przez Angulara.
Program.cs
Plik Program.cs to nowość i coś co zazwyczaj się nie widzi w aplikacji WEB. Pojawił się on po raz pierwszy w ASP.NET 1.0 i jego rola polega na stworzeniu obiektu WebHostBuilder. Ten obiekt stworzy inny obiekt IWebHost,a on będzie trzymał całą naszą aplikację.
Co jest WebHost i o co chodzi?
Czym jest ten web host? Wywołuje on cały kontekst twoje aplikacji i musi on implementować interfejs IWebHost. WebHost referuje się do serwera, który będzie obsługiwał zapytania HTTP do twojej aplikacji.
Teraz czy webhost i serwer to jedno i to samo? Niedokładnie? Host zajmuje cyklem życia twojej aplikacji : pierwszy startem i akceptacją zapytań HTTP. Host dba o konfigurację, jak i o kolejność działa odpowiednich komponentów.
Host jest raczej otoczką na sam serwer. Host konfiguruje się na użycie określonego serwera, natomiast sam serwer nie ma o nim pojęcia. Ma to sens, ponieważ ASP.NET CORE może działać nie tylko na IIS i nie tylko na systemie Windows.
Jeżeli otworzysz ten plik to znajdziesz taki kod. Jak widzisz nasz host jest budowany w taki sposób. Jak widzisz jest w nim tak mało kodu,że aż się prosi coś do niego dodać. Co tutaj się dzieje? Aby na to odpowiedzieć trzeba zobaczyć stary kod z innej wersji ASP.NET CORE.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Swoją drogą dużo się pozmieniało, bo kiedy w ASP.NET CORE 1.X ten budowniczy wyglądał tak :
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
host.Run();
}
}
Co to robiło: Ustawiało to Kestrel, czyli serwer. Ustawiało folder główny aplikacji, w który będą szukane pliki konfiguracyjne appsettings.json. Dla serwera IIS mówimy, że chcemy mieć z nim integrację. Definiujemy użycie klasy Startup, bo może chciałbyś skorzystać z innej klasy.
...i ostatecznie budujemy ten host i go uruchamiamy metodą Run()
Do ASP.NET CORE 2 i 3 została dodana metoda CreateDefaultBuilder(), która ustawia te wartości domyślne za Ciebie.
Jak widzisz nasz WebHost odnosi się do klasy Startup, a ona co robi?
Startup.cs
Klasa Startup zastępuję plik i klasę Global.asax. Jeśli programowałeś wcześniej w ASP.NET to wiesz, że w Global.asax miałeś dostęp do wielu rzeczy jak globalne zdarzenia serwera (pierwsze uruchomienie, błąd). Był tam duży bałagan.
<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
}
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
</script>
Chłopaki więc z Microsoftu zrobili więc porządek i stwierdzili, że potrzebna nam jest jakaś inna kontrola nad cyklem życia serwera niż zdarzenia.
Cała idea zrodziła się z OWIN i pamiętam, jakie to było super cool w 2013 roku. Chryste, ale jestem stary.
https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/
Oczywiście później ta idea została spłaszczona do minimum. Co możemy zrobić w klasie startup.
- Możemy dodać konfigurację usług, z jakich będziemy korzystali oraz określić co będzie wstrzykiwane zależnością . To się dzieje w metodzie : ConfigureServices()
- Konfigurujemy obsługę pipeline (nawet nie będę próbował tego tłumaczyć na polski). Pipeline/Potok (potok to chyba dobre określenie) zapytań HTTP jest sterowany przez tak zwane middleware w metodzie : Configure()
Zobaczmy jak to wygląda w kodzie:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Cała ta metodą konfiguruję potok/pipeline zapytań HTTP. Wiesz mi ten kod jest czytelniejszy niż wszystkie Global.asax.cs, które widziałem w życiu
Co tutaj się dzieje. Po pierwsze sprawdzamy, na jakim środowisku się znajdujemy i jeśli jesteśmy na developerskim to śmiało wyrzucamy błąd strony. Na produkcji prawdopodobnie chcielibyśmy wyświetlić stronę z błędem dla użytkownika.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
Później mam linijkę kodu, z której nawet ja korzystam na blogu. Czyli przekierowanie z HTTP na HTTPS. Jak wiesz przeglądarki straszą użytkowników gdy strona działa na http i masz duży komunikat, że strona nie jest zabezpieczona. Nie wiadomo też jak mapuje Google strony w swojej przeglądarce, które są HTTP. Teraz każda strona musi być pod protokołem HTTPS.
Robi to na samym początku sprawdzania potoku HTTP. Kolejność ma znaczenie w tym sprawdzaniu potoku zapytania.
app.UseHttpsRedirection();
Później obsługujemy pliki statyczne z folderu wwwroot oraz pliki z aplikacji Angular, które będą pod ścieżką "/ClientApp/src/assets/". Bez tych komend nie obsłużysz plików statycznych jak JS,CSS czy HTML. Metody te nie mają parametrów więc na chwilę obecną działa na domyślnych ustawieniach.
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
Teraz mamy middleware Routing, które informuje pipeline, że będziemy korzystać ze ścieżek. Później w UseEndpoint je deklarujemy. Jak widzisz to tutaj deklarujemy adres do kontrolerów ASP.NET CORE. Mapowanie pobiera nazwę kontrolera np. WeatherForcast, później przechodzimy do akcji. Jeśli tego nie zrobimy to wykonamy akcję domyślna . Czyli :
https://localhost/WeatherForcast/index to samo co https://localhost/WeatherForcast/
Na koniec może jeszcze podać parametry opcjonalne do metody https://localhost/WeatherForcast/index/2
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
Na koniec została nam ta deklaracja middleware. Konfigurujemy tutaj dwie rzeczy.
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
Na początek określamy folder, w którym znajduje się nasza aplikacja Angular.
Druga część jest bardziej złożona. Jak widzisz chcemy uruchomić serwer Angulara gdy jesteśmy na środowisku deweloperskim/testowym.
ASP.NET CORE wysyła wtedy informację o zapytaniu HTTP dalej do instancji serwera CLI Angulara. Dlaczego to jest potrzebne? Służy to do szybkiego odświeżana kodu. Chodzi o to byś z każdą zmiana kodu nie musiał ręcznie uruchamiać Angular CLI.
Na produkcji nie będzie na to potrzebne.
Jeśli chcesz się przekonać w praktyce jak to działa to dodaj plik statyczny w folderze wwwroot.
Na przykład plik HTML o nazwie hello
Oto jego zawartość.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
JESTEM PRZED MVC I SPA
</body>
</html>
Jeżeli uruchomisz teraz aplikację "CTRL+F5" i wpiszesz w adresie hello.html to zobaczysz, że ten plik jest obsłużony.
Gdybyś za komentował linkę app.UseStaticFiles() to wtedy uruchomi Ci się aplikacja Angulara, gdyż to jest pod koniec naszego potoku.
Sam Angular będzie próbował zrozumieć "/hello.html" i wyrzuci cichy błąd "Cannot match any routes" w konsoli przeglądarki o tym, że nie wie o co ci chodzi.
Myślę, że ten prosty przykład pokazuje Ci jak ten potok/pipeline działa.
Najpierw ASP.NET CORE będzie próbował zrozumieć twoje zapytanie HTTP, później będzie to robić aplikacja Angular. Jak konfiguruje się ścieżki w Angularze o tym później.
appsetting.json
W starym ASP.NET nie było dobrego miejsca na informację konfiguracyjne. Zazwyczaj robiło się to w pliku web.config. Pliku, w który także konfigurował się serwer IIS.
Oczywiście skoro ASP.NET CORE działa na każdych systemach oznacza to, że nie można już polegać na pliku web.configu. Dlatego mam plik appsettings.json. Po pierwsze format XML został zastąpiony formatem JSON.
Logika jest prosta . Każde ustawienie ma swój klucz i wartość. Złożona wartość składających z kolejnych kluczy i wartości będzie traktowana później jako klasa.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Istnieje kilka sposób na zmapowanie tych wartości na obiekty w C#. Jeden z najprostszych powodów polega na wstrzyknięciu sobie całej konfiguracji i wyszukanie wartości po odpowiednim indeksem w taki sposób.
public HomeController(IConfiguration configuration)
{
var includeScopes = configuration["Logging:IncludeScopes"];
}
Lepszy sposobem jest stworzenie swoje własny klasy, która mapuje konkretną dział konfiguracji.
Warto wspomnieć, że plik konfiguracyjny ma swój pod plik. On określa działania konfiguracji, gdy jesteś na innym środowisku. Coś jak web.debug.config z okresu do ASP.NET 4.X
Jeżeli takie pliki konfiguracyjne Ci się nie podobają możesz stworzyć swój system edytując kod w WebHost w pliku Program.cs.
Front end Angular
Przechodzimy w końcu do części front-endowej. Zobaczmy ten szczyt ewolucji. Co zrodziły te roczne i półroczne zmiany w Angularze. Jak działa wersja 9.
Naszą przygodę warto zacząć od plików konfiguracyjnych.
angular.json
W środowisku pracy Angulara jest to jeden z najważniejszych plików. Co w nim jest:
- version : Wersja konfiguracji
- newProjectRoot : ścieżka, w której nowe projekty zostaną utworzone w relacji do foldera głównego. Jak widzisz ścieżka wskazuje na coś czego nie ma. To normalne. W naszym środowisku pracy mamy już dwa projekty Angular w dwóch folderach
- /ClientApp/src/ (aplikacja)
- /ClientApp/e2e/ (testy)
- Nie potrzebujemy definiowania tej wartości.
- Projects : Lista projektów i ich konfigurację
- deafultProject: Projekt domyślny. Gdy będziemy używać komendy Angulara w CMD i nie podajemy nazwy projektu to Angular uzna, że chodzi nam o domyślny projekt.
Na razie niczego tutaj nie musisz zmieniać.
package.json
Serce każdej aplikacji JavaScript plik package.json. Jest to plik konfiguracyjny Node Package Manager (NPM). Zawiera ona listę paczek, które muszą zostać ściągnięte do uruchomienia aplikacji.
Mały wstęp do NPM. NPM zaczął swoje życie jako domyślny menadżer paczek do Node.js. Node.js jest środowiskiem JavaScript do aplikacji back-end i nie tylko. Przez wiele lat NPM stawał się także miejscem hostingowym dla wielu niezależnych projektów JavaScript w tym także Angulara. NPM miał wielu konkurentów, ale po paru latach musiał zostać wybrany jako zwycięzca.
Jeśli nie wiesz co to jest? Proste jest to NuGet dla JavaScript.
Paczki te lądują w folderze /node_modules/ i jest on domyślnie ukryty w Visual Studio. O ile Visual Studio ma swój system do zarządzania paczkami JavaScript to radzę nie kombinować.
Jeżeli chcesz dodać paczkę to albo robisz to przez komendy NPM, albo dopisujesz nazwę paczki do tego pliku w sekcji dependencies.
"dependencies": {
"@angular/animations": "9.1.9",
"@angular/common": "9.1.9",
Visual Studio przy przebudowaniu zrobi za Ciebie resztę. Tylko pamiętaj to może potrwać. Lepiej jest korzystać z poleceń NPM.
Mała informacja co do konfiguracji paczek:
Tylda (~) np. "@types/jasminewd2": "~2.0.8" : Mówi nam, że pobierze każdą wersje 2.0.X i pomnie wersje 2.1.X czy 2.2.X
Kareta (^) np. "bootstrap": "^4.5.0" : Mówi nam by pobierać wszystko poniżej wersji 4.5.0. Czyli wersji 4.6.0 mówimy nie. Dla paczki 4.4.0, jeśli nie istnieje nic wyżej to mówimy tak.
Aktualizacja Angulara do wersji 9
Domyślnie ten szablon korzysta z wersji Angulara 8.3 i mi się to nie podoba. Nie chce czekać pół roku na .NET 5 i na nowy szablon z Angularem 9. Więc aktualizować paczki?
Możesz z chirurgiczną precyzją edytować paczki w dependencies i w devDependencies. O to mój plik po delikatnym dodaniu odpowiednich wersji Angulara. Visual Studio pobierze nowe paczki, gdy zrobisz Rebuild Solution.
{
"name": "showbook",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build:ssr": "ng run ShowBook:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "^3.0.1",
"bootstrap": "^4.5.0",
"core-js": "3.6.1",
"jquery": "^3.5.1",
"oidc-client": "^1.9.1",
"popper.js": "^1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.901.7",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "~3.4.4",
"@types/jasminewd2": "~2.0.8",
"@types/node": "~12.11.6",
"codelyzer": "^5.2.2",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^4.4.1",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "^4.12.0",
"protractor": "~5.4.2",
"ts-node": "~8.4.1",
"tslint": "~5.20.0"
}
}
Jednak gdybym Ci powiedział, że ten proces był bezbolesny to bym skłamał. Dlatego skorzystajmy z polecenia upgrade.
Mając program CMDER (https://cezarywalenciuk.pl/blog/programing/cmder--comand-line-twoich-marzen) wchodzę na folder swojej aplikacji angular i korzystam z polecenia ng update.
Otrzymasz potem tabelkę z poleceniami ng update (paczka) i tak będziesz miał pewność, że masz najnowszego Angulara.
Potem użyj komendy NPM Install by zainstalować jeszcze raz paczki z package.json tak dla pewności.
tsconfig.json
Angular korzysta z TypeScript, a on ma także swój plik konfiguracyjny. Co to jest TypeScript? TypeScript to darmowy wynalazek Microsoft, który działa jako nakładka na język JavaScript.
Oznacza to, że kod JavaScript jest poprawnym kodem TypeScript, ale nie na odwrót. Bo nakładka TypeScript ma dodatkowe funkcję, które JavaScript nie ma. Dodatkowo możesz skompilować TypeScript do określonej wersji języka JavaScript, a raczej jego definicji ECMAScript.
Po co to wszystko? JavaScript w 2020 to niezłe cacko w przeciwieństwie do tego, co było jeszcze 10 lat temu jednakżę ciągle w nim paru rzeczy brakuje. TypeScript oferuje : statyczne typowanie, klasy i interfejsy. Określenie typów w parametrach jak ich granic.
Mając IDE jak Visual Studio masz podpowiadacza kodu. Do dużych projektów TypeScript dba o ład i porządek. Od Angulara 2 kod domyślnie jest pisany w TypeScript.
Aż ciężko sobie wyobrazić dwie drużny z Google i Microsoftu sobie, aż tak słodząc przy tworzeniu Angulara 2 : https://devblogs.microsoft.com/typescript/angular-2-built-on-typescript/
https://vsavkin.com/writing-angular-2-in-typescript-1fa77c78d8e8
Wracają do tego pliku warto coś do niego dodać :
"angularCompilerOptions": {
"strictMetadataEmit": true
}
Dodajemy zachowanie do kompilatora Angulara. Mówimy mu by wyświetlał błędy na ekranie związane z działaniem kompilacji TypeScript . Bez tej linijki na błędy byś musiał patrzeć w pliku .metadata.json.
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
},
"angularCompilerOptions": {
"strictMetadataEmit": true
}
}
Jak zawsze o kompilatorze możesz więcej poczytać tutaj : https://angular.io/guide/aot-compiler
Inne piki:
.editorconfig : plik do konfiguracji miejsca pracy IDE. Używany przez Visual Studio i Visual Studio Code i nie tylko.
.gitignore : Mówi GIT, które pliki ma ignorować przy wrzucaniu do GitHub-a.
README.md : Dokumentacja wstępna. .md jest rozszerzeniem plików MarkDown.
package-lock.json : Przechowuje informację o już zainstalowanych paczkach w folderze /node-modules/.
/node-modules/ : Folder, w którym są wszystkie paczki JavaScript.
tslint.json : TSLint to plik konfiguracyjny używany przez różne IDE jak np. Visual Studio czy Visual Studio Code.
Folder /ClientApp/src/
Pora zobaczyć jak działa nasza aplikacja Angular i zobaczyć pliki źródłowe.
W Folderze /ClientApp/src/app/ znajdują się wszystkie pliki TypeScript powiązane z naszą aplikacją Angular.
W /ClientApp/src/app/assets/ przechowujemy wszystkie zdjęcia, obrazki tak jakby to był folder wwwroot na poziomie .NET.
Folder /ClientApp/src/environments/ przechowujemy konfigurację budowania aplikacji w zależności od środowiska. Plik environment.ts jest domyślnym plikiem, a na przykład na produkcji będzie taki plik environment.prod.ts
Oprócz folderów mamy także pliki :
index.html : Główna strona HTML. Zazwyczaj CLI dodaje za Ciebie wszystkie wskaźniki do plików JavaScript i CSS przy budowaniu aplikacji.
karma.conf.js : Plik konfiguracyjny dla Karma. Karma to narzędzie, które uruchamia testy Jasmine. Na razie możesz to zignorować.
main.ts : Punkt startowy aplikacji. Kompiluje on aplikację i umieszcza siebie w niej jako AppModule, które zostanie uruchomiony przez przeglądarkę.
polyfills.ts : Wsparcie dla skryptów polyfill w przeglądarce
styles.cs : Lista plików CSS załączonych do projektu, jak i idealne miejsce do stylów globalnych
test.ts : Główny punkt wejścia testu jednostkowego
tsconfig.*.json : Konfiguracja dla różnych aspektów aplikacji.
tslint.json : ustawienia TSLint do obecnego projektu
Folder App:
W tym folderze znajduje się logika aplikacji w nim są : modules, services, components jak szablony HTML i style do komponentów.
Jak widzisz każdy kompoment ma swój folder.
AppModule
Aplikacja Angulara wymaga głównego modułu, od którego mogą wystartować inne moduły. Tutaj zaczyna się cykl życia aplikacji Angular. Pozostałe moduły są modułami stroniczymi
Moduł główny App ma referencje do wszystkich modułów. Cykl działania aplikacji wygląda tak.
Najpierw wykonuje się plik main.ts, a on tworzy AppModule, który jest w pliku app.module.ts.
Później on wczyta wszystkie inne komponenty, gdy będą one potrzebne aplikacji. Jeżeli zajrzysz do pliku app.module.ts to zobaczysz tam dużo stwierdzeń import. Mamy tutaj import modułów (module), provider-ów i tak dalej.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { CounterComponent } from './counter/counter.component';
import { FetchDataComponent } from './fetch-data/fetch-data.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
CounterComponent,
FetchDataComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Strona serwera Angulara
Tak Angular ma swój własny serwer i jego definicja startu zaczyna się w folderze /ClientApp/src/app/ w pliku app.server.module.ts.
Uruchamia on Universal Server-Side Rendering (SSR). Jest to technologia renderująca aplikację Angular . .NET Core wspiera jego działanie więc mówimy tutaj ciągle o tym samym serwerze.
Uruchomienie aplikacji więc tak naprawdę wygląda tak:
Nie musisz wnikać jak renderowanie działa.
AppComponent
Angular jest zbudowany z cegieł. Każdy cegła jest komponentem (Component). A sam Angular jest jakby drzewem tych cegieł. Tak chyba ta analogia nie za bardzo wyszła.
Component (będę się trzymał angielskiej nazwy) definiuje swój widok. Ten widok będziemy modyfikować w kodzie TypeScript przy pomocy logiki, danych z serwisów i tak dalej.
Service Providers, czyli wszelakie usługi, narzędzia mogą wstrzyknięte do Component i daje to elastyczność kodu.
Konwencja na mówi by każdy Component miał swój oddzielny folder i swoją przestrzeń nazw. Wyjątkiem jest AppComponent, który służy za główny Component do wyświetlania innych. Jest to główny węzeł drzewa tych komponentów.
Składa się on z dwóch plików. app.component.ts zawiera logikę . Natomiast app.component.html zawiera główny widok strony i layout. Gdybyś chciał dodać element, który będzie się pojawiał w każdym widoku to lepiej jest go dodać tutaj. Daje Ci też możliwość umieszczenia różnych Components na stronie.
<body>
<app-nav-menu></app-nav-menu>
<div class="container">
<router-outlet></router-outlet>
</div>
</body>
app-nav-menu odnosi się do Component-u nav-menu. Natomiast router-outlet wyświetli odpowiedni Component w zależności od adresu strony.
Jak widzisz nie zawiera on jednak innych plików. Nie mamy tutaj pliku CSS <*>.component.css czy dedykowanego testu jednostkowego w pliku <*>.component.spec.ts
Inne Komponenty
Mamy CounterComopnent, FetchDataComponent , HomeComponent, NavMenuComponent
Niektóre z nich mają swoje testy. Chodzi o plik counter.component.spec.ts
Przetestujmy ten szablon teraz, bo hej udało się i mam nadzieje, że dotarłeś do tego momentu artykułu .Omówiliśmy cały szablon Angular w ASP.NET CORE.
Wciskamy F5 i do dzieła.
W zależności od swojego wyboru na pasku nawigacyjnym znajdziesz się na innym widoku. Jak odkryliśmy inny widok to też innym Compoment
NavCompoment
NavCompoment jest sub-Component-em . W końcu to renderuje inne komponenty w zależności od ścieżki. Component korzysta z modułu RouterModule. Jego definicja znajduje się w głównym module aplikacji.
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
])
Gdybyś więc chciał dodać nowy widok, czyli komponent to w tym miejscu byś zadeklarował, pod jaką ścieżką on miałby się wywołać.
Później w widoku nav-menu.component.html dodałbyś kolejny element do listy:
<ul class="navbar-nav flex-grow">
<li
class="nav-item"
[routerLinkActive]="['link-active']"
[routerLinkActiveOptions]="{ exact: true }"
>
<a class="nav-link text-dark" [routerLink]="['/']">Home</a>
</li>
<li class="nav-item" [routerLinkActive]="['link-active']">
<a class="nav-link text-dark" [routerLink]="['/counter']"
>Counter</a
>
</li>
<li class="nav-item" [routerLinkActive]="['link-active']">
<a class="nav-link text-dark" [routerLink]="['/fetch-data']"
>Fetch data</a
>
</li>
</ul>
ConuterComponent i FetchDataComponent
Oto dwa komponenty pokazujące prostą logikę przycisku i zapytania do ASP.NET CORE o dane JSON. Pamiętasz pod adresem https://localhost:44382/WeatherForecast mamy API RESTOWE, które zwraca losowe dane pogodowe.
Angular tego JSON potem układa w piękną tabelkę.
Odpalmy test jednostkowy
Otwórz CMD i nawiguj do ścieżki ClientApp i wpis następujące polecenie :
npm run ng test
Uruchomi ci się przeglądarka z konsolą Karma i pokaże Ci liczbę wykonanych testów.
To było na tyle. Pozostało na skasować te domyślne komponenty w szablonie i zbudować własne.
Do zobaczenia w następnym wpisie, w którym wyświetlę kolekcję książek.