I'm building a RESTful Webapp with an AngularJS Frontend.
I'm trying to separate the Data - received via JSON - into an Object which can be passed between controllers, following this tutorial:
http://www.webdeveasy.com/angularjs-data-model/
I'm struggling at the point "Sharing a model between controllers ".
My Problem is, that i can't reach the values from inside of the Object, and also not from the controller, which uses the Object. I 'm only able to reach the values with the {{ }} tags in the html template.
HereÅ› the Object:
(function () {
"use strict";
angular
.module('app.admin')
.factory('AdminEventFactory', AdminEventFactory);
/** #ngInject */
function AdminEventFactory($http) {
function AdminEvent(eventData) {
if (eventData) {
this.setData(eventData);
}
};
//AdminEvent Objekt
AdminEvent.prototype = {
setData: function (eventData) {
angular.extend(this, eventData);
},
load: function (id) {
var scope = this;
$http({
method: "GET",
url: "http://docker-backend.test/api/events/" + id
}).then(function mySuccess(response) {
scope.setData(response.data);
}, function myError(response) {
console.log(response);
});
},
//How to reach the values set in the load method via getter here?
getTitle: function(){
console.log(this.setData.title); //undefined
}
};
return AdminEvent;
}
}());
Here's the call from the controller:
(function () {
"use strict";
angular
.module('app.admin')
.controller('Admin.EventsController', AdminEventsController);
/** #ngInject */
function AdminEventsController( [...] , AdminEventFactory) {
[...]
function editEvent($eventId) {
var event = new AdminEventFactory();
event.load($eventId);
console.log(event);
event.getTitle(); //undefined
$scope.event = event;
}
[...]
In the HTML Template could the value be reached:
<!-- "TestTitle" -->
<div>{{ event.title }}</div>
The event logged via console:
AdminEvent {}
description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
endDate: "2019-05-16T22:00:00+00:00"
id: 62
startDate: "2019-05-15T22:00:00+00:00"
title: "TestTitle"
> __proto__: Object
[...]
I like to reach things like title via getter and setter in the controllers, in the model and not only via template tags {{ }}.
Thank you for your help!
Have the load method return a promise:
load: function (id) {
var scope = this;
return $http({
method: "GET",
url: "http://docker-backend.test/api/events/" + id
}).then(function mySuccess(response) {
scope.setData(response.data);
return response.data
}, function myError(response) {
console.log(response);
throw response;
});
},
In the controller, use the returned promise:
function editEvent($eventId) {
var event = new AdminEventFactory();
var promise = event.load($eventId);
promise.then(function(data) {
console.log(data);
console.log(event);
event.getTitle(); //undefined
});
$scope.event = event;
}
The .then method of the promise waits for the data to arrive from the server before executing the console.log statements.
Related
I'd like to create dynamically controllers responsible for view of data from REST API. My idea is to use ng-repeat directive with data from service and inside it create object with ng-controller directive with parameter from ng-repeat output (The most important condition is that each one question must have its own $scope). Unfortunatelly I don't know how to pass data from service.
AngularJS service code
(function () {
'use strict';
angular
.module('App')
.factory('questionsDataService', questionsDataService);
questionsDataService.$inject = ['$http'];
function questionsDataService($http) {
return {
getMetadata: function (taskId) {
var metaData = $http.get('api/toDo/taskVariables/' + taskId).then(
function (response) {
return response.data;
});
return metaData;
},
getQuestionsData: function (taskId) {
var questionsData = $http.get('api/toDo/getQuestions/' + taskId).then(
function (response) {
return response.data;
});
return questionsData;
}
}
}
})();
I'm not sure if I understood the question, your title is misleading, but I will show how to get the data from the service. I don't think you need a new controller for each item in an ng-repeat, but without more info on why you are trying to do this, I can't help there.
(function () {
'use strict';
angular
.module('App')
.controller('myController', myController);
// you are injecting your service here, this will be whatever the string is from the .factory() call
myController.$inject = ['$scope', 'questionsDataService'];
// again, we are passing the service in to the controller function
function myController($scope, questionsDataService) {
// your service calls are returning promises
// this will get run when the controller is initialized
questionsDataService.getMetadata(1).then(function(data){
// here is where you can access the returned metadata
// save it to the scope so you can access it in the DOM
console.log(data);
})
// if you want to call your service on a button click, or with some other function
$scope.getQuestions = function (id) {
questionsDataService.getQuestionsData(id).then(function (data) {
// here is where you can access the returned data
// save it to the scope so you can access it in the DOM
console.log(data);
})
}
// I added a service method that returns a string, rather than a promise
// this will get run when the controller is initialized
var str = questionsDataService.getServiceName();
console.log(str);
}
})();
(function () {
'use strict';
angular
.module('App')
.factory('questionsDataService', questionsDataService);
questionsDataService.$inject = ['$http'];
function questionsDataService($http) {
return {
getMetadata: function (taskId) {
var metaData = $http.get('api/toDo/taskVariables/' + taskId).then(
function (response) {
return response.data;
});
return metaData;
},
getQuestionsData: function (taskId) {
var questionsData = $http.get('api/toDo/getQuestions/' + taskId).then(
function (response) {
return response.data;
});
return questionsData;
},
// adding this just to show you how to access functions that don't return promises
getServiceName: function () {
return "questionsDataService";
}
}
}
})();
I have the following 3 methods in my module.factory dataservice I am using Angular 1.5
getCannedJSON . This function works as intended and i would like the others to behave the same way. I copy and pasted the JSON i got from my webAPI in postman and put this in to the function. It returns an array of objects like i want.
getDataFromAPI. For some reason I cannot get this function to return the response. The console.log(response) has exactly the data I want aka the same data as getCannedJSON. Instead it returns a d {$$State: object} any idea how i could alter this code to change have it return in the same format as the getCannedJson method?
getDataFromApiWithDynamicUrl this is no different than the above method but it will take a dyanmic url for the web api. It workds fine minus it not returning an array list of json objects it instead returns the same $$State object.
I would like getDataFromAPI to return the same array of all the objects in the json request like getCannedJson does. Any ideas where I am messing up. Below is a screenshot of the two different types of objects they are returning via console.log I would like the data at the bottom to look like the data at the top.
The code for the dataService module factory is below
(function (module) {
'use strict';
DataService.$inject = ['$http', '$q'];
function DataService($http, $q) {
var getDataFromAPI = function () {
var returnthis;
return $http({ //this top level returns instead
url: "http://localhost:34183/api/quality/month",
dataType: 'json',
method: 'GET',
}).success(function (response) {
console.log("This Response shown below is pefect! but it wont return....");
console.log(response);
return (response);//This never returns
}).error(function(error){
console.log(error);
});
return returnthis;
};
var getDataFromApiWithDynamicUrl = function (pathurl) { // this is version 2 of the method i want to work where i can dynamically change the url to the path
return $http.get(pathurl);
};
var getCannedJSON = function ($http) {
return [{
"hockeyTeam": "Sharks",
"PlayoffRecord": {
"wins": "0"
},
},
{
"hockeyTeam": "Pengiuns",
"PlayoffRecord": {
"wins": "1"
},
}
];
};
return {
getDataFromAPI: getDataFromAPI,
getDataFromApiWithDynamicUrl: getDataFromApiWithDynamicUrl,
getCannedJSON: getCannedJSON
};
}
module.factory('DataService', DataService);
})(angular.module('MyCoolModule'));
below is the code where i call these methods to consume the JSON data in my controller.
(function (module) {
'use strict';
hockeyViewController.$inject = ['DataService'];
function hockeyViewController(DataService) {
var vm = this;
vm.headers = [
{ name: 'Hockey Team', key: 'hockeyTeam' },
{ name: 'Record', key: 'PlayoffRecord'}
];
vm.cannedData = angular.copy(DataService.getCannedJSON());
vm.getDataFromAPI = DataService.getDataFromAPI();
vm.getDataFromAPIwithCustomURL = [];
DataService.getDataFromApiWithDynamicUrl("http://localhost:34183/api/quality/month").then(function(response){
console.log("this response should work - and it does it in the right format");
console.log(response.data);// this looks perfect
vm.getDataFromAPIwithCustomURL = response.data;
return response.data;
}, function (error) {
console.log(error);
});
vm.testMonthResults2 = angular.copy(DataService.getDataFromApiWithDynamicUrl("http://localhost:34183/api/quality/month"));
console.log("canned json Data- works great");
console.log(vm.cannedData);// this works perfectly
console.log("this is the data results with dynamic url - returns wrong object the $$state ");
console.log(vm.getDataFromAPI);// returns $$state not array of objects
console.log(vm.getDataFromAPIwithCustomURL); // this returns [] which is wrong
console.log(DataService.getDataFromApiWithDynamicUrl("http://localhost:34183/api/quality/month"));
// this doesnt work either
}
function reportTabularViewDirective() {
return {
restrict: "E",
controller: hockeyViewController,
controllerAs: 'vm',
bindToController: true,
scope: {
},
templateUrl: "app/widgets/hockey-view.html"
};
}
module.directive('hockeyView', hockeyViewDirective);
})(angular.module('MyCoolModule'));
Can try this one
var getDataFromAPI = function () {
return $http({
url: "/api/quality/month", // try using relative path
dataType: 'json',
method: 'GET',
}).then(function(response) {
console.log(response);
return respose.data;
}, function(error) {
console.log(error);
return [];
});
};
But better to use like: service return only promise and in controller use then function to handle response
In service:
var getDataFromAPI = function() {
return $http.get('/api/quality/month');
};
in controller:
DataService.getDataFromAPI().then(function(response) {
console.log(response.data);
}, function(error) {
console.log(error);
});
You get a $promise object by calling DataService.getDataFromAPI(). You need to handle the $promise object to get the response.
DataService.getDataFromAPI().then(function(response) {
// console.log(response);
})
The same applies when you getDataFromApiWithDynamicUrl() function.
For more info, see doc:
$http
$q
As you may know, AngularJS $http service is allowing to call it with/out specific function, for ex:
$http(req).then(function(){...}, function(){...});
$http.get('/someUrl', config).then(successCallback, errorCallback);
I would like to get some more information about the way I can implement it on my factory and generally in JS.
Functions are Objects in JavaScript. This means that you can assign other properties and functions on a function.
function foo(){
//do something
}
foo.bar = function(){
//do something else
}
As it was mentioned above you can implement what you want using Angular's '$resource'. Here is an example of how it can be used:
app.service('testResource', ['$resource', function ($resource) {
var apiBaseUrl = 'http://test-backend/api';
var testResource = $resource(
apiBaseUrl + '/test/'
{},
{
'query': {
method: 'GET',
isArray: true
}
}
);
this.getAll = function () {
return testResource
.query()
.$promise
.then(function (data) {
var tests = [];
angular.forEach(data[0], function (value) {
tests.push(value);
});
return tests;
});
};
}]);
Then inject it in Controller (or wherever) and call it:
testResource.getAll().then(
function (data) {
$scope.tests = data;
}
);
You can also implement other methods such as POST, PUT, DELETE.
I have one factory contains save customer function.On success I want to pass its response in controller so that i can update the view.
Factory
sampleApp.factory("authFactory", function($location, $http, transformRequestAsFormPost) {
return {
saveCustomer: function(data) {
var request = $http({
method: "post",
url: "webservice/ws.php?mode=saveCustomer",
transformRequest: transformRequestAsFormPost,
data: data
});
request.success(
function(response) {
console.log(response);
}
);
}
};
});
Controller
sampleApp.controller('customerController', function($scope, testService,authFactory,$http) {
$scope.addCustomer = function() {
var data = {name: $scope.customerName,city: $scope.customerCity};
// Calling Factory Function
authFactory.saveCustomer(data);
// How to fetch response here
}
});
Please help me to solve that problem
Thanks
Various ways, the first one that comes to mind is something like this:
//in your factory
return {
saveCustomer: function(data) {
var request = $http({...});
return request;
}
}
//in your controller
authFactor
.saveCustomer(data)
.success(function() {
//update controller here
})
You are working with "promises" here. You can do a few different things depending on what you return from your service method.
One thing you can do is simply return the promise and handle it in your controller.
service:
return {
saveCustomer: function(data) {
return $http({...});
}
}
contoller:
authFactor.saveCustomer(data).success(function(customer) {
$scope.customer = customer;
})
Another thing you can do is return an object reference and put it on your scope. When the object is filled, it will be updated in your scope.
service:
return {
saveCustomer: function(data) {
var customer = {};
$http({...}).success(function(data){
angular.copy(data, customer);
});
return customer;
}
}
controller:
$scope.customer = authFactor.saveCustomer(data);
The advantage of this second way is that most of your logic stays in your service. You controller stays simple and doesn't have to know about promises or handle them.
I have a $resource whose API will always return some data that needs to be cleaned up before going into the presentation layer. Specifically, it's .NET returning Date objects in the lovely '/Date(...)/' format.
I don't want to have to write a callback every time I call .query() or .get(). Is there some way to extend the resource with a callback that gets called upon REST methods that update the instance's properties, or by adding some sort of $watch that gets fired when the date property changes? Basically something that will happen for every instance of this $resource.
angular.module('myAppServices', ['ngResource'])
.factory('Participant', ['$resource', function ($resource) {
var res = $resource('api/url/participants/:id', { id: '#id' });
// This obviously doesn't work, but something kinda like this?
res.prototype.$watch(this.FieldName, function(newVal, oldVal) {
if (needsCleaning(newVal.fieldName) {
this.FieldName = cleanupField(newVal);
}
};
});
Ah-ha, I found a way around it and will leave it here. In version 1.1.2 they added support for passing all the $http.config options to a $resource. Naturally, the CDN I'm using doesn't have a recent enough version of angular-resource.js, but switching CDNs solved that.
I just used the transformResponse option to modify the data as it comes back.
angular.module('myAppServices', ['ngResource'])
.factory('Participant', ['$resource', '$http', function ($resource, $http) {
var res = $resource('api/url/participants/:id', { id: '#id' }, {
save: {
method: 'POST',
transformResponse: $http.defaults.transformResponse.concat([
function (data, headersGetter) {
data.FieldName = yourDateParsingFunction(data.FieldName);
return data;
}
])
}
});
I'm just adding my transformer on to $httpProvider's transformResponse, which will do all the deserialization, etc.
An easy way to do this is to overwrite the existing $resource methods you want to do post-processing on with your own. See the code and comments below for an example.
angular.module('myAppServices', ['ngResource'])
.factory('Participant', ['$resource', function ($resource) {
var res = $resource('api/url/participants/:id', { id: '#id' }, {
// create aliases for query and get to be used later
_query: { method: 'GET', isArray: true },
_get: { method: 'GET' }
});
// redefine the query method
res.query = function() {
// call the original query method via the _query alias, chaining $then to facilitate
// processing the data
res._query.apply(null, arguments).$then(function(res) {
var data = res.data;
// do any processing you need to do with data here
return data;
});
};
// redefine the method
res.get = function() {
// call the original get method via the _get alias, chaining $then to facilitate
// processing the data
res._get.apply(null, arguments).$then(function(res) {
var data = res.data;
// do any processing you need to do with data here
return data;
});
};
return res;
});
You'd use it the same way you're currently using Participant in your code, via Participant.query() or Participant.get(). The data you return in the chained $then handler will be used to resolve the promise returned by $resource.
The way I did it was by adding a service to the module:
angular.module('keeniolab', ['ngResource']).
factory('KeenIO',function ($resource) {
// factory implementation
}).service('KeenModel', function (KeenIO) {
var KeenSession = function () {
this.data = {};
};
KeenSession.prototype.fetch = function (query) {
var self = this;
KeenIO.get(query, function (result) {
self.data = result;
});
};
return new KeenSession();
});
Now you can simply monitor the collection:
$scope.$watchCollection(function () {
return KeenModel.data;
},
function (value) {
// code here
});
Keen.IO Resource Factory with Service Model