Reputation: 6320
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.
Upvotes: 1
Views: 177
Reputation: 366
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.
Upvotes: 0
Reputation: 44916
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>
Upvotes: 1