I need to write code which accesses data in a backend.
Trouble is that backend is not ready yet. So I want to inject a service which would, possibly by configuration, use the real backend or a mock object.
I think angular services are what I need. But I am not clear how to implement this.
app.factory('myService', function() {
var mySrv;
//if backend is live, get the data from there with $http
//else get data from a mock json object
return mySrv;
});
Somehow I guess I'd have to write two more services, the real one and the fake one, and then call the one or the other in 'myService'?
Maybe I totally misunderstand mocking, but I'd rather not want this to be mocked for test runs (not for unit tests like in this post: Injecting a mock into an AngularJS service - I'd like the app to really use my mock for demo and development purposes.
This is actually where Angular's dependency injection system really comes in handy. Everything in Angular is basically a provider at some level. Methods like service and factory are just convenience functions to avoid boilerplate code.
A provider can be configured during the bootstrap process, which is really handy for setting up scenarios exactly like what you are describing.
At it's simplest, a provider just needs to be a constructor function that creates an object with a $get function. The $get is what creates the actual services, and this is where you can check to see which one to create.
//A simple provider
function DataServiceProvider(){
var _this = this;
_this.$get = function(){
//Use configured value to decide which
// service to return
return _this.useMock ?
new MockService() :
new RealService;
};
}
Now you can register this as a provider with your application module.
angular.module('my-module', [])
.provider('dataService', DataServiceProvider);
And the provider can be configured before it creates the first service instance. By convention, the provider will be available as NAME + 'Provider'
angular.module('my-module')
.config(['dataServiceProvider', function(dataServiceProvider){
//Set the flag to use mock service
dataServiceProvider.useMock = true;
}]);
Now whenever you inject dataService anywhere in your application, it will be using the mock service you provided based on configuration.
You can see a full working example of this in the snippet below.
(function(){
function RealService(){
this.description = 'I\'m the real McCoy!';
}
function MockService(){
this.description = 'I\'m a shady imposter :P';
}
function DataServiceProvider(){
var $this = this;
$this.useMock = false;
$this.$get = function(){
return $this.useMock ?
new MockService() :
new RealService();
};
}
function CommonController(dataService){
this.dataServiceDescription = dataService.description;
}
CommonController.$inject = ['dataService'];
angular.module('common', [])
.provider('dataService', DataServiceProvider)
.controller('commonCtrl', CommonController);
angular.module('provider-app-real-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = false;
}]);
angular.module('provider-app-mock-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = true;
}]);
var moduleNames = ['provider-app-real-service','provider-app-mock-service'];
angular.forEach(moduleNames, function(modName){
//Have to manually bootstrap because Angular only does one by default
angular.bootstrap(document.getElementById(modName),[modName]);
});
}());
<script src="http://code.angularjs.org/1.3.0/angular.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div class="row" id="provider-app-real-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
<div class="row" id="provider-app-mock-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
</div>
Does this need to be a service? If you want to create a mock service then Josh's answer above is perfect.
If your not tied to using a service then I suggest looking at my answer to the following question Mock backend for Angular / Gulp app which also about mocking out a backend. Regardless of wether your backend is created or not mocking it out allows for more stable test runs and development of you app.
Related
I have a code which works for both factory and service :
I am confused at what circumstances I need to use which code.
This code output my value in console.log using service
var mod = angular.module("MyModule", []);
mod.service("myService", function() {
console.log("this is service");
return {
getvalue : function() {
return "My value";
}
};
});
mod.controller("MyController", function(myService) {
console.log("MyController - myFactory: " + myService.getvalue());
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyModule">
<div ng-controller="MyController"></div>
</div>
This code I have just change mod.service() to mod.factory() and rest of code is same and it output's my value in console.log using factory
var mod = angular.module("MyModule", []);
mod.service("myFactory", function() {
console.log("this is Factory");
return {
getvalue : function() {
return "My value";
}
};
});
mod.controller("MyController", function(myFactory) {
console.log("MyController - myFactory: " + myFactory.getvalue());
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyModule">
<div ng-controller="MyController"></div>
</div>
This blog post by Todd Motto is worth a thousands answers.
Using .factory() gives us much more power and flexibility, whereas a
.service() is essentially the “end result” of a .factory() call. The
.service() gives us the returned value by calling new on the function,
which can be limiting, whereas a .factory() is one-step before this
compile process as we get to choose which pattern to implement and
return.
It explains what is the difference at the source code level.
In real life, my opinion is that it's a matter of taste to use one or the other.
Coming from Java, I like the fact that .service looks like a class with the use of this.
Let me explain
Services
Services provide a method for us to keep data around for the lifetime of the app and communicate
across controllers in a consistent manner.
Services are singleton objects that are instantiated only once per app (by the $injector) and lazyloaded
(created only when necessary). They provide an interface to keep together those methods
that relate to a specific function.
Factory
This service factory function is responsible for generating a single object or function that becomes
this service, which will exist for the lifetime of the app. When our Angular app loads the service, the
service will execute this function and hold on to the returned value as the singleton service object.
The service factory function can be either a function or an array, just like the way we create
controllers:
So i would like you to use factory for your core operations, and call this factory from one service(You need to create one saporate services for this), so this way you can provide more isolation in your app.
Example
*make one factory like :
app.factory('memberServiceFactory', ['$http','$filter', function ($http,$filter) {
return {
getData: function (data) {
return {};
},
addData: function (file, formData) {
},
}
}]);
*make one service like :
app.service('memberListService', ['memberServiceFactory', function (memberServiceFactory){
this.getList = memberServiceFactory.getData;
this.addMember = memberServiceFactory.addData;
}]);
You can see here i am calling factory from services, you can provide encapsulation like this.
Two angular apps are on a same page, first one have a service that modifies data model. Calling the service from its parent app modifies data and DOM accordingly.
But calling same service from another app makes a copy of data and makes changes on that copy only.
How could angular service be called from another app so it have access to data model from it's parent app?
Here's a plunk
<div ng-app="app1">
<div ng-controller="myctrl">
<input type="text" value="{{data.list.name}}">
<br>
<button ng-click="update()">run from app1</button>
</div>
</div>
<div id='app2div' ng-app='app2' ng-controller="ctrl2">
<button ng-click="update2()">run from app2</button>
</div>
<script>
var test = angular.module("app1",[]);
test.controller("myctrl", function($scope, service)
{
$scope.scope1 = true;
$scope.data=service.data;
$scope.update=function()
{
service.updateValue();
};
}).factory("service", function()
{
return new (function(){
this.data={list:{name:"name0"}},
this.updateValue=function()
{
var self=this;
self.data.list=self.values[self.count];
self.count++;
},
this.values= [{name:"name1"}, {name:"name2"}, {name:"name3"}],
this.count=0
})();
});
var app2 = angular.module('app2',['app1']);
app2.controller('ctrl2', function($scope, service){
$scope.scope2 = true;
$scope.update2 = function()
{
service.updateValue();
};
});
angular.bootstrap(document.getElementById("app2div"), ['app2']);
</script>
Explanation
There is not really any built in support for cross-app communication in AngularJS, so it can be a bit tricky. But it is possible.
First it's important to understand the difference between module, app and instances.
In this case we will use the word app to describe the entire composition of modules.
In your case you have:
app1 - which consists of the module app1.
app2 - which consists of the modules app2 and app1.
Now when you use either ng-app or angular.bootstrap to bootstrap an application a new instance of the app is created.
Each instance gets its own instance of the $injector service.
Now comes an important part - each service in AngularJS is a singleton in the sense that it is only created once per $injector instance (including $rootScope).
This means that after you have bootstrapped the both applications you will have:
Instance of app1 -> Instance of $injector -> Instance of service
Instance of app2 -> Instance of $injector -> Instance of service
So there are two instances of service, each with their own internal state. Which is why the data is not shared in your case.
So it wouldn't even matter if you instead bootstrapped app1 two times, there would still be two instances of everything.
How to
To retrieve an app´s injector instance you can use the injector method that is available on angular elements.
Note that this is not the same as the method angular.injector, which is used to create entire new instances of $injector.
Retrieve a reference to a DOM element of a specific app that was used with ng-app or angular.bootstrap:
var domElement = document.getElementById('app1');
Turn it into an angular (jqLite) element:
var angularElement = angular.element(domElement);
Retrieve the app's injector:
var injector = angularElement.injector();
Use the injector to retrieve the correct instance of a service:
var myService = injector.get('myService')
Call the service:
myService.doSomething();
If this is done outside of Angular's digest loop you will need to trigger it manually:
var rootScope = injector.get('$rootScope');
rootScope.$apply();
Or:
var scope = angularElement.scope();
scope.$apply();
Example solution
We have two apps:
var app1 = angular.module('app1', ['shared']);
var app2 = angular.module('app2', ['shared']);
app1.controller('MyController', function($scope, sharedFactory) {
$scope.sharedFactory = sharedFactory;
});
app2.controller('MyController', function($scope, sharedFactory) {
$scope.sharedFactory = sharedFactory;
})
Both apps use the shared module consisting of a factory named sharedFactory with the following api:
var api = {
value: 0,
increment: increment,
incrementSync: incrementSync
};
Remember that each app has its own instance of the factory.
The increment method will simply increment the value. If called from app1 the incrementation will take place in app1's instance of the service.
The incrementSync method will increment the value in both app1's and app2's service instance:
function incrementSync() {
var app1InjectorInstance = angular.element(document.getElementById('app1')).injector();
var app2InjectorInstance = angular.element(document.getElementById('app2')).injector();
console.log(app1InjectorInstance === app2InjectorInstance); // false
var app1SharedFactoryInstance = app1InjectorInstance.get('sharedFactory');
var app2SharedFactoryInstance = app2InjectorInstance.get('sharedFactory');
console.log('app1-sharedFactory:', app1SharedFactoryInstance);
console.log('app2-sharedFactory:', app2SharedFactoryInstance);
app1SharedFactoryInstance.increment();
app2SharedFactoryInstance.increment();
var otherInjectorInstance = app1SharedFactoryInstance === api
? app2InjectorInstance
: app1InjectorInstance;
otherInjectorInstance.get('$rootScope').$apply();
}
Note that if incrementSync is for example called from within app1, we will be in app1's digest loop, but we will have to start app2´s digest loop manually. This is what the part involving otherInjectorInstance does.
Demo: http://plnkr.co/edit/q2ZEG8VqxHORRzLy8wqZ?p=preview
Hopefully this will help you get going and find a solution to your problem.
We use services to make HTTP calls to a backend service and inject that service in our controller. The service is dependent on region. We decide on which service to use, based on the user's region. Is there a way to tag services with some qualifier and then perhaps use this qualifier to inject the appropriate service into our controller?
In AngularJS there is the concept of providers, which allow you to configure a component a certain way prior to injecting it into other services, controllers etc.
The configuration happens at the config phase of an application.
A provider in AngularJS is the most generic approach to components that can be injected into other components. Other facilities like factories and services are just syntactic sugar on top of providers.
Note that if the qualifier changes at runtime, a provider is not the right option.
You want to use a factory or service (singleton) that might have a getInstance(qualifier) function returning the right component.
The drawback of this would be that you need to have the qualifier available and instantiation would be handled in a manual fashion.
I have created a little example showing how a 'Region' component could be configured based on the region a user is coming from:
var app = angular.module('MyApp', []);
// Specific region component that will be created using a certain preset.
app.factory('SpecificRegion', [
function() {
return function(region) {
var specificRegion = region;
this.getName = function() {
return specificRegion;
}
}
}
]);
// The object returned from the $get factory function is available to other
// components as 'Region'.
app.provider('Region', function RegionProvider() {
var userRegion = 'NO_REGION';
// Configuration interface for the provider
this.setRegion = function(region) {
userRegion = region
}
// Actual factory function which configures a component based
// on a qualifier.
// It would also be possible to return different components, not only
// one configured a certain way. Dependency injection for factories
// is available at this point.
this.$get = ['SpecificRegion',
function regionFactory(SpecificRegion) {
return new SpecificRegion(userRegion);
}
];
});
// Application config phase:
// Configure the region component to behave based on a qualifier.
// The qualifier could come from anywhere, e.g $window.userRegion
app.config(['RegionProvider',
function(RegionProvider) {
RegionProvider.setRegion('EMEA');
}
]);
// Bringing it all together ...
app.controller('MainCtrl', ['$scope', 'Region',
function($scope, Region) {
$scope.regionName = Region.getName();
}
]);
.region {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MainCtrl">
<b>Hello from:</b> <span class="region">{{ regionName }}<span>
</div>
</div>
Editable version on plunker: Region Component
I'm trying to get my first ASP.NET Webforms and AngularJS app up and running, but I'm struggling...
I created a blank, new ASP.NET 4.5.1 webforms app, and included WebAPI into the mix. I created a sample page for my list of customers, and a standard, EF6-based WebAPI CustomerController : ApiController with all the usual CRUD methods. I tested that WebAPI using Fiddler and low and behold - I get my 8 customers back from my database.
Getting this into AngularJS however has been a bit of a unsuccessful and highly frustrating experience ....
I included AngularJS from NuGet and that seems to have worked - no errors shown or anything, a pile of angular*.js files dumped into my Scripts folder.
I created a basic CustomerList.aspx page based on a master page which includes the <html lang="en" ng-app="TestAngular"> tag.
In order to get the data from the WebAPI service, I created my Angular module, and created a model inside the $scope, and created a service to fetch the data from the WebAPI:
Inside app.js:
var testModule = angular.module('TestAngular', [ ]);
testModule.controller('clientController', function ($scope, clientService) {
$scope.model = [];
clientService.getAllClients(function(results) {
$scope.model.clients = results;
});
$scope.model.clientCount = $scope.model.clients.count;
});
testModule.factory('clientService', function ($http) {
var srv = {};
srv._baseUrl = 'http://localhost:56313';
// Public API
return {
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
};
});
From what limited Javascript understanding I have, this should define a clientService (the testModule.factory() call) that calls my WebAPI URL, gets the JSON back, and the callback function then stuffs those customers retrieved into the $scope.model.clients property, and the $scope.model.clientCount should also be calculated.
My ASPX page looks something like this:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="CustomerList.aspx.cs" Inherits="TestAngular.CustomerList" MasterPageFile="~/Site.Master" %>
<asp:Content runat="server" ID="content" ContentPlaceHolderID="MainContent">
<h2>My customers</h2>
<div class="panel panel-default" data-ng-controller="clientController">
We have a total of {{ model.clientCount }} clients in our database table ...
<div class="list-group" data-ng-repeat="client in model.clients">
<div class="list-group-item">
<span>{{ client.Name }}</span><br/>
<span>{{ client.Name2 }}</span><br/>
</div>
</div>
</div>
</asp:Content>
So the <div> with the data-ng-controller should "connect" that DIV with the AngularJS controller, should load the customers from the WebAPI call, and contain them in the model, which would then be rendered to the ASPX page using the data binding syntax ({{ client.Name }} etc.)
Trouble is: the call to the WebAPI happens and the correct 8 customers are returned, however, when I debug into the Javascript code, the clientCount is always undefined and the ASPX page only shows two empty rows which probably would correspond to customers that have been retrieved - but why only 2, not 8 ??
I'm totally lost and stuck - can anyone spot what I'm doing wrong, what I'm missing here??
You are definately on the right track. At the moment, the problem is down to the clientService getAllClients method.
You should return the promise and then the data will chain through to the controller:
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
You may also want to take a look at the count line:
$scope.model.clientCount = $scope.model.clients.count;
Before the promise is resolved (and the callback is invoked), $scope.model.clients will be undefined. So, I'd expect this line to fail. Also, to get the count of an array, you need length.
You should set the clientCount inside of the callback:
clientService.getAllClients(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = $scope.model.clients.length;
});
Edit:
Typically, it is favoured to use the promise returned by $http. So, the controller would slightly change to:
clientService.getAllClients().then(function(response) {
$scope.model.clients = response.results;
$scope.model.clientCount = response.results.length;
});
And then the service would change to:
getAllClients: function() {
return $http.get(srv._baseUrl + '/api/Customer');
}
Angular uses promises from $q instead of callbacks (for most apis). They make chaining and exception handling much easier.
Also, since, in this case, you know you are handling a promise from $http, you can use the success method in the controller as well:
clientService.getAllClients().success(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = results.length;
});
success unwraps the response and sends only the body through to the callback.
I got services in angular that I need to be configurable, this way I can remove coupling from my app and share some of my code.
My question is how do you achieve this ?
With module.value() and dependancy injection ? Then how do you get a default value ? How do you make a value calculated at init and not hardcoded ?
With a another service ? How do you make that generic ?
Do you have a code exemple, because I'm a bit lost with this.
In order to create a configurable service, you need to use a provider, see: http://docs.angularjs.org/guide/providers. Providers allow you to configure the factory or service in the module's config block before it is instantiated. For a simple example, see http://plnkr.co/edit/QMLBBQ3obE90FtCA2eBu
For a more complete/complex example of how to use configurable services, I would suggest looking at the Restangular project, which also demonstrates a method of providing default values that can be overriden by the app developer.
I believe something like this should work within your service, if you were to use .value() to declare some objects for injection
var myLocalValue = myInjectedValue || "Some default value";
I'm not super javascript savvy but this answer on SO seems to elude to the fact that this will work too:
JavaScript OR (||) variable assignment explanation
I made a fiddle here to test it out it seems to work how I would expect:
http://jsfiddle.net/u3MY8/1/
JavaScript
var app = angular.module("myapp",[]);
// define a value
app.value('myThing', 'weee');
// use it in a service
app.factory('myService', ['myThing', function(myThing, myOtherThing){
var myLocalOtherThing = myOtherThing || "some default";
return {
whatsMyThing: function() {
return myThing; //weee
},
whatsMyOtherThing: function() {
return myOtherThing;
},
whatsMyOtherThingWithDefault: function() {
return myLocalOtherThing;
}
}
}]);
// use it in a controller
app.controller('someController', function($scope, myService) {
$scope.foo = myService.whatsMyThing(); //weee
$scope.bar = myService.whatsMyOtherThing(); //""
$scope.baz = myService.whatsMyOtherThingWithDefault(); //some default
});
HTML
<div ng-app="myapp" ng-controller="someController">
<ol>
<li>{{foo}}</li>
<li>{{bar}}</li>
<li>{{baz}}</li>
</ol>
</div>
To note this only works if myOtherThing isn't injected, if you list it to be injected but it hasn't been defined you'll run into errors, but I'm not sure exactly what the use case is.