Passing argument(s) to a service in AngularJs - angularjs

I am trying to configure my first tidbits of the AngularJs for a trivial stuff, but unfortunately unsuccessful at it after considerable amount of time.
My Premise:
Users select one of the options from a dropdown and have an appropriate template loaded into a div below the select. I have set up the service, a custom directive (by following the ans by #Josh David Miller on this post, and a controller in place. The ajax call in service is working fine except that the params that I pass to the server is hardcoded. I want this to be the 'key' from the dropdown selected by user. At the moment I am failing to have this code passed to the service.
My configuration:
var firstModule = angular.module('myNgApp', []);
// service that will request a server for a template
firstModule.factory( 'katTplLoadingService', function ($http) {
return function() {
$http.get("${createLink(controller:'kats', action:'loadBreedInfo')}", {params:{'b1'}}
).success(function(template, status, headers, config){
return template
})
};
});
firstModule.controller('KatController', function($scope, katTplLoadingService) {
$scope.breed = {code:''}
// here I am unsuccessfully trying to set the user selected code to a var in service,
//var objService = new katTplLoadingService();
//objService.breedCode({code: $scope.breed.code});
$scope.loadBreedData = function(){
$scope.template = katTplLoadingService();
}
});
firstModule.directive('showBreed', function ($compile) {
return {
scope: true,
link: function (scope, element, attrs) {
var el;
attrs.$observe( 'template', function (tpl) {
if (angular.isDefined(tpl)) {
el = $compile(tpl)(scope);
element.html("");
element.append(el);
}
});
}
};
})
and the HTML setup is
<form ng-controller="KatController">
<select name="catBreeds" from="${breedList}" ng-change="loadBreedData()"
ng-model="breed.code" />
<div>
<div show-breed template="{{template}}"></div>
</div>
</form>
I need the currently hardcoded value 'b1' in the $http ajax call to be the value in $scope.breed.code.

Your ajax request is async while your controller behaves as if the request were sync.
I assume that the get request has everything it needs to perform right.
First pass a callback to your service (note the usage of fn):
firstModule.factory( 'katTplLoadingService', function ($http) {
return {
fn: function(code, callback) { //note the callback argument
$http.get("${createLink(controller:'kats', action:'loadBreedInfo')}",
params:{code: code}}) //place your code argument here
.success(function (template, status, headers, config) {
callback(template); //pass the result to your callback
});
};
};
});
In your controller:
$scope.loadBreedData = function() {
katTplLoadingService.fn($scope.breed.code, function(tmpl) { //note the tmpl argument
$scope.template = tmpl;
});
}
Doing so your code is handling now your async get request.
I didn't test it, but it must be doing the job.

I think you defined the factory not in right way. Try this one:
firstModule.factory('katTplLoadingService', ['$resource', '$q', function ($resource, $q) {
var factory = {
query: function (selectedSubject) {
$http.get("${createLink(controller:'kats', action:'loadBreedInfo')}", {
params: {
'b1'
}
}).success(function (template, status, headers, config) {
return template;
})
}
}
return factory;
}]);
firstModule.controller('KatController', function($scope, katTplLoadingService) {
$scope.breed = {code:''}
$scope.loadBreedData = function(){
$scope.template = katTplLoadingService.query({code: $scope.breed.code});
}
});

Related

AngularJS broadcast data to iframe

How can I get the username from index.html?
index.html
{{username}}
index controller:
app.controller('index',function($scope,$rootScope,mainService){
$scope.getUsername = funciton(){
mainService.getUsername().then{
function successCallback(response){
$rootScope.username = response.data.username;
}
}
}
})
iframe.html
{{user}}
iframe controller
app.controller('iframe',function($scope,$rootScope){
$scope.user = $rootScope.username
})
but {{user}} show nothing
Also, I tried to post the message by Service like dataService.username result is the same.
Use $rootScope.$broadcast to raise an event, first paramter for the event name and an optional second parameter to pass an argument.
app.controller('IndexController', function ($scope, $rootScope, MainService) {
$scope.getUsername = function() {
MainService.getUsername().then(function (response) {
$rootScope.$broadcast('username-fetched', { username: response.data.username });
});
};
});
Then catch the event on the other controller by using $scope.$on.
app.controller('IframeController', function ($scope) {
$scope.$on('username-fetched', function (event, data) {
$scope.user = data.username;
});
});

Custom directive link executes before response.data gets its data

I have created custom directive as below,
.html:
<input type="text" placeholder="Search" ng-model="selected" typeahead="Customer.CompanyName for Customer in typeaheadSrc | filter:$viewValue" />
.directive:
module.directive("typeahead", [
function () {
return {
restrict: "E",
templateUrl: typeahead.html,
link: function (scope, element, attrs) {
scope.typeaheadSrc = scope.$eval(attrs.datasource);
}
};
}
]);
Above code is just a custom directive where I can use it anywhere. Only thing it does is take datasource (here I use it as attrs.datasource) to display in typeahead.
Below is the way I use it in html I want,
<div class="col-md-10">
<typeahead datasource={{GetAllCustomers}}></typeahead>
</div>
And it pass the data from controller to html like this,
$scope.GetAllCustomers = [
{
"CompanyName ": "Customer1"
},
{
"CompanyName ": " Customer2"
},
{
"CompanyName ": " Customer3"
},
{
"CompanyName ": " Customer4"
}
];
Above code works fine where GetAllCustomers is passed as datasource of custom directive attrs.datasource field above.
Problem is when I use webapi to get data instead of static data in GetAllCustomers. I use factory and service to get data as below,
module.controller("customerController", [“$scope”, "customerFactory",
function ($scope, customerFactory) {
$scope.GetAllCustomers = customerFactory.GetAllCustomers().then(function (result) {
return result;
},
function (error) {
console.log("GetAllCustomers failed");
});
]);
module.factory("customerFactory", [
"customerService",
"$q",
function (customerService, $q) {
var customerObj = {};
var deferred = $q.defer();
customerObj.GetAllCustomers = function () {
return customerService.GetAllCustomers()
.then(function (response) {
deferred.resolve(response.data);
return deferred.promise;
}, function (response) {
deferred.reject(response);
return deferred.promise;
});
};
return customerObj;
}]);
module.service("customerService", [
"$http",”$scope”
function ($http,$scope) {
$scope.GetAllCustomers = function () {
var tempUrl = TEMPLATES_PATH.customer_api;
var request = {
method: GET,
url: tempUrl
};
return $http(request, { withCredentials: true });
};
}
]);
In above code also GetAllCustomers will get value from webapi, but the problem is response will be loaded only after custom directive is executed. Hence attrs.datasource value will be null.
Now the flow is,
Factory request
Typeahead custom directive
Factory response
Custom directive should be executed after factory response, but it will execute after request is done and before response is given back.
Flow should be like below,
Factory request
Factory response
Typeahead custom directive
Please tell me how to do this for above code. I am not able to create fiddle for webapi. Please tell me how to do this. I tried using promise, but its not working for me. I want to know where to write promise if it is needed.
Change like this, i think it will work
first at the directive
<typeahead datasource={{GetAllCustomersValue}} ng-if="GetAllCustomersValue"></typeahead>
second at the factory part
$scope.GetAllCustomers = customerFactory.GetAllCustomers().then(function (result) {
$scope.GetAllCustomersValue=result;
return $scope.GetAllCustomersValue;
},
function (error) {
console.log("GetAllCustomers failed");
});
]);

AngularJS - Multiple Directive Instances making XHR call multiple times

I have an Angularjs directive 'ExampleDirective' which has the controller 'ExampleController'. The controller defines two Promise objects where each Promise object makes an Http GET request and returns the response.
In the directive, we get the response data from the promise objects and process them to render the directive.
ExampleDirective gets instantiated twice within the same view and each instance makes it's own Http GET requests. This causes performance issues on the front end due to two requests sent at the same time to make expensive database calls and read from the same table as well.
Controller:
angular.module('exampleModule')
.constant("EXAMPLE_URL", "{% url 'example:get_example' %}")
.controller('exampleCtrl', ['$scope', '$http', 'EXAMPLE_URL', exampleCtrl]);
function exampleCtrl($scope, $http, EXAMPLE_URL) {
$scope.examplePromise = $http.get(EXAMPLE_URL).then(function(response) {
return response.data;
});
}
Directive:
angular.module('exampleModule')
.directive('exampleDirective', ['exampleFactory', 'STATIC_URL', '$http', '$window', exampleDirective]);
function exampleDirective(exampleFactory, STATIC_URL, $http, $window) {
return {
scope: {
title:'#?',
loadingImage:'#?',
},
restrict: 'AE',
templateUrl: STATIC_URL + 'example/example-template.html',
controller: "exampleCtrl",
link: function (scope, element, attrs) {
//add default options:
if (!scope.title) {
scope.title = 'Example Title';
}
if (!scope.loadingImage) {
scope.loadingImage = '';
}
scope.examplePromise.then(function(data) {
scope.exampleData = data;
// do something
});
}
};
}
Is there a way to instantiate a directive multiple times but not have to make the Http GET requests in the controller twice?
UPDATE
This is what I did, I added a service as suggested in the answer.
Service:
angular.module('chRatingsModule')
.factory('chExampleFactory', ['$http', 'EXAMPLE_URL', chExampleFactory]);
function chExampleFactory($http, EXAMPLE_URL) {
var api = {}
var promise = null;
api.examplePromise = examplePromise;
function examplePromise() {
if (promise == null) {
promise = $http.get(EXAMPLE_URL).then(function(response) {
return response.data;
});
}
return promise;
}
return api;
}
Updated Directive:
angular.module('exampleModule')
.directive('exampleDirective', ['exampleFactory', 'STATIC_URL', '$http', '$window', exampleDirective]);
function exampleDirective(exampleFactory, STATIC_URL, $http, $window) {
return {
scope: {
title:'#?',
loadingImage:'#?',
},
restrict: 'AE',
templateUrl: STATIC_URL + 'example/example-template.html',
link: function (scope, element, attrs) {
exampleFactory.examplePromise.then(function(data) {
scope.exampleData = data;
// do something
});
}
};
}
First solution, probably the best one: don't make the call from the directive, which should just be a graphical element. Do the call from the controller, and pass the data as argument to both directives.
Second solution, use a service in the directive, and always return the same promise:
myModule.factory('myService', function($http) {
var promise = null;
var getData = function() {
if (promise == null) {
promise = $http.get(...).then(...);
}
return promise;
};
return {
getData: getData
};
});
The controller defines two Promise objects where each Promise object
makes an Http GET request and returns the response.
Change to:
The SERVICE defines two Promise objects where each Promise object
makes an Http GET request and returns the response.
The service then can remember that it has already done the GET(s) and just return their result every subsequent time it is asked for them.

Change Http.get Url and show again in ng-repeat

I have url that returns data
I read data with angular
var appModule;
(function () {
appModule = angular.module('Ryka', []);
appModule.controller('TaskControler', ['$scope', '$http', function (scope, http) {
scope.mytask = mytask;
http.get('http://localhost/_vti_bin/RykaSolution/RykaService.svc/MyOverDueTasks/first').success(function (data) {
scope.Tasks = data}).error(function (err) { alert(err) })
}]);
})();
i want to change http.get url using ng-click ... this code executes when the page loads
You could do it like this (simplified example):
(function () {
var appModule = angular.module('Ryka', []);
appModule.controller('TaskControler', ['$scope', '$http', function (scope, http) {
scope.tasks = undefined;
$scope.getTasks = function (type) {
http.get('http://localhost/tasks/' + type).success(function (data) {
scope.tasks = data
}).error(function (err) {
// Handle error
})
}
// Initial loading of data.
$scope.getTasks("overdue");
}]);
})();
Just a little bit of explanation. Calling $scope.getTasks("overdue") would result in the URL like this:
http://localhost/tasks/overdue
You could then call the same method from your view like this:
<a ng-click="getTasks('current')">Get my current tasks</a>
which would result in a such URL:
http://localhost/tasks/current
I hope you got and idea.
PS: You would be better off by moving getTask method into a service.

Angular ui-router get asynchronous data with resolve

I want to display a form with data corresponding to the edited item. I use ui-router for routing. I defined a state:
myapp.config(function($stateProvider) {
$stateProvider.
.state('layout.propertyedit', {
url: "/properties/:propertyId",
views : {
"contentView#": {
templateUrl : 'partials/content2.html',
controller: 'PropertyController'
}
}
});
In PropertyController, I want to set $scope.property with data coming from the following call (Google Cloud Endpoints):
gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
I don't know if I can use resolve because the data are returned asynchronously. I tried
resolve: {
propertyData: function() {
return gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
}
}
First issue, the propertyId is undefined. How do you get the propertyId from the url: "/properties/:propertyId"?
Basically I want to set $scope.property in PropertyController to the resp object returned by the async call.
EDIT:
myapp.controller('PropertyController', function($scope, , $stateParams, $q) {
$scope.property = {};
$scope.create = function(property) {
}
$scope.update = function(property) {
}
function loadData() {
var deferred = $q.defer();
gapi.client.realestate.get({'id': '11'}).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property = deferred.promise;
}
});
You need to read the docs for resolve. Resolve functions are injectable, and you can use $stateParams to get the correct value from your routes, like so:
resolve: {
propertyData: function($stateParams, $q) {
// The gapi.client.realestate object should really be wrapped in an
// injectable service for testability...
var deferred = $q.defer();
gapi.client.realestate.get($stateParams.propertyId).execute(function(r) {
deferred.resolve(r);
});
return deferred.promise;
}
}
Finally, the values for resolve functions are injectable in your controller once resolved:
myapp.controller('PropertyController', function($scope, propertyData) {
$scope.property = propertyData;
});
I think your controller function needs $stateParams parameter from which you can get your propertyId. Then you can use $q parameter and create promise to set $scope.property with something like this:
var deferred = $q.defer();
gapi.client.realestate.get(propertyId).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property=deferred.promise;
Here is description of using promises for handling async calls.
Try this easy way to use resolve in proper way
State code:
.state('yourstate', {
url: '/demo/action/:id',
templateUrl: './view/demo.html',
resolve:{
actionData: function(actionData, $q, $stateParams, $http){
return actionData.actionDataJson($stateParams.id);
}
},
controller: "DemoController",
controllerAs : "DemoCtrl"
})
In the above code I am sending parameter data which I am sending in the url,For examples if i send like this /demo/action/5
this number 5 will go to actionData service that service retrieve some json data based on id.Finally that data will store into actionData You can use that in your controller directly by using that name
Following code return some JSON data based on id which iam passing at state level
(function retriveDemoJsonData(){
angular.module('yourModuleName').factory('actionData', function ($q, $http) {
var data={};
data.actionDataJson = function(id){
//The original business logic will apply based on URL Param ID
var defObj = $q.defer();
$http.get('demodata.json')
.then(function(res){
defObj.resolve(res.data[0]);
});
return defObj.promise;
}
return data;
});
})();
How about this:
function PropertyController($scope, $stateParams) {
gapi.client.realestate.get($stateParams.propertyId).execute(function(resp) {
$scope.property = resp;
});
}

Resources