I've stumbled upon a problem that should be common and obvious but I can't seem to wrap my head around it.
I'm working on a small prototype app. My backend developer provides me with profile data in a JSON object. Let's say, it looks like this:
profile = {Name: 'John', Email: '[email protected]', DOB: '1980-11-03'}
I need these values in multiple locations and I also don't want to put backend http calls in the controllers, so I've created a service to handle this:
angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
var service = this;
service.Name = null;
service.Email = null;
service.DOB = null;
service.getProfile = function () {
return $http.get('/profile').then(function (response) {
service.Name = response.data.Name;
service.Email = response.data.Email;
service.DOB = response.data.DOB;
return true;
});
};
return service;
}])
.controller('ProfileCtr', ['ProfileService', function (service) {
var vm = this;
service.getProfile().then(function () {
vm.Name = service.Name;
vm.Email = service.Email;
vm.DOB = service.DOB;
});
}]);
There are a number of problems with this solution:
One solution would be to add a layer of indirection and create an object within the service:
angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
var service = this;
service.profile = {};
service.getProfile = function () {
return $http.get('/profile').then(function (response) {
for (key in response.data) {
service.profile[key] = response.data[key];
};
return true;
});
};
return service;
}])
.controller('ProfileCtr', ['ProfileService', function (service) {
var vm = this;
service.getProfile().then(function () {
vm.profile = service.profile;
});
}]);
This works in general, but now I get awkward controllerAs syntax:
<div ng-controller="ProfileCtr as ctr">
<h1> {{ ctr.profile.Name }}</h1>
<p> Email: {{ ctr.profile.Email }} <br /> DOB: {{ ctr.profile.DOB }}</p>
</div>
I'm wondering whether there is a way that gives me both: clean HTML {{ ctr.Name }}
syntax and
a DRY programming style.
Thanks for any hints!
I have a feeling that you want more than this, but this to me is at least DRY:
angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
var service = this;
service.getProfile = function () {
return $http.get('/profile').then(function (response) {
return response.data;
});
};
return service;
}])
.controller('ProfileCtr', ['ProfileService', function (ProfileService) {
var vm = this;
ProfileService.getProfile().then(function (profile) {
vm.profile= profile;
});
}]);
The service gets the data. You could add functionality for caching here too. The controller uses the service to get the data. There is no repeated code.
I like to use the $scope
variable, which would remove the one-layer of indirection issue. However, the controllerAs does have it's advantages, particuarly if you are using nested controllers and want to make it clear which controller you are using. And the $scope
identifier will be removed in version 2.
Using a directive for this section of html instead of a controller should make you code easier to read and re-use. It also is advised to make it ready to be upgraded to version 2.
Then:
app.directive('isolateScopeWithControllerAs', function () {
var controller = ['ProfileService', function (ProfileService) {
var vm = this;
ProfileService.getProfile().then(function (profile) {
vm.profile= profile;
});
}];
return {
restrict: 'EA', //Default for 1.3+
controller: controller,
controllerAs: 'vm',
bindToController: true, //required in 1.3+ with controllerAs
templateUrl: // path to template
};
});
Then your HTML still gives you:
<h1> {{ vm.profile.Name }}</h1>
<p> Email: {{ vm.profile.Email }} <br /> DOB: {{ vm.profile.DOB }}</p>
The ProfileCtr as vm
would come into more use if you were using the directive for more than one object. For example, if you has a user directive, then you could have:
controllerAs: 'user',
with user.profile.name
and ng-repeat='friend in user.friends'
etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With