Angular2’yi Mvc Üzerine Çalıştırma ve Routing, Http Service, Input, Output ve Custom Pipe
Selamlar,
Bugün Asp.Net Mvc üzerinde Angular2 projesi nasıl yaratılır? Angular2’de bir sevisden data nasıl çekilir? Routing nasıl yapılır? Ve son olarak çekilen bu datanın, farklı bir directive’de gösterimi nasıl olur gibi konularını hep beraber inceleyeceğiz. Bu gösterme işini ilgili provider ve @input, @output parametrelerini de kullanarak, nasıl tekrarlanabilir koddan kurtulunabileceğini ve her yerde çalışabilecek farklı directiveleri de yine bu makalede incelemiş olacağız.
Öncelikle yeni bir MVC projesi oluşturulur. Oluşturulacak proje template tipinin makinanızda olmadığı farz edilip, Visual Studio 2015’de Online Project Type sekmesinden angular diye aranıp, “Angular2WebTemplate” seçilir. Ve ilgili Angular2 projesi aşağıdaki gibi oluşturulur.
Daha sonra ilgili solution sağ tıklanıp “Controllers” folder’ı eklenir. Ve daha sonra içerisine sağ click ile “Controller” yani boş bir HomeController eklenir. Ayrıca otomatik oluşan Index action’ı sağ tıklanıp, boş bir Index view oluşturulur. Projenin son durumu yandaki gibidir. Dikkat edilir ise “app” klasörü altına, tüm Angular 2 için gerekli yapılar oluşturulmuştur.Burada başlangıç noktamız “app.modules.ts“‘dir.
Ayrıca ilgili tüm paketlerin indirilmesi için, admin ile açılmış bir command propmt’da, oluşturulan projenin folder’ı altına gelinip, “npm install” komutu yazılır. Ayrıca var olan paketlerin son versionlarına yükseltilmesi için, aşağıdaki adresden de görüldüğü gibi “npm install -g npm-check-updates” şeklinde ilgili paket indirilir. Ve “ncu -u” komutu ile tüm paketler son güncel haline getirilir.
npm-check-updates
app.modules.ts’in son hali aşağıdaki gibidir: Aşağıdaki kodlara bakıp aklınız karışmasın, projenin son halini görmektesiniz. Yani yazılmış, bitmiş olan tüm projenin son halidir. Dikkat ederseniz tüm oluşturulan classlar burada importlu olarak tanımlanmıştır.
@NgModule: Angular2 için bir kabuktur. Yani bu kabuk içinde angular ayağa kalkmaktadır.
imports:
- BrowserModule: İle anguların web ortamında çalışacağı tanımlanır. Bildiğiniz gibi Mobilede de çalıştırılabilmektedir.
- routing: Bir menu mantığının kullanılması ve çeşitli Componentlare gidilebilmesi için tanımlanır.
- HttpModule: Bir Get, Post yani request, response işlerinin yapılması için tanımlanır.
declarations: Tüm componentlar ve pipe eski adı ile filterlar burada tanımlanır.
bootstrap: Başlangıç component’ı belirlenir. Bu projede “AppComponent”‘dır.
providers: Proje içindeki tüm servisler burada tanımlanır. Bu projede sadece bir service vardır. O da “PersonService”‘dir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { NgModule} from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { PersonService } from './personService'; import { PersonComponent } from './person.component'; import { AppComponent } from './app.component'; import { Dashboard } from './dashboard/dashboard.component' import { PersonalCabinet } from './personalCabinet/personalCabinet.component' import { Hello } from './hello.component' import { ListPersonComponent } from './listperson.component' import { CapitalPipe } from './capital.pipe' import { HttpModule } from '@angular/http'; import { routing } from './app.routing'; @NgModule({ imports: [BrowserModule, routing, HttpModule], declarations: [AppComponent, Dashboard, PersonalCabinet , Hello, ListPersonComponent, PersonComponent, CapitalPipe], bootstrap: [AppComponent], providers: [PersonService] }) export class AppModule {} |
Routing: Yukarıda dikkat ederseniz routing bir dosya içinde tanımlanmıştır: “import { routing } from ‘./app.routing’;”
app.routing.ts: Aşağıda görüldüğü gibi url satırına girilecek key yani adres satırında olacak link “path” kısmında tanımlanmıştır. İlgili url’e göre gidilecek sayfaya ait sınıfın adı “component” tarafında tanımlanmıştır. Gidilecek tüm componentlar yine bu dosya altında import ile tanımlanmıştır. Ayrıca route ve providerlar için de ilgili modulelerin tanımlanması unutulmamalıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import { ModuleWithProviders } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { Dashboard } from './dashboard/dashboard.component' import { PersonalCabinet } from './personalCabinet/personalCabinet.component' import { Hello } from './hello.component' import { ListPersonComponent } from './listperson.component' const appRoutes: Routes = [ { path: '', component: Dashboard }, { path: 'personal', component: PersonalCabinet }, { path: 'hello', component: Hello }, { path: 'listperson', component: ListPersonComponent }, { path: '**', // otherwise route. component: Dashboard } ]; export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes); |
AppComponent: Başlangış sayfası aşağıda görülen sınıftır. Index sayfasında selector “<my-app><my-app/>” şeklinde tanımlanmıştır. Ilgili html sayfası “templateUrl” sayesinde belirlenmiştir. styleUrls ile de ilgili sayfada kullanılacak css dosyası yolu gösterilmiştir. Hatırlarsanız templateUrl yerine, bir template yapısı da kullanabilir ve ilgili html’i buraya string olarak tanımlayabilirdik. Son olarak sayfanın title’ı ilgili sınıf altında tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { Component } from '@angular/core'; import { OnInit } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: '/app/app.component.template.html', styleUrls: ['app/app.component.css'], }) export class AppComponent { title = 'My Blog Angular 2 App' } |
app.component.template.html: Aşağıda görüldüğü gibi bir Menu ve benim de başta bunun nerden geldiğini anlamlandıramadığım, “@angular/core”‘dan gelen “<router-outlet></router-outlet>” directive’i konulmuştur.
Not: Dikkat edilir ise, artık ilgili classlarda Directiveler tanımlanmamaktadır.
1 2 3 4 5 6 7 8 |
<h1>{{title}}</h1> <nav> <a routerLink="/">Dashboard</a> <a routerLink="/hello">Hello</a> <a routerLink="/listperson">ListPerson</a> <span class="crayon-o"><</span><span class="crayon-i">a</span> <span class="crayon-v">routerLink</span><span class="crayon-o">=</span><span class="crayon-s">"/personal"</span><span class="crayon-o">></span><span class="crayon-v">PersonalCabinet</span><span class="crayon-o"><</span><span class="crayon-o">/</span><span class="crayon-v">a</span><span class="crayon-o">></span> </nav> <router-outlet></router-outlet> |
HomeController/Index: İlgili action’da hiçbir kod tanımlı değildir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Angular2WebTemplate1.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } } |
Views/Home/Index: Aşağıda görüldüğü gibi ilgili Angular2 templatin’den gelen script dosyaları ve AppComponent selector’ü olan “<my-app><my-app/>” sayfa içerisinde tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Index</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <base href="/"> <link rel="stylesheet" href="styles.css"> <title>A2 template</title> <!-- 1. Load libraries --> <!-- Polyfill(s) for older browsers --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.js"></script> <script src="node_modules/reflect-metadata/Reflect.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <!-- 2. Configure SystemJS --> <script src="systemjs.config.js"></script> <script> System.import('app').catch(function (err) { console.error(err); }); </script> </head> <body> <my-app>Loading...</my-app> </body> </html> |
Bu makalede üzerinde çalışılacak sayfa “ListPersonComponent” olucaktır. Öncelikle ilgili kişilerin çekileceği bir “person.json” dosyası oluşturulur.
person.json: Aşağıda görüldüğü gibi “id” ve “name” alanlarının bulunduğu bir data kümesi vardır. Amaç bu öğrencileri bir ekranda listelemek ve istenen kişinin seçilebilmesidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
[ { "id": 1, "name": "bora kaşmer" }, { "id": 2, "name": "engin polat" }, { "id": 3, "name": "seçil kaşmer" }, { "id": 4, "name": "duru kaşmer" }, { "id": 5, "name": "ada polat" }, { "id": 6, "name": "ramiz köfte" }, { "id": 7, "name": "fırında sütlaç " }, { "id": 8, "name": "patlıcan oturtma" }, { "id": 9, "name": "sütlü nuriye" } ] |
Şimdi öncelikle bir servis yazalım. İlgili Json dosyasını okusun ve bir property olarak dönsün.
PersonService: Aşağıda görüldüğü gibi ilgili provider’ın, başka bir sınıfın constructer’ında çağrılıp kullanılabilmesi için “@Injectable()” olarak tanımlanması gerekmektedir. Ayrıca “http.request” yapılabilmesi için ilgili kütüpahanelerin sayfanın başında import edilmesi gerekmektedir. “People” adında bir property tanımlanmıştır. İlgili request işleminden sonra, “subscribe()” methodu içinde lambda kullanılarak dönen response.json() “People” property’sine atanmaktadır. Ayrıca hata durumu “err=>console.error(err)” örnek amaçlı console’a yazılmaktadır. İlgili provider’ın kullanılabilmesi için makalenin başında da anlatıldığı gibi ilgili tüm fileların “app.modules.ts“‘de tanımlanması gerekmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; @Injectable() export class PersonService { private _people: JSON; get People() { return this._people; } set People(thePeople) { this._people = thePeople; } constructor(private http: Http) { this.http.request('./app/person.json') .subscribe(response => this.People = response.json(), err => console.error(err), () => console.log("OK")); } } |
Şimdi gelin ilgili servisi, yaratılma anında constructer’ında alıp, listeleme işlemi yapacak sayfayı tanımlayalım:
ListPersonComponent(Part 1): Aşağıda görüldüğü gibi yukarıda tanımlanan provider yani services modules’de tanımlandığı için, bir daha burada provider olarak tanımlanmamış sadece dosya yolu sayfanın tepesinde belirtilmiştir. “ModuleWithProviders” librarysi bu yüzden kullanılmıştır. Aşağıdaki component’de “Service” değişkenine constructer’dan dönen “@Injectable()” “PersonService“‘ı atanmıştır.
Önemli Not: Constructer’da service yerine, ilgili service’e ait json “People” propertysi doğrudan çekilmeye çalışıldığında, çekilmeye çalışılan bu datanın gelmediği, “null” döndüğü görülmüştür. Bunun için öncelikle service nesnesinin kendisi çekilmiştir. Nedenine gelince, constructer işlemi sırasında ilgili datanın dönmesi beklenmemektedir. Bu sorun ilgili servisden data, promises ile asenkron olarak çekilir ise çözülme ihtimali yüksektir. Aşağıda görüldüğü gibi henüz bir sıralama işlemi yapılmamıştır. Ben bu işlem için bir başka directive kullanmayı doğru buldum. Böylece ilgili sıralama, projenin başka yerlerinde de kullanılabilecektir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import { ModuleWithProviders, Component } from '@angular/core'; import { PersonService } from './personService'; @Component({ selector: 'listperson', template: `<h2>All Person List</h2><div></div> Listelme işlemi yapılacak! ` }) export class ListPersonComponent { Service constructor(public personService: PersonService) { this.Service = personService; } } |
PersonComponent: İlgili listelemenin yapılacağı directive aşağıdaki gibidir. Burada @Input ve @Output kullanılmıştır. @Input: Servisden dönen herbir person nesnesinine ait ismi almaktadır. @Output: Bu list directivine dışardan bağlanan eventi tetiklemek için kullanılır. Yani bu nesnenin kullanıldığı sayfa üzerindeki event’i tetikler. Parametre olarak dışarı, kendisine atanan bu “name” değişkenini gönderir. Selector: “<list></list>“‘dir. Template olarak bir <span> içine ilgili isim ve yanına görüşme button’u konulmuştur.
Not: Button’un tıklanma eventinde “getChat()” methodu çağrılmış ve bu method sırasında yeni yaratılan “EventEmitter” ile dışarıda kendisine (talk) eventi ile bağlı method, tıklanan isim ile birlikte çağrılmıştır. Kısaca dışarı bir iletişim söz konusudur. (@Output)
Önemli bir nokta da aşağaıda görüldüğü gibi ilgili isimlerin baş harfleri büyük yazılmıştır. Bunu custom Pipe ile yapmaktayız. “{{name | capital}}” burada yazılan capital eski adı ile bir filter yeni adı ile Angular2’de bir pipedır. Amacı ismi boşluklarına göre bölmek. Ve herbir kelimenin baş harfini büyütmektir. Bu örnekteki sözkonusu durum adın ve soyadın büyük harf ile yazılmasıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import { Component, Input, Output, EventEmitter } from '@angular/core' import { CapitalPipe } from './capital.pipe' @Component({ selector: 'list', template: ` <div> <span>{{name | capital}}</span> <button (click)="getChat()">Lets Talk</button> </div> ` }) export class PersonComponent { @Input('name') name; @Output('talk') talk = new EventEmitter(); getChat(e) { this.talk.next(this.name); } } |
CapitalPipe: Yukarıda bahsedilen pipe aşağıdaki gibi kodlanmıştır. Görüldüğü gibi regex ile boşluklarına göre ayrılan herbir kelimenin ilk harfi büyük harf ile değiştirilmiş ve geri dönülmüştür.
1 2 3 4 5 6 7 8 9 10 11 |
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'capital' }) export class CapitalPipe implements PipeTransform { transform(value: string, args: string[]): any { if (!value) return value; return value.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); } } |
ListPersonComponent(Full): Aşağıda görüldüğü gibi “<List>” directive’i “Service.People“‘daki eleman sayısı kadar “*ngFor” ile dönülmüş ve “[name]” @Input elemanına “person.name” değeri atanmıştır. Ayrıca (talk)@Output EventEmitter’a “chatPerson()” methodu bağlanımıştır. İlgili method’da “doneList[]” dizisine tıklanan isim ve state durumu “completed” şeklinde eklenmektedir. “.completed” style’ı aşağıda görüldüğü gibi tanımlanmıştır. Kısaca ilgili isimin üstü çizilmektedir. Yani görüşüldü anlamına gelmektedir.
En altta ise yani “Kayıt Yaptırılan Öğrenciler” kısmında, ilgili listeye eklenen, yani görüşülen isimler “<li *ngFor” ile dönülüp listelenmektedir. İlgili listenin içine basılan span’a “<span [ngClass=’person.state’]” şeklinde ilgili “person.state” değeri sitil olarak atanmıştır. Ayrıca ilgili span’ın yanına konan “Kaldır” button’unun “(click)” event’ine “removeFromList()” methodu bağlanmıştır. İlgili button tıklanınca “doneList[]” içerisinden, ilgili öğrenci çıkartılar ve böylece tıklanan kişi kayıt yaptıran öğrenciler listesinden kaldırılmış olunur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import { ModuleWithProviders, Component } from '@angular/core'; import { PersonService } from './personService'; import { PersonComponent } from './person.component'; @Component({ selector: 'listperson', template: `<h2>All Person List</h2><div></div> <list *ngFor="let person of Service.People" [name]="person.name" (talk)="chatPerson($event)"> </list><br> <b>Kayıt Yaptıran Öğrenciler:</b><br> <style> .completed{ text-decoration:line-through; } </style> <ul> <li *ngFor="let person of doneList"> <span [ngClass]="person.state">{{person.name}}</span><button type="button" (click)=removeFromList(person)>Kaldır</button> </li> </ul> ` }) export class ListPersonComponent { //persons; Service doneList=[]; constructor(public personService: PersonService) { this.Service = personService; } chatPerson($event) { var person = { name: $event, state: 'completed' } this.doneList.push(person); console.log(`You said hello to ${$event}`) } removeFromList(person) { this.doneList.splice(this.doneList.indexOf(person), 1); } } |
Bu makalede önce Angular2’yi Mvc bir projede beraberce nasıl kullanacağımızı inceledik. Daha sonra Angular2’de Routing konusuna ve ilgili paketleri neden app.modules’e eklememiz gerektiğini gördük. Daha sonra “Json” bir dosyadan nasıl data okunur, okunan dosya başka bir directive’de @Input kullanılarak listelenir. Directive’den dışarı doğru @Output kullanılarak nasıl bir event tetiklenir gördük. Son olarak bir html element’in class’ına nasıl model ile atama yapacağımızı gördük. Ve böylece bu makalenin de sonuna geldik :)
Yeni bir makalede görüşmek üzere hoşçakalın.
Kaynak : https://github.com/Drag13/MvcWithAngular2/tree/master/WebProject
Bora Bey
HomeController da olan bir veriyi nasıl alacağız ?
httpGet yaparak servis veriyi controller üzerinden alabiliyormu ?
Bora Bey
HomeController da olan bir veriyi nasıl alacağız ?
httpGet yaparak servis veriyi controller üzerinden alabiliyormu ?
indirdiğimiz template de sanki en dışdaki index.html çalışıyor index.cshtml calışmıyor sanki ?
Selamlar,
Bende uygulama Mvc şeklinde yani Index.cshtml sayfası ile start oluyor. Yani kısaca bir sorun olmaması lazım. Olmadı bendeki projeyi GitHub’a koyup paylaşırım.
HomeController’daki veriyi Model ile ViewBag,ViewData ile veya clientSide taraftan AjaxPost ile alabilirsiniz. Daha farklı bir sürü yol var ama ilk etapta bunlar yeterli gibi..
İyi çalışmalar.
Visual Studio 2017 için kullandığınız proje template tipi mevcut değil galiba. Dışarıdan elle indirdiğimde ise uyumsuz olduğu için yüklenmiyor. Acaba VS 17 için nasıl bir çözüm üretebiliriz?
Sanırım şuan Visual Studio 2017 için yok. Nedeni sanırım IDE’nin halen RC durumunda olmasından kaynaklanıyor.
İyi çalışmalar.
İyi günler bora bey.
Daha önce bir yazınızda sormuştum tekrar olacak.
Yaptığınız örneğinizde ilgili adrese( örnek olarak /listperson) direk tarayıcıdan set ederek girdiğimizde listeleme sayfası çıkıyormu?
Selamlar,
Url’e bu şekilde yazarsan EVEt gelir: http://localhost:49863/listperson
İyi çalışmalar.
Teşekkürler.
izniniz ile bir şey daha sormak istiyorum.
RouterModule.forRoot(appRoutes); kodunda ki forRoot ve aynı nesnenin forChild fonksiyonları tam olarak ne işlevi yerine gertiriyor ?
Orada forChild fonksiyonunu hangi durumlarda kullanmalıyız?
Selam Süleyman,
Öncelikle sorun çok güzel:) Soru gibi soru :)
Router yapısında bir başlangıç noktan olmak zorunda. İşte bu başlangıçtaki route yollarını forRoot ile tanımlıyorsun. Yani proje ilk başlarken bu maim moduleler yükleniyor. Bir de başta yüklenmeyen submoduleler, lazy load moduler gibi yapılar var. İşte bunları da routing için register etmek istediğinde forChild’ı kullanıyorsun. Yoksa bu tip yapıları forRootda tanımlasan başlangıçta direk hatayı alırsın.
Umarım açıklayıcı olmuştur.
İyi çalışmalar.
Hocam merhabalr , angular2webtemplate oluşturunca bu sizin attiginiz yapiya ulasamadim
Sadece angular2 nin klasotleri geldi mvc ye ait olanlar gelmedi asp .net core olmadan olmuyor mu .
Teşekkür ederim
Selam Emre,
Eğere makaleyi okursan, ilgili template yüklendiği zaman Mvc ile alakalı hiçbir dosyanın oluşmadığı sadece ilgili kütüpahanelerin yüklendiğini görebilirsin.
Bunu da makalede geçen “Daha sonra ilgili solution sağ tıklanıp “Controllers” folder’ı eklenir. Ve daha sonra içerisine sağ click ile “Controller” yani boş bir HomeController eklenir. Ayrıca otomatik oluşan Index action’ı sağ tıklanıp, boş bir Index view oluşturulur.” çümlesinden çıkarabilirsin.
Bu ara da Asp.Net Core, bu makalede uzaktan yakından bahsedilmemiştir.
İyi çalışmalar.
routing işlemi bile angularjs ile yapılınca mvc projesi içinde kullanılmasında çokta bi önemi olmuyormuş gibi anladım.
web api + html angularjs uygulama yapılsa daha iyi sanki, en azından derleme işlemini bi miktar aradan çıkarmış oluruz diye düşündüm.
Yani Mvc’nin çok bir espirisi olmuyor Angular2’de diyebilirim.
http://prntscr.com/e7uha1
http://prntscr.com/e7uhfh
http://prntscr.com/e7uhq1
http://prntscr.com/e7uhvq
Hocam angular2 ile bir web apiden liste halinde gönderdiğim veriyi çekebiliyorum ancak bu şekilde id ye göre olunca undefined diyor nerde hata yapoyrum teşekkür ederim
Selam Emre,
GetMacDetay()’ı bir Button’a tıklanınca çekiyorsan sync yapıp bir çek:) Eğer Constructer’da çağrıyor isen çağırma ve Propery ile çağır.
Veya Delay(1000) koyup bir dene.
İyi çalışmalar.
selamlar, örnekteki uygulamanın projesini github’ta paylaşabilir misiniz?
Selamlar,
Yakın zamanda paylaşırım.
İyi çalışmalar.
Makale için teşekkürler.
TypeScript for Visual Studio 2015 ‘in kurulması gerektiğinden bahsetmemişsiniz hocam. Bu olmayınca derleyince hatalar oluşuyor.
Teşekkürler Rahman,
Sanırım bende önceden kuruludur. Bundan dolayı fark etmediğim için yazıda da bahsetmemişimdir.
Uyarı için teşekkürler.
İyi çalışmalar.
600 dene hata oldu..ahanda link sana katkı olsun
http://www.typescriptlang.org/index.html#download-links
Teşekkürler Hocam , Şuanda varolan bir asp.net mvc projemize angular4 eklemek istiyoruz nasıl bir yol izlememiz gerekiyor