JavaScript functions confusion - javascript

I am in the process of learning AngularJS, and have the following code (from video tutorials I'm going through).
eventsApp.controller('EventController',
function EventController($scope, eventData) {
eventData.getEvent(function(event) {
$scope.event = event;
});
I am confused about this bit of the JavaScript eventData.getEvent(function(event) { } I admit that my JavaScript knowledge isn't super, but JavaScript functions are so strange in this regard compared to C# or Java functions/methods.
I understand that eventData is my AngularJS service and on that, I have a method called getEvent() but that does not take in an event object as a parameter! Here is my AngularJS service code:
eventsApp.factory('eventData', function($http, $log) {
return {
getEvent: function(successcb) {
$http({ method: 'GET', url: 'data/event/1.json' }).
success(function(data, status, headers, config) {
successcb(data);
}).
error(function(data, status, headers, config) {
$log.warn(data, status, headers, config);
});
}
}
});
Even when I tell myself "ignore object types and classes (thinking along C# / Java lines) and think of it as a plain object", then still I don't get where the event parameter in eventData.getEvent(function(event) { } came from!
Can someone please clarify?
Thanks

You're not actually passing event as the parameter, you're passing that entire function as the parameter as a callback. When getEvent reaches .success, it's calling that function (called successcb in the service), and passing data (where data is actually the event parameter that you're seeing in your controller.
It actually looks more like this:
eventsApp.factory('eventData', function($http, $log) {
return {
getEvent: function() {
$http({ method: 'GET', url: 'data/event/1.json' }).
success(function(data, status, headers, config) {
// successcb(data);
// this is essentially what's happening, except in your controller...
// ...so, this won't actually work because you don't have $scope in this service
// but it's the same idea
successcb(data);
var successcb = function(event) {
$scope.event = event;
};
}).
error(function(data, status, headers, config) {
$log.warn(data, status, headers, config);
});
}
}
});

Javascript functions can be treated as inline objects and can be passed as parameters.
for example you can have something like the below (pseodo code):
var x = function fun(){alert ('hi');};
callerFunction(x);
which is same as
callerFunction(function fun(){alert ('hi');});
Above is what is the concept behind and all modern JS libraries (like jQuery) leverage it to include all callback functions as parameters.

Your service method is waiting for variable REFERENCE_TO_FUNCTION.
Maybe this example will make it more clear:
eventsApp.factory('eventData', function($http, $log) {
return {
getEvent: function(REFERENCE_TO_FUNCTION) {
$http({ method: 'GET', url: 'data/event/1.json' }).
success(function(data, status, headers, config) {
REFERENCE_TO_FUNCTION(data);
}).
error(function(data, status, headers, config) {
$log.warn(data, status, headers, config);
});
}
}
});

Related

Defer execution of page controller until init data is gathered

I have an SPA. I have some basic init data that I'm fetching from the server that I'm certain that I want to defer every page load until that data is loaded. (this data contains whether the user is logged in, permissions, and other vital stuff). So if I have a service for fetching and accessing that data, a page controller might start execution before I have the data, which is bad.
I can't use a promise either, partly because it doesn't solve my problem that I don't want the page to begin loading, and partly because it can't be updated easily and I don't want to always use a promise to fetch this basic data
this is what i've tried so far:
my service
app.factory('AppData', function($q, $http){
var appData = {};
$http
.post( "/api/GeneralActions/getInitData", {
}).success(function (data, status, headers, config) {
appData = data;
}).error(function (data, status, headers, config) {
});
return {
getAppData: function () {
return appData;
}
};
});
my page controller:
app.controller('MainPreferences', function($scope, AppData){
// when this gets executed, appData is null
$scope.appData = AppData.getAppData();
});
Try following snippet
app.factory('AppData', function($q, $http){
var appData = {};
$http
.post( "/api/GeneralActions/getInitData", {
}).success(function (data, status, headers, config) {
//appData = data;
angular.extend(appData, data);
}).error(function (data, status, headers, config) {
});
return {
getAppData: function () {
return appData;
}
};
});
Instead creating appData object again, just extend it with data . By this way your appData object pointer will not change and controllers will also get updated.
Are you using ngRoute? If so, it sounds like what you want is to have a resolve property on your routes to require them to load something before changing the path to the new route.
See the ngRoute docs and search for resolve.
If you are using the stock Angular ngRoute routing system, you can use the resolve property on the route to specify a map of promise-returning functions. The route controller will not be initialized before these promises are all resolved, and as a bonus, the promises' results are injected into the route controller.
For example:
$routeProvider.when('/foo', {
controller: 'fooCtrl',
resolve: {
bar: function($http) { return $http.get('/load/the/bar'); }
}
});
// bar is injected from the route resolve
myApp.controller('fooCtrl', function($scope, bar) {
$scope.bar = bar;
});
I think it should be:
app.factory('AppData', function($q, $http){
var appData = {};
return {
getAppData: function () {
$http.post( "/api/GeneralActions/getInitData", {}).success(function (data, status, headers, config) {
return data;
}).error(function (data, status, headers, config) {
});
}
};
});

Angular doesn't update my object after http request

this is my angular code:
samirsoftApp.controller("OrderCtrl",function($scope,$http){
$scope.currentStep=1;
$scope.defaultQuantity=1;
$scope.item={};
$scope.getItem = function(){
$http.get('/test/getItemDetails/')
.success(function(data, status, headers, config) {
$scope.$apply(function(){
$scope.item = data;
});
console.log($scope.item);
})
.error(function(data, status, headers, config) {
console.log(data)
});
};
});
when it request and get answer, it does not update my item object, so I used $apply and it did not work and throw an error:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.13/$rootScope/inprog?p0=%24digest
S/<#https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:6:417
.....
I also tried
$timeout(function() {
$scope.item = data;
}, 0);
instead of $apply` but it prints a null object in consol
it's like this:
samirsoftApp.controller("OrderCtrl",function($scope,$http,$timeout){
$scope.currentStep=1;
$scope.defaultQuantity=1;
$scope.item={};
$scope.getItem = function(){
$http.get('/test/getItemDetails/')
.success(function(data, status, headers, config) {
$timeout(function() {
$scope.item = data;
}, 0);
console.log($scope.item);
})
.error(function(data, status, headers, config) {
console.log(data)
});
};
});
What should I do for my object to be updated after a http request.
thanks
You can assign the data returned by $http.get() request like this
$http.get('/test/getItemDetails/')
.success(function(data, status, headers, config) {
$scope.item = data;
console.log($scope.item);
})
.error(function(data, status, headers, config) {
console.log(data);
});
};
No need for $apply as angular automatically updates $scope
I suspect the problem is elsewhere with your code
Possibly:
You defined function#getItem which contained the $http request but where did you call it?
Make sure the call to URL /test/getItemDetails/ is
returning data by manually browsing or by using a utility like POSTMAN.
being called with the proper URL - try checking in developer tools > network for 404 Errors
I have updated your code - demo here - http://jsbin.com/notudebiso/1/edit?html,js,output
(using a $http call to github api)

Angular http json request issue

Hello I have a simple question but I'm running into problems. I edited some code that I found on line. I'm trying to utilize an Angular http service to retrieve JSON data but it doesn't seem to be working
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $http) {
$http.get('https://www.dropbox.com/s/325d678ksplb7qs/names.json')
sucess(function(data, status, headers, config) {
$scope.posts = data;
}).
error(function(data, status, headers, config) {
// log error
});
});
My code example is below
http://codepen.io/jimmyt1001/pen/dPVveN
You spelled wrong sucess should be success
CODE
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $http) {
$http.get('https://www.dropbox.com/s/325d678ksplb7qs/names.json')
.success(function(data, status, headers, config) {
$scope.posts = data;
}).
error(function(data, status, headers, config) {
// log error
});
});
you should use a service for this:
json.service('getJson', ['$http', function ($http) {
var promise = null;
//return service
return function () {
if (promise) {
return promise;
} else {
promise = $http.get('url');
return promise;
}
};
}]);
function MainCtrl($scope, getJson) {
getJson().success(function (data) {
$scope.json = data;
});
};
Remember to inject the service name in your controller.
tl;dr
It should be like this:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $http)
{
$http.get('https://www.dropbox.com/s/325d678ksplb7qs/names.json')
.success(function(data, status, headers, config)
{
$scope.posts = data;
})
.error(function(data, status, headers, config)
{
// log error
});
});
I.e. you're missing a dot (.) before success and your success is incorrectly typed (you type sucess).
Original
Your code should be structured like this:
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
As explained in the docs.
Yours is like this:
$http.get('https://www.dropbox.com/s/325d678ksplb7qs/names.json')
sucess(function(data, status, headers, config) {
Wherea you're missing a dot (.) before the success, and your success is spelled wrong (yours is sucess).
It's decent practice to copy existing demos until you're certain on how they're really setup. Also, use your developer tools to catch easy bugs like this.
It's also possible that your dropbox call is simply invalid, but if you fix your code accordingly then the error method should be able to catch it and you should be able to see the error.

AngularJS: Call a particular function before any partial page controllers

I want to call a particular function: GetSession() at the beginning of my application load. This function makes a $http call and get a session token: GlobalSessionToken from the server. This session token is then used in other controllers logic and fetch data from the server. I have call this GetSession()in main controller: MasterController in $routeChangeStart event but as its an asynchronous call, my code moves ahead to CustomerController before the $http response.
Here is my code:
var GlobalSessionToken = ''; //will get from server later
//Define an angular module for our app
var myApp = angular.module('myApp', ['ngRoute']);
//Define Routing for app
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/customer', {
templateUrl: 'partials/customer.html',
controller: 'CustomerController',
resolve: {
loadData: function($q){
return LoadData2($q,'home');
}
}
}).
otherwise({
redirectTo: '/home'
});
}]);
//controllers start here and are defined in their each JS file
var controllers = {};
//only master controller is defined in app.js, rest are in separate js files
controllers.MasterController = function($rootScope, $http){
$rootScope.$on('$routeChangeStart', function(){
if(GlobalSessionToken == ''){
GetSession();
}
console.log('START');
$rootScope.loadingView = true;
});
$rootScope.$on('$routeChangeError', function(){
console.log('ERROR');
$rootScope.loadingView = false;
});
};
controllers.CustomerController = function ($scope) {
if(GlobalSessionToken != ''){
//do something
}
}
//adding the controllers to myApp angularjs app
myApp.controller(controllers);
//controllers end here
function GetSession(){
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
}).error(function (data, status, headers, config) {
console.log(data);
});
}
And my HTML has following sections:
<body ng-app="myApp" ng-controller="MasterController">
<!--Placeholder for views-->
<div ng-view="">
</div>
</body>
How can I make sure this GetSession() is always called at the very beginning of my application start and before any other controller calls and also called only once.
EDIT: This is how I added run method as per Maxim's answer. Still need to figure out a way to wait till $http call returns before going ahead with controllers.
//Some initializing code before Angular invokes controllers
myApp.run(['$rootScope','$http', '$q', function($rootScope, $http, $q) {
return GetSession($http, $q);
}]);
function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
}
Even though some of the solutions here are perfectly valid, resolve property of the routes definition is the way to go, in my opinion. Writing your app logic inside session.then in every controller is a bit too much , we're used such approach too in one of the projects and I didn't work so well.
The most effective way is to delay controller's instantiation with resolve, as it's a built-in solution. The only problem is that you have to add resolve property with similar code for every route definition, which leads to code duplication.
To solve this problem, you can modify your route definition objects in a helper function like this:
function withSession(routeConfig) {
routeConfig.resolve = routeConfig.resolve || {};
routeConfig.resolve.session = ['getSessionPromise', function(getSessionPromise) {
return getSessionPromise();
}]
return routeConfig;
}
And then, where define your routes like this:
$routeProvider.when('/example', withSession({
templateUrl: 'views/example.html',
controller: 'ExampleCtrl'
}));
This is one of the many solutions I've tried and liked the most since it's clean and DRY.
You can't postpone the initialisation of controllers.
You may put your controller code inside a Session promise callback:
myApp.factory( 'session', function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
} );
myApp.controller( 'ctrl', function($scope,session) {
session.then( function() {
//$scope.whatever ...
} );
} );
Alternative: If you don't want to use such callbacks, you could have your session request synchronous, but that would be a terrible thing to do.
You have not provided any details related to GetSession. For scenarios like this you should use the resolve property while defining your routes in $routeProvider. I see you are using resolve already.
What you can do now is to wrap the GlobalSessionToken into a Angular service like GlobalSessionTokenServiceand call it in the resolve to get the token before the route loads. Like
resolve: {
loadData: function($q){
return LoadData2($q,'home');
},
GlobalSessionToken: function(GlobalSessionTokenService) {
return GlobalSessionTokenService.getToken() //This should return promise
}
}
This can then be injected in your controller with
controllers.MasterController = function($rootScope, $http,GlobalSessionToken){

Sending JSON using $http cause angular to send text/plain content type

I just want to send the following JSONobjects to my API backend:
{
"username":"alex",
"password":"password"
}
So I wrote the following function, using Angular $http:
$http(
{
method: 'POST',
url: '/api/user/auth/',
data: '{"username":"alex", "password":"alex"}',
})
.success(function(data, status, headers, config) {
// Do Stuff
})
.error(function(data, status, headers, config) {
// Do Stuff
});
I read in documentation for POST method that Content-Type header will be automatically set to "application/json".
But I realized that the content-type I receive on my backend (Django+Tastypie) api is "text/plain".
This cause my API to not respond properly to this request. How should I manage this content-type?
The solution I've moved forward with is to always initialize models on the $scope to an empty block {} on each controller. This guarantees that if no data is bound to that model then you will still have an empty block to pass to your $http.put or $http.post method.
myapp.controller("AccountController", function($scope) {
$scope.user = {}; // Guarantee $scope.user will be defined if nothing is bound to it
$scope.saveAccount = function() {
users.current.put($scope.user, function(response) {
$scope.success.push("Update successful!");
}, function(response) {
$scope.errors.push("An error occurred when saving!");
});
};
}
myapp.factory("users", function($http) {
return {
current: {
put: function(data, success, error) {
return $http.put("/users/current", data).then(function(response) {
success(response);
}, function(response) {
error(response);
});
}
}
};
});
Another alternative is to use the binary || operator on data when calling $http.put or $http.post to make sure a defined argument is supplied:
$http.put("/users/current", data || {}).then(/* ... */);
Try this;
$http.defaults.headers.post["Content-Type"] = "application/json";
$http.post('/api/user/auth/', data).success(function(data, status, headers, config) {
// Do Stuff
})
.error(function(data, status, headers, config) {
// Do Stuff
});

Categories

Resources