AngularJS’de Service, Factory ve Provider
Selamlar,
Bugün AngularJS’de Service, Factory ve Provider arasındaki farkları güncel örnekler ile hep beraber inceleyeceğiz.
AngularJS Service:
Uygulama için kullanacağımız metodları, Service oluşturarak kolaylıkla yapabiliriz. Serviceler new keyword ile yaratabileceğimiz objeleri oluştururlar. “this” ile ilgili methodları tanıtırlar.
Örneğin aşağıdaki diziye, bir arkadaş listesi json olarak atanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var friendsList = [{ id: 0, 'name': 'Engin', 'email': 'engin@enginpolat.com', 'phone': '123-2343-44' }, { id: 1, 'name': 'Mehmet', 'email': 'mehmet@pakcamsil.com', 'phone': '222-3333-54' }, { id: 2, 'name': 'Caglar', 'email': 'caglar@caglarozenc.com', 'phone': '234-5421-78' } ]; |
Gerekli arkadaş listesi: “this” keyword’ü ile “list()” methodu sayesinde aşağıdaki gibi döndürülür.
1 2 3 |
this.list = function () { return friendsList; } |
Düzenleme amaçlı belli bir ID’ye göre belirlenen “friend” aşağıda görüldüğü gibi “get()” methodu ile alınmaktadır. “forEach” ile döngüye girilip, istenen id’li “friend” bulunana kadar ilerlenmektedir. Bulunan result daha sonra geri döndürülmektedir.
1 2 3 4 5 6 7 8 9 |
this.get = function (id) { var result=null; angular.forEach(friendsList, function (frnd) { if (frnd.id == id) { result= frnd; } }); return result; } |
Yeni bir kişi eklenecek ya da var olan kişi Update edilecek ise aşağıdaki “save()” methodu çağrılır. Eğer “friend.id==null” ise bu yeni bir kayıt demektir ve “friendList”‘e eklenir. Deği ise “Update” işlemi için ilgili “id”‘deki kayıt bulunur ve yeni kayıt ile güncellenir. Buradaki “uid” bir çeşit “identity”dir. Ve her yeni kayıt da “+1” arttırılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
this.save = function (friend) { if (friend.id == null) { friend.id = ++uid; console.log(JSON.stringify(friend)); friendsList.push(friend); } else { for (var i = 0; i < friendsList.length; i++) { if (friendsList[i].id == friend.id) { friendsList[i] = friend; } }; } } |
En son delete işlemi aşağıda görüldüğü gibi yapılır. İlgili kayıt “id” değerine göre bulunup “splice” komutu ile “friendList”‘den çıkarılır.
1 2 3 4 5 6 7 |
this.delete = function (id) { angular.forEach(friendsList, function (frnd) { if (frnd.id == id) { friendsList.splice(friendsList.indexOf(frnd), 1); } }) } |
Yukarıda parça parça yazılan “FriendServices” aşağıda görüldüğü gibi tanıtılan ilgili modul’e eklenir.
friends.js(Part 1):
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
var module = angular.module('app', []); module.service("FirendServices", function () { var uid = 2; var friendsList = [{ id: 0, 'name': 'Engin', 'email': 'engin@enginpolat.com', 'phone': '123-2343-44' }, { id: 1, 'name': 'Mehmet', 'email': 'mehmet@pakcamsil.com', 'phone': '222-3333-54' }, { id: 2, 'name': 'Caglar', 'email': 'caglar@caglarozenc.com', 'phone': '234-5421-78' } ]; this.save = function (friend) { if (friend.id == null) { friend.id = ++uid; console.log(JSON.stringify(friend)); friendsList.push(friend); } else { for (var i = 0; i < friendsList.length; i++) { if (friendsList[i].id == friend.id) { friendsList[i] = friend; } }; } } this.get = function (id) { var result=null; angular.forEach(friendsList, function (frnd) { if (frnd.id == id) { result= frnd; } }); return result; } this.delete = function (id) { angular.forEach(friendsList, function (frnd) { if (frnd.id == id) { friendsList.splice(friendsList.indexOf(frnd), 1); } }) } this.list = function () { return friendsList; } }); |
Şimdi sıra geldi aşağıda görülen ilgili modeli yaratılan”FriendController”‘a bağlamaya. Oluşturulan “FriendServices”‘deki tüm methodlar, “Controller”‘da tanımlanan “saveFriend(), delete() ve edit()” methodlarında tek tek çağrılır. Ayrıca sayfada kullanılacak arkadaş listesi olan “$scope.friends” yukarıda tanımlanan “FriendServices”‘deki “list()” methodu ile doldurulur. Böylece ilgili service’in bir instance’ı alınıp tanımlı methodlar çağrılmış olunur.
friends.js(Part 2):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module.controller('FriendController', function ($scope, FirendServices) { $scope.friends = FirendServices.list(); $scope.saveFriend = function () { FirendServices.save($scope.newfriend); $scope.newfriend = {}; } $scope.delete = function (id) { FirendServices.delete(id); if ($scope.newfriend.id == id) $scope.newfriend = {}; } $scope.edit = function (id) { $scope.newfriend = angular.copy(FirendServices.get(id)); } }) |
UI ekranı aşağıda görüldüğü gibidir. Tasarım için “bootstrap” kullanılmıştır.
Aşağıda görüldüğü gibi ilgili scriptler eklendikten sonra var olan kayıtlar bir “Table” içinde sıralanmıştır. Tablo içinde 2 tane action buttonu vardır. Bunlarda amaç ilgili kaydı silmek veye editlemektir. Ayrıca yeni kayıt girişi ve edit işlemleri için 3 “input” alan eklenmiştir.
Kodun çalışır hali burdan incelenebilir:
Index.cshtml:
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 |
<script src="~/Scripts/angular.min.js"></script> <script src="~/Scripts/friends.js"></script> <script src="~/Scripts/jquery-1.9.1.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Scripts/bootstrap.min.js"></script> <div ng-app="app" ng-controller="FriendController"> <form class="well"> <label>Name</label> <input type="text" name="name" ng-model="newfriend.name" /> <label>Email</label> <input type="text" name="email" ng-model="newfriend.email" /> <label>Phone</label> <input type="text" name="phone" ng-model="newfriend.phone" /> <br /> <input type="hidden" ng-model="newfriend.id" /> <input type="button" value="Save" ng-click="saveFriend()" /> </form> <table class="table table-striped table-bordered"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Phone</th> <th>Action</th> </tr> </thead> <tbody> <tr ng-repeat="friend in friends"> <td>{{ friend.name }}</td> <td>{{ friend.email }}</td> <td>{{ friend.phone }}</td> <td> <a href="#" ng-click="edit(friend.id)">edit</a> | <a href="#" ng-click="delete(friend.id)">delete</a> </td> </tr> </tbody> </table> </div> |
AngularJS Factory:
Service ve Factory nispeten birbirine benzer yapılardır. Factory statik ve tüm uygulamada tek bir instance oluşturularak kullanılmasına rağmen, service için her seferinde “new service()” ile yeni bir instance oluşturup kullanılır. Factory aslında singleton servis nesnesi üreten , primitive tipler, fonksiyon ve object döndüren yapılardır.
factory.js: Aşağıdaki örnekte “DropDownFactory” adında bir factory nesnesi vardır. Dikkat edeceğiniz gibi “getAllData()” ve “writePlaka()” adında 2 tane function’ı vardır. Ilgili functionlar “service”‘in aksine “this” keyword’ü olmadan tanımlanmışlardır. Amaç dropdown bir listeye, illere göre plaka numaralarının doldurulması ve seçilen bir il’e göre ilgili plakanın ekrana yazdırılması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 |
/// <reference path='angular.min.js' /> var module = angular.module('app', []); module.factory('DropDownFactory', function () { return { getAllData: function () { return [{ plaka: '01', il: 'ADANA' }, { plaka: '02', il: 'ADIYAMAN' }, { plaka: '03', il: 'AFYON' }, { plaka: '04', il: 'AĞRI' }, { plaka: '05', il: 'AMASYA' }, { plaka: '06', il: 'ANKARA' }, { plaka: '07', il: 'ANTALYA' } , { plaka: '08', il: 'ARTVİN' }, { plaka: '09', il: 'AYDIN' }, { plaka: '10', il: 'BALIKESİR' }, { plaka: '11', il: 'BİLECİK' }, { plaka: '12', il: 'BİNGÖL' }, { plaka: '13', il: 'BİTLİS' } , { plaka: "14", il: 'BOLU' }, { plaka: "15", il: 'BURDUR' }, { plaka: "16", il: 'BURSA' }, { plaka: "17", il: 'ÇANAKKALE' }, { plaka: "18", il: 'ÇANKIRI' }, { plaka: "19", il: 'ÇORUM' }, { plaka: "20", il: 'DENİZLİ' } , { plaka: "21", il: 'DİYABAKIR' }, { plaka: "22", il: 'EDİRNE' }, { plaka: "23", il: 'ELAZIĞI' }, { plaka: "24", il: 'ERZİNCAN' }, { plaka: "25", il: 'ERZURUM' }, { plaka: "26", il: 'ESKİŞEHİR' } , { plaka: "27", il: 'GAZİANTEP' }, { plaka: "28", il: 'GİRESUN' }, { plaka: "29", il: 'GÜMÜŞHANE' }, { plaka: "30", il: 'HAKKARİ' }, { plaka: "31", il: 'HATAY' }, { plaka: "32", il: 'ISPARTA' } , { plaka: "33", il: 'İÇEL' }, { plaka: "34", il: 'İSTANBUL' }, { plaka: "35", il: 'İZMİR' }, { plaka: "36", il: 'KARS' }, { plaka: "37", il: 'KASTAMONU' }, { plaka: "38", il: 'KAYSERİ' } , { plaka: "39", il: 'KIRKLARELİ' }, { plaka: "40", il: 'KIRŞEHİR' }, { plaka: "41", il: 'KOCAELİ' }, { plaka: "42", il: 'KONYA' }, { plaka: "43", il: 'KÜTAHYA' }, { plaka: "44", il: 'MALATYA' } , { plaka: "45", il: 'MANİSA' }, { plaka: "46", il: 'KAHRAMANMARAŞ' }, { plaka: "47", il: 'MARDİN' }, { plaka: "48", il: 'MUĞLA' }, { plaka: "49", il: 'MUŞ' }, { plaka: "50", il: 'NEVŞEHİR' } , { plaka: "51", il: 'NİĞDE' }, { plaka: "52", il: 'ORDU' }, { plaka: "53", il: 'RİZE' }, { plaka: "54", il: 'SAKARYA' }, { plaka: "55", il: 'SAMSUN' }, { plaka: "56", il: 'SİİR' } , { plaka: "57", il: 'SİNOP' }, { plaka: "58", il: 'SİVAS' }, { plaka: "59", il: 'TEKİRDAĞ' }, { plaka: "60", il: 'TOKAT' }, { plaka: "61", il: 'TRABZON' }, { plaka: "62", il: 'TUNCELİ' } , { plaka: "63", il: 'ŞANLIURFA' }, { plaka: "64", il: 'UŞAK' }, { plaka: "65", il: 'VAN' }, { plaka: "66", il: 'YOZGAT' }, { plaka: "67", il: 'ZONGULDAK' }, { plaka: "68", il: 'AKSARAY' } , { plaka: "69", il: 'BAYBURT' }, { plaka: "70", il: 'KARAMAN' }, { plaka: "71", il: 'KIRKLARELİ' }, { plaka: "72", il: 'BATMAN' }, { plaka: "73", il: 'ŞIRNAK' }, { plaka: "74", il: 'BARTIN' } , { plaka: "75", il: 'ARDAHAN' }, { plaka: "76", il: 'IĞIDIR' }, { plaka: "77", il: 'YALOVA' }, { plaka: "78", il: 'KARABÜK' }, { plaka: "79", il: 'KİLİS' }, { plaka: "80", il: 'OSMANİYE' }, { plaka: "81", il: 'DÜZCE' }]; }, writePlaka: function (No) { return "Plaka :" + No; } } }); module.controller('CityController', function ($scope, DropDownFactory) { $scope.cityList = DropDownFactory.getAllData(); $scope.update = function () { $scope.Plaka = DropDownFactory.writePlaka($scope.selectedItem) }; }); |
Yukarıda görüldüğü gibi “$scope.cityList“‘e tüm Json plaka datası atanmıştır. Ayrıca “$scope.update()” function’ı ile de seçilen data yani “$scope.selectedItem“, “$scope.Plaka” property’sine atanmıştır.
Factory.cshtml: DropDown’ın doldurulduğu view aşağıdaki gibidir. DropDown “ng-change” eventinde yukarıda tanımlanan “update()” function’ı çağrılmıştır. Ayrıca “<select>” elementinde “ng-model” yukarıda tanımlanan “selectedItem” nesnesidir. Böylece seçilen item bu değere atanmaktadır. Ayrıca “cityList” datası, içinde gezilerek “ng-option” directive ile value için “item.plaka“, “as” ile key için “item.il” değerleri kullanılmıştır. Ayrıca default değer olarak”ng-init” directive’inde “selectedItem“‘a istanbul ili atanmıştır. Yani seçilen il null ise ilk değer İstanbul olarak atanmıştır.
1 2 3 4 5 6 7 |
<script src="~/Scripts/angular.min.js"></script> <script src="~/Scripts/factory.js"></script> <div ng-app="app" ng-controller="CityController"> <select ng-model="selectedItem" ng-options="item.plaka as item.il for item in cityList" ng-change="update()" ng-init="selectedItem = selectedItem || cityList[33].plaka"></select> <h1>{{Plaka}}</h1> </div> |
Kodların çalışır hali burdan incelenebilir:
Aşağıdaki resimde “<select>” elementinin “key&value” pair şeklinde nasıl doldurulduğu görülmektedir.
AngularJS Provider:
Provider’ın Service ve Factory’ye göre en belirgin özelliği uygulama runtime’ından ilk olarak modul’e configuration yapıldığı esnada veya initialization sırasında çalıştırılabilmesidir. Bu aslında AngularJS üzerinde Module’de dependency Injection’dan başka bir şey değildir. Function ismine ait parametrenin çağrımında, “$get” methoduna ait olan değer geri döndürülür.
provider.js: Aşağıda “app” modulüne “DefaltCookie” provider’ı tanımlanmıştır. Kullanıcı Adı ve Soyadı bilgileri, “$get” function’ında “User” cookiesine bakılmış ve “WellCome” kelimesi ile beraber döndürülmüştür. “setName()” function’ında da “User” cookiesine bakınılmış ve eğer değeri null ise gelen “name” parametresi “7” gün süre ile ilgili cookie’ye atanmıştır. Aslında burada amaç giriş yapan kullanıcı bilgilerinin bir daha sayfaya gelindiğinde 7 gün boyunca korunmasını sağlamak ve giriş ekranında karşısına çıkarmaktır. Ayrıca “app.config()” kısmında kullanıcı bilgilerine hiçbir değer atanmamış ise default değer ilgili cookie’ye kaydedilmektedir.
Not: Burada dikkatinizi çekeceğini düşündüğüm nokta “[‘ngCookies’]” yani “angular-cookies.js“‘in kullanılmayıp yerine neden “Jquery.cookie” kullanıldığı konusu olacaktır. Nedeni Injection işlemi Configuratin sırasında yapılmasının mümkün olmadığıdır. AngularJS cookie’nin kullanılması için ya “app.run()” altında yada Controller altında çalıştırılması gerekmektedir. Ama amaç provider kullanımı olduğu için”app.config()“‘de ilgili işlemlerin yapılması gerekmekte ve bu nedenle “jquery.cookie”‘si kullanılmaktadır.
Aşağıda “app.directive” custom “focus” direktivi kullanılmıştır. Tek amacı “Atachment” olarak atandığı elemente focus olunmasını sağlamaktır. Yani burda “Ad” input alanına focus olunulmaktadır.
“app.config()” alanında tanımlı provider’ın “setName()” methodu çağrılmış ve eğer “User” cookiesi null ise belirlenen “Bora Kasmer” ismi default olarak ilgili cookieye set edilmiştir.
“app.controller“‘da “$scope.Message” ile tanımlı “DefaultCookie” provider’ın “GetMessage” function’ından dönen değer atanır. Ayrıca “$sope.SetUser()” function’ı ile input alanlara girilen Ad ve Soyad bilgisi “User” cookiesine atanır. Son olarak karşılama mesajına yani “$scope.Message”‘a Cookie’deki değer atanı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 34 35 36 37 38 39 40 |
var app = angular.module('app', []); app.provider('DefaultCookie', function () { this.$get = function () { var name = $.cookie("User"); return { GetMessage: "WellCome, " + name + "!" } }; this.setName = function (name) { if ($.cookie("User") == null) { $.cookie("User", name, { expires: 7 }); } }; }); app.directive('focus', function() { return { restrict: 'A', link: function(scope, element) { element[0].focus(); } } }); app.config(function (DefaultCookieProvider) { DefaultCookieProvider.setName('Bora Kasmer'); }); app.controller('myCtrl', function ($scope, DefaultCookie) { $scope.Message = DefaultCookie.GetMessage; $scope.SetUser = function () { if ($scope.User != "") { $.cookie("User", $scope.UserName + " " + $scope.UserSurname, { expires: 7 }); $scope.Message = "WellCome, " + $.cookie("User") + "!"; } } }); |
Örnek ekran çıktısı aşağıdaki gibidir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@{ Layout = null; } <!DOCTYPE html> <html> <head> <script src="~/Scripts/jquery-2.2.0.min.js"></script> <script src="~/Scripts/jquery.cookie-1.4.1.min.js"></script> <script src="~/Scripts/angular.min.js"></script> <script src="~/Scripts/provider.js"></script> <meta name="viewport" content="width=device-width" /> <title>Provider</title> </head> <body> <div ng-app='app' ng-controller='myCtrl'> <b>Ad:</b> <input type="text" ng-model="UserName" focus/> <b>Soyad:</b> <input type="text" ng-model="UserSurname" /> <input type="button" ng-click="SetUser()" value="Giriş" /><br /><br /> <h1>{{Message}}</h1> </div> </body> </html> |
Browser üzerindeki “User” cookie’sinin değeri aşağıda görüldüğü gibi “Kemal Sunal”‘dır.
Buna göre Service, yeni bir instance’ı oluşturulan klasik javascript fonksiyonlarından başka birşey değillerdir. Belli özel işleri yapmak için kullanılan yapılardır. Bir çeşit bussines katmanın ayrıldığı bir yapı gibi düşünmek de hiç te yanlış olmaz. Böylece kodun okunabilir ve yönetilebilirliği artırılmaktadır. Factory, functionda oluşan result’ı dönen (yukarıdaki örnekte sub function list) statik ve tek instancelı bir yapıdır. Provider injection işleminin yapılabildiği, daha enbaşta confing’de çağrılabilen, $get() functionındaki değerin döndürüldüğü bir yapıdır. Böylece AngularJS’de “Service”,”Factory” ve “Provider” arasındaki farklara ve kullanım şekillerine detaylı örnekler ile göz attık.
Böylece geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hoşçakalın.
Source Code: https://github.com/borakasmer/AngularServices
Harika olmuş çok teşekkürler.
Teşekkürler Berk..
Mrb bora hocam öncelikle emeklerinden dolayı çok teşekkür ederim, ilk baştaki service kısmında frnd.id diye bir fonksiyon var o kısmını anlayamadım o ismi daha önce bir yerde tanımlanmış olarak görmediğim için iliskilendiremedim , rica etsem o kısmını birazdaha net aciklayabilirmisiniz çok teşekkür ederim hayırlı çalışmalar
Selam Mert,
Öncelikle ben teşekkür ederim.
Soruna gelince durum şu, Yandaki sil button’una basılınca ilgili kişinin ID’si delete() functin’ına gönderilir: “ng-click=”delete(friend.id)”
Daha sonra ilgili Delete function’ın içinde yandaki gibi tüm friendList gezilir. “angular.forEach(friendsList, function (frnd) {”
Burada gezilen her bir kişi sıra ile “frnd” değişkenine atanmıştır. Yani “frnd” functin değil aslında bir “friend” sınıfıdır. Baştaki “frnd.id“‘nin amacı, tüm friend listesi gezilerek, secilen “frnd.id” ile gezilen lisetedeki “id”‘lerden birbiri ile örtüşenini bularak, seçilen kişiyi belirlemek ve silmektir.
İyi çalışmalar.