Merhaba,

Spring Boot ve AngularJS ile uygulama geliştirme yazısının ikinci adımındasınız. İlk yazı için linki tıklayabilirsiniz.

Spring Boot Projesine Statik Kaynak Ekleme

Yazının bu adımındaki kütüphaneleri anlatmaya geçmeden önce, Spring Boot ile statik kaynakları nasıl sunacağımza bir göz atalım. Spring Boot projenizdeki statik kaynakları, /static, /public, /resources veya /META-INF/resources klasörlerine kopyalayarak istemci tarafından erişilmelerine izin verebilirsiniz Kaynak.

Örnek olarak uygulamamızın ana sayfasını ekleyelim, projenizin src/main/resources klasörü altına, public klasörü ekleyip, bu klasör altına index.html dosyası ekleyip, içeriğine aşağıdakileri yazalım.

<!DOCTYPE html>  
<html>  
<body>  
    Hello world from static content...
</body>  
</html>  

Şimdi projemizi çalıştırıp, http://localhost:8080/ adresine gidelim. Aşağıdaki sayfanın bizi karşıladığını göreceksiniz.

Ekran Görüntüsü

Projemizin bundan sonraki adımlarında kullanacağımız ve istemci tarafını etkileyecek dosyaları src/main/resources/public klasörü altından sunabileceğiz.

AngularJS

Şimdi uygulamamıza ön yüz kütüphaneleri ekleyeceğiz. Bunlardan AngularJS ile başlayalım. AngularJS kökeni GWT'ye uzanan bir Web Uygulama Geliştirme Framework'ü. Web uygulamarında ön yüz iş mantığını yazan ekipler tarafından, daha kullanışlı bir katman olması için geliştirilmiştir. AngularJS HTML etiketleri içerisine, yine HTML etiketlerine benzer etiket veya bu etiketlere ait özellikler ekleyerek kontrol etmenize olanak sağlıyor.

Gelin uygulamamıza AngularJS'in ana kütüphanesini ekleyelim. AngularJS'in websitesine gidip, CDN adresini https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js alıp, az önce projemize eklediğimiz index.html dosyasının <head> etiketleri arasına ekleyelim.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"  
            type="text/javascript"></script>

Daha sonra sayfanın AngularJS tarafından yönetildiğini söyleyebilmemiz için (bir başka deyişle sayfanın bir AngularJS uygulaması olduğunu belirtebilmemiz için), html etiketi içerisine, ng-app özelliğini boş olarak ekliyoruz. En basit özelliklerden biri olan HTML şablonları (template) ve ng-model bağlayıcısını (binding) kullanacağız. Tüm bunların ardından, index.html sayfamız aşağıdaki gibi olacaktır.

<!DOCTYPE html>  
<html ng-app>  
<head>  
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"
            type="text/javascript"></script>
</head>  
<body>  
    <div>
        <label>İsminiz:</label>
        <input type="text" ng-model="myName" />
    </div>

    <h2>Merhaba; {{myName}}</h2>

</body>  
</html>  

Burada dikkat etmemiz gereken birinci nokta, {{}} parantezleri arasına yazılan değişken. AngularJS'de HTML dosyasına şablon oluşturmak için, uygulamanızdaki değişkenleri yazdırmak istediğinizde, yukarıdaki çift süslü parantez notasyonunu kullanabilirsiniz. Buradaki örnekte, myName bir değişken ve o değişken <h2> etiketi içerisinde yazdırılmaktadır. Dikkat ederseniz, myName değişkeni bir yerde daha geçmekte, o da <input> etiketi içerisi. Burada ise, input etiketi içerisindeki ng-model özelliği içerisine yazılmış durumda. ng-model sizin uygulamanızdaki değişkenleri ile HTML Form nesneleri arasındaki bağı kurmak için kullanılıyor. Mesela buradaki örnekte olduğu üzere, <input> form nesnesinin değeri, uygulamadaki myName değişkenine bağlı.

Şimdi sayfayı yenilediğimizde aşağıdaki ekran görüntüsünü alacağız;

İlk Angular Ekran Görüntüsü

Sayfada dikkat edeceğiniz ilk özellik, siz form'a yazmaya başlar başlamaz sayfa içerisinin değiştiğini göreceksiniz. İşte burada AngularJS'in gücünü görmeye başlıyoruz :) Aynı sayfayı jQuery ile aynı yapıda yapmak istesek, input etiketine bir değişiklik dinleyici ekleyip, her değişiklik yaşandığında, h2 etiketi içeriğini değiştirmek olabilirdi. Gördüğünüz üzere basit bir~iki HTML etiketi ile çözülebilen bir iş için, kontrol mantığı yazmak gerekecekti. Şimdi bu gücü şöyle düşünün. Elinizde dizi var ve siz diziyi sayfada ul, li etiketleri ile gösteriyorsunuz. AngularJS ile, diziniz her değiştiğinde sayfadaki 'ul' listesi de otomatik olarak güncellenecek. Bu örneği ileride sayfalarda listeleme yaparken göreceğiz.

Yönlendirme (Routing) Ekleme

Uygulamamız tabi ki her zaman, değişken göster ve değişkenin değerini değiştir şeklinde olmayacak. Değişken değeri sunucudan okunacak, sonra tekrar sunucuya yazılacak. Sunucudan gelecek verilere göre sayfa değişecek vs. vs. Bütün bunları kontrol edebilmemiz için her sayfaya Controller sınıfları yazabileceğiz.

Yine aynı şekilde uygulamamız tek bir durum'dan (state) oluşmayacak. Bazı ekranlarda listeleme yapıp, bir sonraki ekranda değişiklik yapabileceğiz. Tüm ekranların da kendilerine ait Controller sınıfları olacak ve her ekran kendi iş kontrolünü düzenleyecek.

Tüm bunları yapabilmek için AngularJS kendine ait yönlendirme (routing) kütüphanesine sahip. Ancak bu kütüphane bazı noktalarda yetersiz kalabiliyor. Biz onun yerine UI Router eklentisini kullanacağız. UI Router eklentisi, normal yönlendirme eklentisinin yapamadığı durum hiyerarşisi oluşturabilme kapasitesine sahip. Yani tüm uygulamanız bir ağaç yapısında tasarlayıp, bu ağacın her bir dalını bir durum olarak tanımlayabilirsiniz. Alt durumlar üst durumları bire bir aynısı olup, üst durumun bir kısmında çizdirilecektir. Şimdi UI Router eklentisini sayfamıza ekleyelim. Sayfamızın head etiketi arasına aşağıdaki satırları ekleyelim.

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"  
            type="text/javascript"></script>

Şimdi uygulamamızda bazı şeyler ciddiye binmeye başladığı için, klasörleme yapımızda bazı değişiklikler yapacağız. index.html dosyasını eklediğimiz klasöre, js isminde bir klasör daha ekleyip, içerisine app.js dosyası ekleyin. Yeni dosyamızın içerisini doldurmaya başlamadan önce bu dosyayı index.html sayfamızdan çağıralım.

<script src="js/app.js" type="text/javascript"></script>  

Şimdi app.js dosyasını açıp ilk modülümüzü yazmaya başlayalım.

var myModuleConfig = function($stateProvider, $urlRouterProvider){

    // herhangi bir eşleşme olmaması halinde,
    //    hangi sayfaya gideceği durumunu belirtir
    $urlRouterProvider.otherwise("/");

    // bu satırdan itibaren, uygulamadaki
    //    durumları tanımlamaya başlıyoruz
    $stateProvider.state("home", {
        // adres çubuğunda görünecek yolun tanımı
        url: "/",
        // bu durumun sayfa olarak gösterim dosyasını işaret eder
        templateUrl: "/views/home.html"
    });

};
// yukarıda fonksiyon tanımında kullandığımız servis vs objelerinin
//    angular içerisinden buraya inject edilmesini sağlar
myModuleConfig.$inject = ["$stateProvider", "$urlRouterProvider"];

// angular'daki modül tanımı aşağıdaki gibidir, myFirstAngularModule modül adıdır.
//    modül adından sonraki dizi ise, modülün bağımlılıklarını belirtir
//    bizim sadece UI Router bağımlılığımız mevcut olduğu için onu belirtiyoruz
angular.module("myFirstAngularModule", ["ui.router"])  
    // yukarıda yazdığımız config sınıfını modülümüze uyguluyoruz
    .config(myModuleConfig);

İlk iş mantığımızı oluşturmaya başladık. Şimdi index.html dosyasını tekrar açıp, ng-app özelliğini aşağıdaki gibi değiştirelim. Aşağıdaki değişiklik ile, bu sayfayı, myFirstAngularModule isimli modül yönetir diyoruz.

<html ng-app="myFirstAngularModule">  

Daha sonra sayfa içerisine yazdığımız, kod parçacığını alıp, js klasörünün yanına views klasör oluşturup, home.html dosyası içeriğine yazalım.

<!-- views/home.html -->  
<div>  
    <label>İsminiz:</label>
    <input type="text" ng-model="myName" />
</div>  
<h2>Merhaba; {{myName}}</h2>  

index.html dosyasındaki body etiketi içerisini de aşağıdaki gibi değiştirelim. Bu değişiklik, ana yönlendirme durumunun içeriğinin, sayfanın hangi alanına çizdirileceği belirtilmiş olur.

<div ui-view></div>  

Şimdi tüm değişiklikleri kaydedip, tekrar http://localhost:8080/ adresine gidelim.

UI Router'lı Hali

Az önceki sayfamız bire bir çalışmakta, ancak dikkat ederseniz, adres çubuğunun sonuna #/ eklenmiş durumda. Bu da UI Router kütüphanesinin uygulamazı eklendiğini ve şu an / ile gösterilen ve ismi home olan durumu görüntülemekteyiz.

Ön Yüz Kontrolü Ekleme

Şimdi sayfamıza biraz da iş mantığı ekleyelim. Bu sayfamızı / durumumuzu kontrol edecek kodumuzu, Controller'ı yazalım.

// sayfamızı yönetecek kod parçacığı
// $scope servisi, sayfanızdaki değişkenlere, 
//     controller üzerinden erişmenizi sağlar
//     o yüzden ön yüzden erişilmesi istenen tüm değişkenleri
//     $scope altnda tanımlamanız gerekir
var myHomeController = function($scope){  
    // name adında bir değişken tanımlıyoruz, 
    //   bunu input'a model olarak tanımlayacağız
    $scope.name = "";

    // listelemede göstereceğimiz bütün isimlerin listesi
    $scope.names = [];

    // isim listesine isim ekleme fonksiyonu
    $scope.addName = function() {
        $scope.names.push($scope.name);
        $scope.name = "";
    }
};
myHomeController.$inject = ["$scope"];

...
...

angular.module("myFirstAngularModule", ["ui.router"])  
    .config(myModuleConfig)
    // myHomeController controller nesnesini kaydediyoruz
    .controller("myHomeController", myHomeController);

Yazdığımız Controller'ı durumumuz ile bağlıyoruz.

$stateProvider.state("home", {
        url: "/",
        templateUrl: "/views/home.html",
        // durumu kontrol eden kod parçaçığı
        controller: "myHomeController"
    });

Son olarak sayfamızı (home.html), Controller sınıfındaki değişken ve aksiyonlara göre yeniden düzenliyoruz.

<!-- views/home.html -->

<div>  
    <label>İsminiz:</label>
    <input type="text" ng-model="name" />
    <button type="button" ng-click="addName()">Kaydet</button>
</div>

<ul>  
    <!-- ng-repeat, bir dizi değişkeni üzerinde dönmenize olanak sağlar
         burada olduğu üzere, isimler üzerinde dönüp onu liste
         olarak yazdırmamızı sağlıyor -->
    <li ng-repeat="selectedName in names">
        {{selectedName}}
    </li>
</ul>  

Tüm bunları kaydedip, sunucuyu tekrar başlattığımızda, karşımıza aşağıdaki ekran gelecektir.

Listeye eleman ekleme

Siz her ekle butonuna bastığınızda, input içerisindeki değer, Controller içerisindeki names değişkenine ekleniyor, ve arka plandaki her değişiklik anında ön yüzde fark ediliyor, burada names dizinini listelememizde olduğu gibi.

İstemci tarafındaki iş kodunu yazmaya başladık ancak biraz da gözümüze hitap etmesi için bir tane de css kütüphanesi ekleyelim. Burada da Twitter Bootstrap kullanalım. getbootstrap.com adresine gidip, CDN adreslerini alalım.

    <!-- bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

Bootstrap'ın çalışabilmesi için gerekli olan jQuery'yi de ekleyelim.

    <!-- jquery -->
    <script   src="https://code.jquery.com/jquery-2.2.4.min.js"   integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="   crossorigin="anonymous"></script>

Yine Bootstrap'ın sitesinden örnek şablonu (Navbar olanı seçtim) index.html sayfasına uygulayalım. index.html sayfasının son hali aşağıdaki gibidir.

<!DOCTYPE html>  
<html lang="en" ng-app="myFirstAngularModule">  
<head>  
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="../../favicon.ico">

    <title>Navbar Template for Bootstrap</title>

    <!-- jquery -->
    <script   src="https://code.jquery.com/jquery-2.2.4.min.js"   integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="   crossorigin="anonymous"></script>

    <!-- bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
    <style>
        body {
            padding-top: 20px;
            padding-bottom: 20px;
        }

        .navbar {
            margin-bottom: 20px;
        }
    </style>


    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"
            type="text/javascript"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"
            type="text/javascript"></script>

    <script src="js/app.js" type="text/javascript"></script>
</head>

<body>

<div class="container">

    <!-- Static navbar -->
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">Spring Boot & AngularJS</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
            </div><!--/.nav-collapse -->
        </div><!--/.container-fluid -->
    </nav>

    <div ui-view></div>

</div> <!-- /container -->


</body>  
</html>  

home.html dosyasını da aşağıdaki gibi değiştirelim.

<!-- views/home.html -->

<div class="form-group">  
    <label>İsminiz:</label>
    <input class="form-control" type="text" ng-model="name" />
</div>

<button type="button" class="btn btn-primary" ng-click="addName()">Kaydet</button>

<hr />

<ul class="list-group">  
    <!-- ng-repeat, bir dizi değişkeni üzerinde dönmenize olanak sağlar
         burada olduğu üzere, isimler üzerinde dönüp onu liste
         olarak yazdırmamızı sağlıyor -->
    <li class="list-group-item" ng-repeat="selectedName in names">
        {{selectedName}}
    </li>
</ul>  

Tüm bunların ardından sayfamızın son hali aşağıdaki gibi olacaktır.

Bootstrap eklenmiş hali

Serinin bu yazısında, uygulamamızdan nasıl statik kaynaklarını sunabileceğimize göz attık. Sonrasında uygulamamıza AngularJS kütüphanesini ekledik ve örnek sayfamızı oluşturduk. Daha sonra ise yönlendirme mekanizmasını UI Router kütüphanesi ile gerçekleştirdik. Son olarak ise uygulamamıza Bootstrap kütüphanesini ekleyip ön yüzümüzü de geliştirdik.

Uygulamanın bu yazıdaki son halini, https://github.com/mndeveci/spring-boot-angularjs-project-2 adresinden klonlayabilirsiniz.

Serinin bundan sonraki yazısında ise, ön yüz ile arka planı birbirine bağlayıp, basit bir kaç listeleme ekranı gerçekleştireceğiz.

Kaynakça