I have this controller that is working correctly for an Angular UI typeahead. How would I add the server call as a resource?
var receivableApp = angular.module('receivableApp', ['ui.bootstrap', 'ngResource'])
.controller('ReceivableController', function ($scope,$http) {
$scope.Debtors = function (term) {
return $http.get('/Receivable/GetDebtors/?term=' + term).then(function (response) {
return response.data;
});
};
});
receivableApp.factory('GetDebtors', ['$resource',
function($resource){
return $resource('/Receivable/GetDebtors/');
}]);
This will add a service GetDebtors with a default GET, and other REST handlers.
Now, to make a call inside your controller, you can do:
debtControllers.controller('DebtCtrl', ['$scope', 'GetDebtors',
$scope.debts = GetDebtors.get({term: 'foo'});
]);
This is equivalent to making a call to /Receivable/GetDebtors/?term=foo.
If you want to change the behavior, you can override the default methods available on $resource. More details can be found here.
$resource was meant to retrieve data from an endpoint, manipulate it and send it back.
It's fine to have custom methods on your resource, but you don't want to miss out on the cool features it comes with OOTB.var Todo = $resource('/api/1/todo/:id');
//create a todo var todo1 = new Todo();
todo1.foo = 'bar';
todo1.something = 123;
todo1.$save();
Related
I need to use angular service to create cascading drop-downs.The commented code I created for testing purpose and it is working fine. I need to create two services to call two methods from the MVC controller : GetCompanies() and GetDocTypes()
My questions are: Is my first service correct and how can I call the services from the controller?
Thank you.
/// <reference path="angular.js" />
//var myApp = angular
// .module("myApp", [])
// .controller("companyController", function ($scope, $http) {
// $http.post('CurrentSettings/GetCompanies')
// .then(function (response) {
// var response = $.parseJSON(response.data);
// $scope.currentSettings = response;
// });
// });
var myApp = angular.module("myApp", []);
myApp.service('getCompanies', function () {
$http.post('CurrentSettings/GetCompanies')
.then(function (response) {
var response = $.parseJSON(response.data);
$scope.currentSettings = response;
});
});
myApp.controller("companyController", function ($scope, getCompanies, $http) {
});
The problem with your service is two-fold:
Firstly, there is no way to call the service. You injected it fine, but now what? Think of your service as an API; it's no good just having a reference to it somewhere, you need to be able to use it. I would change to the following:
var myApp = angular.module("myApp", []);
myApp.service('getCompanies', ["$http", function($http) {
this.currentSettings = "Hello";
$http.post('CurrentSettings/GetCompanies')
.then(function(response) {
var response = $.parseJSON(response.data);
this.currentSettings = response;
});
}]);
myApp.controller("companyController", ["$scope", "getCompanies",
function($scope, getCompanies) {
$scope.currentSettings = getCompanies.currentSettings;
}]);
Note a few things:
You need to explicitly inject $http into your service.
I specify the names of the services that I'm injecting as part of an array that includes the function. This actually allows you to name the parameters anything you want, and is considered a best practice.
The service doesn't use $scope directly. Instead, it makes a field available to clients of the service. That client (the controller in this case) can then do with the value whatever it wants, including assigning it to a $scope field.
The controller reads this field from the service. It could also call any functions you specified - making the service an API, as I mentioned before.
The second problem is one of timing. Notice that I used the super-original value of "Hello" to initialize the service field.
The value you receive from the service will depend on whether or not the controller reads the value after your call to the MVC controller returns.
To fix this, the service could expose a second field to indicate that the company list is fully loaded, but that really shifts the problem around instead of fixing it.
What you need is a function that returns a promise. If the value has already been loaded, the promise resolves immediately. If not, it returns a promise that will return once the $http call is done.
Here is the modified code:
var myApp = angular.module("myApp", []);
myApp.service('companiesService', ['$http', '$q', function($http, $q) {
var currentSettings = null;
this.getList = function() {
var def = $q.defer()
if (currentSettings) {
def.resolve(currentSettings);
} else {
$http.get('CurrentSettings/GetCompanies')
.then(function(response) {
var response = response.data;
currentSettings = response;
def.resolve(currentSettings);
});
}
return def.promise;
}
}]);
myApp.controller('companyController', ['$scope', 'companiesService',
function($scope, companiesService) {
$scope.currentSettings = '';
companiesService.getList().then(function(value) {
$scope.currentSettings = value;
});
}
]);
It becomes a bit more complicated because you have to use promises, but these are the things to note:
I changed the name of the service to make it more generic. It can now offer a number of company-related features.
currentSettings is no longer added to this on the service, but instead becomes a normal (private) variable. The calling code can only read it by calling the getList function.
getList returns a promise. The promise is resolved immediately if currentSettings has been assigned. If not, it only resolves once the value is received from the web service.
The controller calls getList and assigns the value to the $scope field in the then function.
I have service:
angular.module('app1App')
.service('Fullcontactservice', function Fullcontactservice(Restangular, $http, $q) {
// AngularJS will instantiate a singleton by calling "new" on this function
var self = this;
self.apiKey = "..........."
self.route = "person"
self.getProfile = function(email){
console.log("called")
console.log(email)
Restangular.one("person").get({email: email, apiKey: self.apiKey})
.then(function(response){
console.log(response)
return response
})
}
return self;
});
Controller:
angular.module('app1App')
.controller('FullcontactCtrl', function ($scope, Fullcontactservice) {
$scope.searchFullcontact = function(){
$scope.data = Fullcontactservice.getProfile($scope.email)
}
});
When I call the searchFullcontact(), Restangular calls fullcontact and returns data but that's not pushed to the scope - I understand why. When I use promises, just results to a {} and no data is pushed.
How can I have it do that. I am trying to avoid the .then() function within my controller to keep it being slim because traditionally I had very large controllers.
Thanks!
Restangular has a handy feature allowing you to make this code work:
self.getProfile = function(email){
return Restangular.one("person").get({email: email, apiKey: self.apiKey}).$object
}
$object is effectively a shortcut where Restangular first creates an empty object which is filled with the data from the REST call once it is available. The code working with $scope.data inside your controller must be flexible to handle the initially empty object. This is usually not an issue if you use data inside the template (html) as angular gracefully handles missing (undefined).
I am trying to create a service when I can set my formSubmit.
For example. In controller A I call "service.setFormSubmit(doThis(obj))" and in controller B I call "service.getFormSubmit()". Where it will execute the function doThis(obj) in controller B.
UPDATE - Re-formulated question.
I have 1 view where I want to edit or create a category. This means I need a dynamic ng-submit. I want to to this in the controller. So like this:
$scope.editCategory = function(obj) {
$scope.formSubmit = 'editCategory'
}
And on the create I want to change the formSubmit var to createCategory of course.
So I can make a difference between creating and editing the category.
Is this possible? Would be really nice if someone has a way to do this..!
Thanks in advance!
Instead of passing around strings which need to be eval'ed, use the service to share functionality directly between controllers.
The service can be dirt-simple:
.factory('MyService', function(){
var service = {};
return service;
});
Once injected and assigned to scope variables in both controllers you have an intermediary unit which can act as a modifiable channel for cross-controller collaboration.
.controller('FirstController', function($scope, MyService){
$scope.service = MyService;
})
.controller('SecondController', function($scope, MyService){
$scope.service = MyService;
$scope.service.create = function(obj){
console.log('Creating');
}
$scope.service.edit = function(obj){
console.log('Editing');
}
})
From the scope of FirstController, you can now call the function also available on the scope of SecondController:
<div ng-controller="FirstController">
<input type="checkbox" ng-model="button.type"> Toggle create/edit<br/>
<button ng-if="button.type" ng-click="service.create(obj)">Create</button>
<button ng-if="!button.type" ng-click="service.edit(obj)">Edit</button>
</div>
Demo
If you aren't reloading the page you can create an encapsulated variable in your service. Your set call would assign the value passed to that variable and your get call would return that variable to the caller.
One way I have achieved passing the data is to submit the form using the service and return a Json result to the service. Store the Json object in the encapsulated variable on the return and then pass a success or failure to the controller. When successful, let the controller redirect the view which will redirect using angular routing and ng-view. Once the new view, along with the new controller is loaded into the page, you can call the variable in your service to retrieve the data on the next controller.
Example Code:
app.factory('service', function ($q, $http) {
var savedData;
return {
loadData: function() {
return data;
},
search: function (parameters) {
var searchURL = '/MVCController/Search?parameter1=' + parameters.one +
'¶meter2=' + parameters.two;
var deferred = $q.defer();
$http.get(searchURL).success(function (data) {
savedData = data;
deferred.resolve(true);
}).error(function(data) {
data = 'An error occurred while searching: ' + data;
savedData = data //(if you want to save the error)
deferred.reject(data);
});
return deferred.promise;
}
}
});
I wonder if I can call controller method from service.
I know that Service is singleton and I can't inject $scope to the service.
In my case I manage Google Maps in service and want to open modal Dialog when user right clicks on Polygon.
As I know, to open/create new instance of dialog, somehow Service must notify controller to do that.
This is a template with controller + method and service: Template
var myApp = angular.module('myApp', []);
function MyCtrl($scope, gridService, $timeout) {
// how to call "foo" method from service?
$scope.foo = function(){
alert('called from service');
};
}
myApp.service('gridService', ['$timeout', function ( $timeout) {
var grid = {
fetching: false,
pristine: true,
pageType: 'Edit'
}
return {
gridSetup: function () {
return grid;
},
setGridSetup: function (newGrid) {
}
}
}]);
Thanks,
The answer is simple: you don't.
The service exists to manipulate data, nothing else. And it really shouldn't care "why" it's doing what it's doing. You ask the service to do something and wait for a response.
Personally I prefer using promises to resolve async operations (i.e. to notify the controller about a state change) since it's heavily supported by many angular services like $http.
But feel free to use callbacks of you wish.
Usually you do not need to call controller from the service - in general the single service could be used by any controller so service shall know nothing about them. In most cases controller calls to the service in react to some user's action and then you need to update view from controller somehow when service did its work (get response from server, etc.). I see following general ways how to do it.
1. Use callbacks.
//controller
$scope.onButtonClick = function() {
$scope.label = "wait, loading...";
function onSuccess(result) {
$scope.label = "done! result is " + result;
}
myService.doSomeWork(param1, param2, onSuccess);
}
//service
doSomeWork: function(param1, param2, onSuccess) {
$.get({...}, onSuccess);
}
So you provide a callback for each action.
2. Subscribe on events
You may use jQuery for events subscribing/triggering
//controller
$(myService).on('update', function() {
$scope.lastUpdateTime = new Date();
});
$scope.onButtonClick = function() {
myService.doUpdate();
}
//service
doUpdate: function() {
$.get({...}, function onOk() {
$(this).trigger('update');
});
}
3. Use promises
A lot of built-in angular services return promise objects, you may use them too:
//controller
myService.doSomething(param1).then(function(result) {
$scope.result = result;
});
//service
doSomething: function(param1) {
return $http.get({...});
}
4. Share some data
An example is $resource service - for example when you call query method it returns empty array-like object that could be safely put to scope and then fills it with values when http request is done.
//controller
$scope.options = myService.options;
$scope.onClick = function() { myService.update() }
//service
options: [],
update: function() {
var self = this;
$http.get({...}).success(function(res) {
self.options.splice(0, self.options.length); //to keep same array
self.options.push.apply(self.options, res.data.options);
});
}
In all these cases services and controllers are separated, services could be used with any controller and you may easily write unit-tests on services that will not break if you change your controller/view part somehow.
A possible solution would be to have a dialog service which you can inject into the grid service. So when the user right clicks on the polygon the handler would call open on the dialog service.
Take a look at the modal service on angular ui as an example.
I am new to angular so it is probably easy question. I have this factory resource at the moment:
angular.module('resources.survey', ['ngResource'])
.factory('Survey', function ($resource) {
return $resource('/backend/surveys/:surveyId/data', {surveyId: '#id'});
});
Controller:
.controller('PagesCtrl', function (Survey) {
var survey = Survey.get({surveyId: 2});
//now I want to change survey variable and share it between two controllers
});
There are no problems with ngResource I can get the data from server. However I want to manipulate with the data from the server and use the same data in other controllers (probably using DI) and allow data manipulation there as well. I know that it can be done with $rootScope, but I was wondering if there is any other way.
Your service should cache the response for the resource request in something like array of surveys and dispense surveys from this array instead of directly returning a resource object.
Controllers would only share data if the same reference for the survey is returned.
Roughly it would look like
.factory('Survey', function ($resource,$q) {
var surveys[];
return {
getSurvey:function(id) {
var defer=$q.defer();
//if survery contains the survey with id do //defer.resolve(survey[i]);
// else query using resource. On getting the survey add it to surveys result and resolve to the newly added survey.
}
}
});
angular.module('resources.survey', ['ngResource'])
.factory('Survey', function ($resource) {
return $resource('/backend/surveys/:surveyId/data', {surveyId: '#id'});
})
.controller('MyCtrl', function($scope,Survey){
//here you can call all the $resource stuff
});
Here is complete documentation and example how to use it:
http://docs.angularjs.org/api/ngResource.$resource
I managed to create a resource that can handle what I wanted. It is probably not as advanced as Chandermani suggested. But it works for my needs.
angular.module('resources.survey', ['ngResource'])
.factory('Survey', function ($resource) {
var resource = $resource('/backend/surveys/:surveyId/data',
{surveyId: '#id'}
);
var Survey = {};
var data = []; //saves the data from server
Survey.get = function(surveyId) {
if(angular.isDefined(data[surveyId])){
return data[surveyId];
}
return data[surveyId] = resource.get({surveyId: surveyId});
};
return Survey;
});
And to call basically I call it like this:
.controller('QuestionsCtrl', function (Survey) {
Survey.get(1).newData = 'newData'; //adding additional data
console.log(Survey.get(1));
});
I guess this can be improved.