Adding a new item with $recource in Angular JS - angularjs

I have created a simple app to store an event to my database. It is working fine but with a wordaround.
I use the following method:
controllers.controller('CalendarAddController', function($scope, $routeParams, Event) {
$scope.addEvent = function() {
$scope.ev=new Event();
$scope.ev.title = $scope.event.title;
$scope.ev.$save().then(function(res) { console.log("success");})
.catch(function(req) { console.log("error saving obj"); })
.finally(function() { console.log("always called") });
}
});
After submitting my form, the function addEvent is called.
If I print the value of $scope.event, it has the right values.
However, when I call .$save() on it, I get the error "TypeError: $scope.event.$save is not a function."
But when I create a new object and assign the values to it, it works fine.
Why isn't it working directly? Always creating a dummy object does not seem to be best practice I suppose.
The service I created
services.factory( 'Resource', [ '$resource', function( $resource ) {
return function( url, params, methods ) {
var defaults = {
update: { method: 'put', isArray: false },
create: { method: 'post' }
};
methods = angular.extend( defaults, methods );
var resource = $resource( url, params, methods );
resource.prototype.$save = function() {
if ( !this.id ) {
return this.$create();
}
else {
return this.$update();
}
};
return resource;
};
}]);
services.factory( 'Event', [ 'Resource', function( $resource ) {
return $resource( '/api/calendar/:id' );
}]);
Update: basic example
Using this example everything works front-end but in my rest API the data is not passed.
<!DOCTYPE html>
<html lang="en-US">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-route.min.js"></script>
<script type="text/javascript">
var app = angular.module('app', ['ngRoute','ngResource'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'pages/home.html',
controller : 'mainController'
})
});
app.factory('Event', function($resource) {
return $resource('/test/api/:id');
});
app.controller('mainController', ['$scope', '$routeParams', 'Event',
function($scope, $routeParams, Event) {
function handleSuccess(data) {
alert("success");
}
function handleError(data) {
alert("error");
}
var event = new Event();
event.title = "a title";
Event.save(event, handleSuccess, handleError);
}]);
</script>
<body>
<div ng-app="app">
<div ng-view></div>
</div>
</body>
</html>
For the back-end I created a very basic script which writes the request to a file:
It always says "array()" so no data is passed.
<?php
$myFile = "testFile.txt";
$fh = fopen($myFile, 'w') or die("can't open file");
$stringData = print_r($_REQUEST,true);
fwrite($fh, $stringData);
fclose($fh);
?>

Where do you initialize $scope.event or is it automatically created by angular? If this is an plain JavaScript object you can't call .$save, but you can use the Event service itself - no need to create a new instance:
Event.create($scope.event)

Related

JavaScript runtime error: [$injector:modulerr] in Angular Js

I am implementing logic through ui-router, Factory and Directive but getting error: JavaScript runtime error: [$injector:modulerr] in Angular Js.
Ui-Routing was working fine.
Index.html file:
<html><head><title>Employee Management System</title>
<link href="Content/bootstrap.css" rel="stylesheet" />
<script src="Scripts/jquery-1.9.1.min.js"></script>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-ui-router.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/app/EmpRecord.js"></script>
<script src="Scripts/app/GetDataService.js"></script>
<script src="Scripts/app/EmpController.js"></script>
<script src="Scripts/app/EmpApp.js"></script></head>
<body ng-app="EmpApp">
<div class="page-header">Employee Management System
</div><div ng-include="'pageContents/menu.html'"></div>
<ui-view></ui-view></body></html>
EmpApp.js
var app = angular.module("EmpApp", ['ui.router']);
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope', 'EmpFact',EmpController])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
templateUrl: '/home.html'
})
.state('Add', {
templateUrl: '/AddEmployee.html'
})
.state('List', {
templateUrl: 'ListEmp.html',
controller: 'EmpController'
}
)
})
.directive('emp-Record', EmpRecord);
ListEmp.html:
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
EmpController
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
GetDataService.js
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}
}
}
All Errors are gone Now but data is not coming.
In short:
Controller:
var EmpController= function ($scope,EmpFact) {
$scope.Employees = EmpFact.GetData();
console.log($scope.Employees);
};
Service:
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}}}
Àpp.js
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope','EmpFact', EmpController])
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td>"
}});
HTML:
<div>
<div><h3>List of Employees</h3></div>
<div emp-Record ng-repeat="e in Employees"></div>
</div>
Ok, so as I suggested in the comment, because the error implies that you haven't injected the EmpFact factory into EmpController, changing
.controller('EmpController', ['$scope', EmpController])
Into:
.controller('EmpController', ['$scope', 'EmpFact', EmpController])
And also injecting it to the controller function:
var EmpController = function ($scope, EmpFact) { ... };
Made the error disappeared, but now you say that "data is not coming".
I suggest another change in your factory, instead of your current code, try this:
var EmpFact = function ($http) {
return {
GetData: function () {
// return a promise which resolve with the actual data returned from the server
return $http.get('http://localhost/EmployeeApi/api/Emp').then(
function (response) {
// return the actual results, instead of the whole response from the server
return response.data;
}
);
}
}
};
Now, in your controller, you should be able to get the data like this:
var EmpController = function ($scope, EmpFact) {
// Call the "GetData" from the factory, which return a promise with the actual results returned from the server
EmpFact.GetData().then(
function(data) {
// in the resolve callback function, save the results data in
// any $scope property (I used "$scope.Employees" so it will be
// available in the view via {{ Employees | json }})
$scope.Employees = data;
}
);
};
By returning a promise you are guaranteed to be able to handle the results returned from an asynchronous request (AJAX). You should be able to use the results in the view like this:
<div emp-Record ng-repeat="e in Employees"></div>
(Note that the above HTML snippet is taken from the comments below this answer)
Edit:
Looking at your directive, it doesn't look like a correct way to construct a table. Change emp-Record to emp-record and wrap it in a <table> tag to make it a valid HTML:
<table>
<tr emp-record ng-repeat="e in Employees"></tr>
</table>
And in your directive's template make sure you close the row tag (Add </tr>):
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td></tr>"
}
});
Thanks Alon for your help as I am new to Angular, converting my ASP.NET MVC code to HTML5/Angular only.
Finally I am able to resolve it.
Data Service/Factory:
var EmpFact = function ($http) {
return {
GetData: function () {
return $http.get('http://localhost/EmployeeApi/api/Emp');
}
}
}
Controller:
var EmpController = function ($scope, EmpFact) {
//EmpFact.GetData() is a promise.
EmpFact.GetData().then(
function (result) {
$scope.Employees= result.data;
}
);
}

Multiple http.get in service

I wanna use multiple ( in this case, 2 ) $http.gets in my service !
As you know the simple form of using $http.get is this :
app.factory('MyService', function ($http, $q) {
return {
getData: function() {
return $http.get('myfile.json')
.then(function(response) {
return response.data;
});
}
};
});
Now I wanna use 2 files ( 2 $http.gets ) and compare them to each other ( with some for loops and etc that I can ... ) !
What can I do now ? :(
use $q.all.
Add $q to controller's dependencies, exemple
$scope.req1 = $http.get('myfile.json');
$scope.req2 = $http.get('myfile2.json');
$q.all([$scope.req1, $scope.req2]).then(function(data) {
// data is array of your files
if ( JSON.stringify(data[0]) === JSON.stringify(data[1])){
console.log('is equal');
}
});
It is an extension of Hajji Tarik's solution. I was able to derive from your comments that you were still not clear with what to code in where. So I have developed a sample application which will assist you for the same.
//--app.module.js--//
angular.module('notesApp', []);
//--app.service.js--//
angular.module('notesApp')
.factory('notesFactory', ['$http',
function($http) {
var notesService = {};
notesService.getData = function(url, method) {
return $http({
url: url,
method: method
});
}
return notesService;
}
]);
//--app.controller.js--//
angular.module('notesApp')
.controller('MainController', ['$scope', '$http', '$log', '$q', 'notesFactory',
function($scope, $http, $log, $q, notesFactory) {
$scope.data = {};
var data1 = notesFactory.getData('http://localhost:3000/api/notes/1', 'GET');
var data2 = notesFactory.getData('http://localhost:3000/api/notes/2', 'GET');
var combinedData = $q.all({
firstResponse: data1,
secondResponse: data2
});
combinedData.then(function(response) {
$log.log(response.firstResponse.data);
$log.log(response.secondResponse.data);
//Write your comparison code here for comparing json results.
}, function(error) {
$scope.data = error;
});
}
]);
<html ng-app='notesApp'>
<head>
<title>
Notes Application
</title>
</head>
<body>
<div ng-controller='MainController'>
</div>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js'></script>
<script src='app.module.js'></script>
<script src='app.controller.js'></script>
<script src='app.service.js'></script>
</body>
</html>

Chaining AngularJS Resource calls within the transformResponse function

I want to chain 2 resource calls together but keep it invisible from the consuming application/controller.
For example: resource "Person" has multiple "Role" records, however I want to keep "Person" and "Role" as separate resources on the server.
So the application calls the "Person" resource for a list of people but before they are returned the resource's transformReponse method calls the "Roles" resource for each person and adds the roles as an array. Therefore the application simply asks for people and gets all people with associated roles.
I've tried to simplify the scenario in the example code listed below. Here the application calls 1 resource which then calls the 2nd, however the data from the 2nd resource is resolved after the call to the initial resource in the controller returns.
Any ideas how to do this would be much appreciated.
angular.module('services', ['ngResource'])
.factory("someService2", function ($resource) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.coolThing = 'BOOM-SHAKA-LAKA-V2';
return data;
}
}
}
);
});
angular.module('services')
.factory("someService", function ($q, $resource, someService2) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
data.title1 = "Resource1";
var defer = $q.defer();
// Call 2nd resource
someService2.get(function(d){
data.title2 = d.coolThing;
defer.resolve(data);
});
return defer.promise;
}
}
}
);
});
var app = angular.module('myApp', ['ngResource', 'services']);
app.controller('MainController', ['$scope', 'someService', function ($scope, svc) {
$scope.title1 = 'Transform Test';
$scope.title2 = 'Transform Test2';
var promise = svc.get().$promise.then(function(data){
$scope.title1 = data.title1;
$scope.title2 = data.title2;
});
}]);
The HTML is very simple:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0-rc.2/angular-resource.js" ></script>
<script type="text/javascript" src="JScript.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<h1>{{title1}}</h1>
<h1>{{title2}}</h1>
</div>
<div>
</body>
</html>
I have finally come to the conclusion that using the TransformResponse function of the Resource will not work as required as it looks like the resource does some external wrapping of the data further up the promise pipeline. ie. the TransformResponse happens before the promise is resolved internally.
My final solution was to simply abstract the resource call in my own object and chain my promises here. This still allows me to abstract the nested calls from the controller which was my main requirement.
Service2:
angular.module('services', ['ngResource'])
.factory("someService2", function ($resource) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.coolThing = 'BOOM-SHAKA-LAKA-V2';
return data;
}
}
}
);
});
Service1, which encapsulates the nested calls:
angular.module('services')
.factory("someService", function ($q, $resource, someService2) {
var r = $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.title1 = 'BOOM-SHAKA-LAKA-V1';
return data;
}
}
}
);
var svc = {
get: function(){
var data;
var defer = $q.defer();
r.get().$promise.then(
function(x){
data = x;
someService2.get().$promise.then(
function(y){
data.title2 = y.coolThing;
defer.resolve(data);
}
);
}
);
return defer.promise;
}
};
return svc;
});
Controller:
var app = angular.module('myApp', ['ngResource', 'services']);
app.controller('MainController', ['$scope', 'someService', function ($scope, svc) {
$scope.title1 = 'Transform Test';
$scope.title2 = 'Transform Test2';
svc.get().then(function(data){
$scope.title1 = data.title1;
$scope.title2 = data.title2;
});
}]);
HTML:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0-rc.2/angular-resource.js" ></script>
<script type="text/javascript" src="http://localhost/Angular/NestedResources/JS.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<h1>{{title1}}</h1>
<h1>{{title2}}</h1>
<pre>
{{data}}
</pre>
</div>
<div>
</body>
</html>

$http.get(url) not returning data

I am building a service in angular and injecting the service in controller. I am trying to fetch data from json file and am using $http. however the data is not getting returned and i get undefined.
I am updating my code as per suggestion by #Phil
Service.js
;(function(app) {
app.factory('authService', ['$log', '$http','$location', function($log, $http,$location) {
var url = 'js/user.json';
var authService= {};
var userExist=null;
authService.authenticate = function(userid) {
var userobj = $http.get(url).success(function (data) {
userExist = data
console.log(data);
return userExist;
$log.info("Loaded users");
})
.error(function (error) {
$log.info(error);
$log.info("No user exists");
return error;
})
return userobj;
}
return authService;
}]);
})(angular.module('userApp'));
Controller.js
;(function(app) {
app.controller('Controller', ['$scope', '$log','$location','authService', function($scope,$log,$location,authService) {
$scope.data={};
$scope.getUsers = function()
{
userid = "123";
$scope.data = authService.authenticate(userid);
console.log($scope.data);
return $scope.data ;
}
}])
}(angular.module('userApp')));
index.html
<div class="main" ng-controller="Controller">
<input type="button" name="btngetusers" ng-click="getUsers()"></input>
</div>
<script src ="js/app.js"> </script>
<script src ="js/controller/Controller.js"> </script>
<script src ="js/services/Service.js"> </script>
user.json
i have placed the json file under the js directory.
[
{
"UserId": "1",
"FName": "Hice",
"LastName": "Harry"
},
{
"UserId": "2",
"FName": "Andrew",
"LastName": "Ads"
}
]
The data is getting returned as undefined. what am i missing here?
UPDATED CODE
I am updating my code as per suggestion by #skubsi
Service.js
;(function(app) {
app.factory('authService', ['$log', '$http','$location', function($log, $http,$location) {
var url = 'js/user.json';
var authService = {};
var userExist=null;
authService.authenticate = function(userid,success,error) {
$http.get(url).success(function(data){
success(data);
})
.error(error);
};
return authService;
}]);
})(angular.module('userApp'));
Controller.js
;(function(app) {
app.controller('MainController', ['$scope', '$log','$location','authService', function($scope,$log,$location,authService) {
var self = this;
this.data = null;
this.getUsers = function(){
function success(response){
self.data = response;
}
function error(){
console.log("error");
}
authService.authenticate(1,success,error);
}
}])
}(angular.module('userApp')));
index.html
<div class="main" ng-controller="MainController as main">
{{main.data}}
<input type="button" name="btngetusers" value ="Get User" ng-click="main.getUsers()"></input>
</div>
<script src ="js/app.js"> </script>
<script src ="js/controller/MainController.js"> </script>
<script src ="js/services/authenticate.js"> </script>
First things first: your JSON is invalid, you can verify this yourself by entering the JSON you supplied in JSONLint.
Parse error on line 2:
[ { UserId: 123,
--------------^
Expecting 'STRING', '}'
Secondly you pass a unknown service into your controller:
authenService
Then you should realize a promise is code that will run asynchronously, meaning that:
userid = "123";
$scope.data = authService.authenticate(userid);
console.log($scope.data);
return $scope.data ;
will not run synchronously. console.log($scope.data); Will be executed long before your authenticate method will be done. So you need to find a way to make your controller handle accordingly whilst keeping concerns separated. (and not falling into a deferred-anti-pattern).
You could for example add additional parameters to your authenticate function, which will enable the function to call back the original caller.
authService.authenticate = function(userid, success, error) { //success and error are functions
$http.get(url).success(function(data) {
//separation of concerns:
//execute logic.. set flags, filter w/e belongs to your authentication process.
success(data);
})
.error(error); //no processing required
};
So that in your controller all that is left to do is calling the authService and providing it a way to set your data:
this.getUsers = function() {
//This will enable to set the response to your controllers data model.
function success(response) {
self.data = response;
window.alert(response);
}
function error() {
window.alert('shite happened');
}
authService.authenticate(1, success, error);
};
Note that I have used the controllerAs syntax instead of $scope. To prove this mechanism works I created a plunker for you to investigate.
Your authenticationService.authenticate method doesn't return anything.
Specifically, the service name is authService and you're calling authenticationService.authenticate.

How to change the contents of an AngularJS view at runtime?

Inside an AngularJS controller I do a $http.post() request and update $scope with new data which works fine.
But in case of an error, I want to change the template of the same controller to another one. I haven't figured out how to change the template of a controller.
Here is some pseudo code inside the controller:
// this is the controller
var self = this
$http
.post('/something', $scope.something)
.success(function(data) {
$scope.result = data
})
.error(function(err) {
$scope.error = err
var statusCode = err.statusCode
// for example on a 403, I want the template 'errors/403.html' to
// be rendered
self.template = $templateCache.get('errors/' + statusCode + '.html')
})
Any ideas how this is possible? I do not want to put '/errors/403' to the routing because it is not idempotent.
Thanks for any suggestions. I am a bit desperate here ...
try like
$location.path('/login');
first of all you should setup the route provider and use then use $location service to change the URL.
configure route provider :
var app = angular.module("myApp", ["ngRoute"]);
app.config(function ($routeProvider) {
$routeProvider.when("/error", { controller: "", templateUrl: "/app/error.html" });
// setup some more routes here as above
$routeProvider.otherwise({ redirectTo: "/login" });
And in JS file -
var self = this
$http
.post('/something', $scope.something)
.success(function(data) {
$scope.result = data
})
.error(function(err) {
$scope.error = err
var statusCode = err.statusCode
// for example on a 403, I want the template 'errors/403.html' to
// be rendered
$location.path("/error");
})
Don't forget to add the angular-route.js file.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body data-ng-app="myApp">
<div data-ng-view=""></div>
<script src="Scripts/angular-route.js"></script>
</body>
</html>
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives'], function ($routeProvider, $locationProvider, $httpProvider) {
var interceptor = ['$rootScope', '$q', function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
window.location = "./index.html";
return;
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);

Resources