Angular JS Newbie: I get the gapi is not defined because sydney.userAuthed is called before the API is loaded. But I don't understand why the method sydney.auth is called when the controller is created. I get the error when the page loads, there is no user interaction.
calling auth api.js:22
ReferenceError: gapi is not defined
at Object.sydney.userAuthed (http://localhost:8888/js/api.js:36:8)
at Object.sydney.auth (http://localhost:8888/js/api.js:23:30)
at new LoginController (http://localhost:8888/js/controllers.js:8:24)
at invoke (http://localhost:8888/lib/angular/angular.js:2902:28)
at Object.instantiate (http://localhost:8888/lib/angular/angular.js:2914:23)
at http://localhost:8888/lib/angular/angular.js:4805:24
at updateView (http://localhost:8888/lib/angular/angular-ui-router.js:931:30)
at <error: illegal access>
at Object.Scope.$broadcast (http://localhost:8888/lib/angular/angular.js:8307:28)
at $state.transition.resolved.then.$state.transition (http://localhost:8888/lib/angular/angular-ui-router.js:747:20) angular.js:5754
OAuth2 API loaded api.js:13
I defined a state with ui-router as:
myapp.config(function($stateProvider, $routeProvider) {
$stateProvider.state('signin', {
url : "/", // root route
views : {
"signinView" : {
templateUrl : 'partials/signin.html',
controller: 'LoginController'
}
},
})
The controller is defined as:
function LoginController($scope, $state) {
$scope.auth = sydney.auth($state);
}
sydney.auth method is used to authenticate a user using Google APIs Client Library for JavaScript.
var sydney = sydney || {};
sydney.auth = function($state) {
console.log("calling auth");
sydney.signin(false, sydney.userAuthed($state));
}
sydney.signin = function(mode, callback) {
gapi.auth.authorize({client_id: sydney.CLIENT_ID,
scope: sydney.SCOPES, immediate: mode,
response_type: 'token id_token'},
callback);
console.log("signin called");
}
sydney.userAuthed = function($state) {
var request =
gapi.client.oauth2.userinfo.get().execute(function(resp) {
if (!resp.code) {
var token = gapi.auth.getToken();
token.access_token = token.id_token;
gapi.auth.setToken(token);
// User is signed in, call my Endpoint
gapi.client.realestate.owner.create().execute(function(resp) {
alert("Signed In");
$state.transitionTo("signedin");
});
}
});
}
EDIT
The correct answer is to define $scope.auth as a function:
function LoginController($scope, $state) {
$scope.auth = function() {
sydney.auth($state);
}
}
It seems what you're trying to do is make the sydney.auth function available from your scope. If so, you should change your LoginController to:
function LoginController($scope, $state) {
$scope.auth = sydney.auth;
}
What your code was doing was calling the sydney.auth function and setting $scope.auth to its return value, which is not what I think you intended.
Related
(My plunkr code resides at http://plnkr.co/edit/6KU3GblQtMdRAx3v3USV?p=preview)
I'm trying to create a Search bar (in navigation) which should ultimately hit the backend REST API. The input search button when clicked on input 'alpha' would trigger a route to products/search/0?searchtext=alpha
Clicking on the button triggers a route change, which should do resolve as follows:
.when("/products/search/:page", {
templateUrl: "products.html",
controller: "ProductsSearchController",
resolve: {
// Define all the dependencies here
ProdSearchServ : "ProdSearchService",
// Now define the resolve function
resultData : function(ProdSearchServ) {
return ProdSearchServ.searchProducts();
}
}
})
However, I'm getting the following error
angular.js:9784 Error: [$injector:unpr] Unknown provider: ProdSearchServProvider <- ProdSearchServ
I believe I'm doing most of the things as per conventions, may be I'm missing something here?
I'm copying script.js code below (also in plnkr link above). It has all the route configuration and the controllers defined.
(function(){
// jargoViewer Create a new Angular Module
// This would go into the html tag for index.html
var app = angular.module("jargoViewer", ["ngRoute"]);
app.config(function($routeProvider){
$routeProvider
.when("/main", {
templateUrl: "main.html",
controller: "NavController"
})
.when("/products/search/:page", {
templateUrl: "products.html",
controller: "ProductsSearchController",
resolve: {
// Define all the dependencies here
ProdSearchServ : "ProdSearchService",
// Now define the resolve function
resultData : function(ProdSearchServ) {
return ProdSearchServ.searchProducts();
}
}
})
.otherwise({redirectTo:"/main"});
});
}());
// Nav Controller
(function() {
var app = angular.module("jargoViewer");
var NavController = function($scope, $location) {
// Function to invoke the Prod search based on input
$scope.search = function() {
console.log("searchText : " + $scope.searchtext);
$location.path("products/search/0").search({searchtext: $scope.searchtext});
};
};
app.controller("NavController", NavController);
}());
// Define the Prod Search Service here
(function() {
// Get reference to the app
var app = angular.module("jargoViewer");
// Create the factory
app.factory('ProdSearchService', function($routeParams, $http, $q) {
var searchProducts = function() {
pageNum = 0;
searchParam = '';
if (('page' in $routeParams) && (typeof $routeParams.page === 'number')) {
pageNum = $routeParams.page;
}
// Check if the router Param contains the field searchtext, if so, check if its a string
if (('searchtext' in $routeParams) && (typeof $routeParams.searchtext === 'string')) {
searchParam = $scope.routeParam.searchtext;
}
// Now make the http API hit to fetch the products
var request = $http({
method: "get",
url: "http://abcd.com/products/search/" + pageNum,
params: {
search: searchParam
},
});
return(request.then(handleSuccess, handleError));
};
function handleError(response) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (
! angular.isObject(response.data) ||
! response.data.message
) {
return($q.reject( "An unknown error occurred."));
}
// Otherwise, use expected error message.
return($q.reject(response.data.message));
}
// I transform the successful response, unwrapping the application data
// from the API response payload.
function handleSuccess(response) {
if(response.data.error == true) {
return($q.reject(response.data.message));
}
return(response.data.data);
}
return {
searchProducts : searchProducts
};
});
}());
// Define the Products Search Controller below
(function() {
var app = angular.module("jargoViewer");
//var ProductController = function($scope) {
var ProductsSearchController = function($scope, $routeParams, ProdSearchService) {
// Nothing to do really here
};
app.controller("ProductsSearchController", ProductsSearchController);
}());
This caused by your bizarre naming conventions. Sometimes ProdSearchServ and sometimes ProdSearchService.
If you just pick one and use it consistantly then you won't run into these types of errors.
Fixed Plunker
In particular you create the service with the name ProdSearchService and then attempt to use it with a different name:
app.factory('ProdSearchService',
//vs
resultData : function(ProdSearchServ) {
I imagine you we under the impression that this code would fix it for you. However, this only applies to dependencies passed into the controller, not functions in general. For services which already exist, you do not need to define them specially like this; instead simply use the correct name in the controller.
// Define all the dependencies here
ProdSearchServ : "ProdSearchService",
I think you don't need to define the dependency when you say
// Define all the dependencies here
ProdSearchServ : "ProdSearchService",
Just do this:
.when("/products/search/:page", {
templateUrl: "products.html",
controller: "ProductsSearchController",
resolve: {
resultData : function(ProdSearchService) { //as you defined it before
return ProdSearchService.searchProducts();
}
}
})
There is a similar question here
As of this post, I'm trying to figure out if the user is logged in (using a token based authentication).
The scheme is following :
1/ The page loads, app run is called, and authenticated is set to false as default
app.run(function($http, UserService) {
UserService.requestCurrentUser();
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
});
app.constant('AUTHENTICATED', false);
2/ UserService call for its method requestCurrentUser() in which a http get is sent to the correct url with the token in its header.
If token is correct, this sends back the user (success case, we're authenticated).
If not, I get a permission error (error case, we're not authenticated).
This updates currentUserproperty and AUTHENTICATED constant.
app.factory('UserService', function ($http, $q, $window, AUTHENTICATED) {
var _currentUser = {};
return {
getCurrentUser: function() {
return _currentUser;
},
setCurrentUser: function(user) {
_currentUser = user;
},
requestCurrentUser: function() {
return $http.get('/accounts/api/').then(
function (response) {
_currentUser = response.data;
AUTHENTICATED = true;
},
function (error) {
AUTHENTICATED = false;
}
);
},
};
});
3/ Controller is called and authenticated and currentUser scope values are updated.
app.controller('AuthCtrl', function ($scope, AuthService, UserService, AUTHENTICATED) {
$scope.authenticated = AUTHENTICATED;
$scope.currentUser = UserService.getCurrentUser();
});
Problem is that controller tries to reach the values before requestCurrentUser method (launched in app run) has received a response. So where should I launch requestCurrentUser to get the expected behavior ?
Thanks
What you could do it wrap your user state object in a parent object. For example:
var state = {
_currentUser: {}
};
return {
getUserState: function(){ return state; }
};
Then inside your controller:
$scope.state = UserService.getUserState();
This way, when your user updates (no matter when or how in your service), anything bound to the state will receive the update. So your controller will have access to state._currentUser when it is available.
I'm working on an Angular web app that is using Firebase to authenticate the user when they login or register. I want to restrict going to the /dashboard url unless they're logged in. I tried following Firebase's docs, but I'm coming up with errors.
I think where I'm having problems was making my controller code work with the one provided. I kept getting the error "Unknown provider: AuthProvider <- Auth <- currentAuth", so I just took out their controller code for now.
Any help would be great!
Here's the doc link: https://www.firebase.com/docs/web/libraries/angular/guide/user-auth.html#section-routers
And my code:
ROUTER CONFIG
var app = angular.module('maggsLashes', ['ngRoute', 'ui.calendar', 'firebase']);
app.config(function($routeProvider) {
$routeProvider
.when('/dashboard', {
templateUrl: 'app/templates/dashboardTmpl.html',
controller: 'dashboardCtrl',
resolve: {
// controller will not be loaded until $requireAuth resolves
// Auth refers to our $firebaseAuth wrapper in the example above
"currentAuth": ["Auth", function(Auth) {
// $requireAuth returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.$requireAuth();
}]
}
})
DASHBOARD CONTROLLER
app.controller('dashboardCtrl', function($scope, $firebaseAuth, $location) {
var ref = new Firebase("https://maggslashes.firebaseio.com/");
var auth = $firebaseAuth(ref);
// app.controller("AccountCtrl", ["currentAuth", function(currentAuth) {
// // currentAuth (provided by resolve) will contain the
// // authenticated user or null if not logged in
// }]);
// console.log(auth);
console.log("Matt is pretty much awesome");
ref.onAuth(function(authData) {
if (authData) {
console.log("User is authenticated w uid:", authData);
}
else {
console.log("client sucks");
}
});
$scope.logOut = function() {
$location.path('/');
$scope.apply();
ref.unauth();
console.log(authData.uid);
};
});
I can see what may be at least on issue. In your router, you refer to "Auth" but do not see your code for that service/factory, so it's possible you don't have that set up. Note that "Auth" is a custom factory.
not you need this somewhere in your code:
app.factory("Auth", ["$firebaseAuth",
function($firebaseAuth) {
var ref = new Firebase("https://docs-sandbox.firebaseio.com", "example3");
return $firebaseAuth(ref);
}
])
Also, can you provide the errors you're receiving?
Wayne
The problem I'm having is that after a user logs-in or signs-up they are redirected to the games view, this happens within the createUser function or after a successful authentication; in either case the redirect is handled with $state.go('games').
This all works fine, however, if the user navigates away from the games view to any other state like createGame or dashBoard and then refreshes the browser in one of those views they are always redirected back to the games view. When I remove the $state.go('games') this doesn't happen, and reload will only reload the current view (like it should).
I have tried changing parameters on $state.go and tried using $state.transitionTo(), but nothing changes this behavior.
Is this just normal behavior for $state.go? If not, am I using it wrong, and are there other ways to redirect? What can I do to stop this behavior?
var game = angular.module('game', ['ui.router','firebase']);
game.config(['$stateProvider', '$locationProvider', function($stateProvider,$locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider.state('signUp', {
url: '/signUp',
templateUrl: '/templates/signUp.html'
});
$stateProvider.state('games', {
url: '/games',
templateUrl: '/templates/games.html',
controller: 'games.controller'
});
$stateProvider.state('login', {
url: '/login',
templateUrl: '/templates/login.html'
});
$stateProvider.state('dashboard', {
url: '/dashboard',
templateUrl: '/templates/dashboard.html',
controller: 'dashboard.controller'
});
$stateProvider.state('createGame', {
url: '/createGame',
templateUrl: '/templates/createGame.html',
controller: 'createGame.controller'
});
}]);
// Root reference to database
game.factory("Fire", function($firebaseAuth) {
var ref = new Firebase("https://money-game.firebaseIO.com/");
return ref;
});
// Gives access to auth methods
game.factory("Auth", ["$firebaseAuth", "Fire",
function($firebaseAuth, fire) {
return $firebaseAuth(fire);
}
]);
game.controller('app.controller', ['$scope', '$state', '$stateParams', 'Auth', 'Fire', function ($scope, $state, $stateParams, auth, fire) {
$scope.user = {
email : '',
password : ''
};
$scope.signUp = function() {
auth.$createUser($scope.user)
.then(function(userData) {
// After successful signup save a user record to users under their auth ID
fire.child('users').child(userData.uid).set({
name : $scope.user.name,
email : $scope.user.email,
joined : Date.now()
});
$state.go('games');
console.log("User " + userData.uid + " created successfully!");
})
.catch(function(error) {
console.error("Error: ", error);
});
};
$scope.login = function() {
auth.$authWithPassword($scope.user).catch(function(error) {
console.error("Authentication failed:", error);
});
};
$scope.logout = function() {
auth.$unauth();
window.location = '/';
};
auth.$onAuth(function(authData) {
if (authData) {
$scope.activeUser = authData;
// After user logs in find user record by auth id
fire.child('users').child(authData.uid).on('value', function(snapshot) {
// Checks if user exsists
if (snapshot.exists()) {
// sets scope user to the user data
$scope.user = snapshot.val();
// sets scope user id to the auth id
$scope.user.id = snapshot.key();
}
});
console.log("Logged in as:", authData.uid);
$state.go('games');
} else {
$scope.activeUser = false;
// $scope.user = '';
}
});
}]);
game.controller('games.controller', ['$scope', '$state', '$stateParams', 'Auth', '$firebaseArray','Fire', function ($scope, $state, $stateParams, auth, $firebaseArray, fire) {
$scope.games = $firebaseArray(fire.child('games'));
$scope.view = 'listView';
$scope.setCurrentGame = function(game) {
$scope.currentGame = game;
};
$scope.addPlayer = function(game) {
console.log(game.$id);
var ref = fire.child('players').child(game.$id);
ref.push({
id : $scope.user.id,
name : $scope.user.name,
email : $scope.user.email
})
};
// swap DOM structure in games state
$scope.changeView = function(view){
$scope.view = view;
}
}]);
game.controller('createGame.controller', ['$scope', '$state', '$stateParams', 'Auth', '$firebaseArray','Fire', function ($scope, $state, $stateParams, auth, $firebaseArray, fire) {
$scope.games = $firebaseArray(fire.child('games'));
$scope.createGame = function() {
if ($scope.format == 'Match Play') {
$scope.skinAmount = 'DOES NOT APPLY';
$scope.birdieAmount = 'DOES NOT APPLY';
}
$scope.games.$add({
name: $scope.gameName,
host: $scope.user.name,
date: $scope.gameDate,
location: {
course: $scope.courseName,
address: $scope.courseAddress
},
rules: {
amount: $scope.gameAmount,
perSkin: $scope.skinAmount,
perBirdie: $scope.birdieAmount,
format: $scope.format,
holes : $scope.holes,
time: $scope.time
}
})
// $state.go('games');
};
}]);
It's not about $state.go
It's actually about when you call it, $state.go does one simple thing : trigger a change in the routing state off your application. You just happen to do it everytime your application authenticates your user against your $firebaseAuth service, in the $onAuth handler.
A single page app is an app
When you refresh the page in the browser, the entire app reloads, starts again, boostraps everything to display your app. This startup process includes re-authenticating your user (I didn't quite see where it is done in your code, but it has to be done), thus triggering the $onAuth handler again… and ultimately execute $state.go('games') again.
Put your $state.go invocation elsewhere
You don't actually mean to do it every time your app authenticates the user, you rather want to do it when your user performs a successful login or sign-up action.
$authWithPassword returns a promise, you could do the state change in a success callback when the promise is resolved.
Hope that helps !
I'm new to AngularJS and just building an app to learn it. My app calls a REST API and right now I have the hostname hard coded in the app. I want to make this in app setting (and maybe later have somewhere to configure it). I thought I'd start with a constant. Should it go in my app.js like this? If so, I'm not sure of the syntax for adding it to the .config settings with $routeProvider there too
(function () {
// Define module and add dependencies inside []
var app = angular.module("haClient", ["ngRoute"]);
app.constant('hostname', 'http://192.192.192.192:8176');
app.config(function ($routeProvider) {
$routeProvider
// Register routes
// Main route
.when("/main", {
templateUrl: "main.html",
controller: "MainController"//,
//activeTab: 'home'
})
// Device List
.when("/devices", {
templateUrl: "devicelist.html",
controller: "DeviceListController"
})
// Device details (param is device name)
.when("/device/:devicename", {
templateUrl: "device.html",
controller: "DeviceController"
})
// Invalid URL's get sent back to main route
.otherwise({ redirectTo: "/main" });
}); // End App Config
}());
This is the module that needs to use it (called from controllers):
(function () {
var deviceCtrl = function ($http) {
var getDevices = function () {
return $http.get("http://192.192.192.192:8176/devices.json/")
.then(function (response) {
return response.data;
});
};
// get details and return a promise
var getDeviceDetails = function (deviceName) {
return $http.get("http://192.192.192.192:8176/devices/" + deviceName + ".json/")
.then(function (response) {
return response.data;
});
};
// Public API
return {
getDeviceDetails: getDeviceDetails,
getDevices: getDevices
};
};
var module = angular.module("haClient");
}());
Can someone enlighten em on the best way to set it and get it?
Thanks
I currently do this by using templates to build the root .html file in the backend. Eg, using doT templates in node.js, I put this below my other js includes:
<!-- load any templated constants -->
<script>
angular.module('mymod').constant('globals', {
api: '{{=it.api}}'
});
</script>
This way my backend can work out the logic of where the client needs to point to. In order to use the value in another service or controller, you simply inject the constant by name:
angular.module('somemod', []).factory('myservice', ['globals', function(globals){
// use globals.api or what ever you set here for example
}]);
The best place to do configuration is in a Provider and inside your config block.
Providers expose an API that allows you to configure your service before it's injected into your controllers and directives.
Suppose you have a service called myService that you want injected into your controller function like this:
app.controller('ctrl', function($scope, myService) { ...});
And myService is responsible for retrieving data through web API calls. Let's further assume that you would like to configure your service with the root URL htt://servername/, since all calls will share the same host name.
You can define your myServiceProvider like this:
app.provider('myService', function(){
var webapiurl;
this.setWebServiceUrl = function (url) {
webapiurl = url;
}
// the injector will call the $get function to create the singleton service that will be injected
this.$get = function( /*injectables*/) {
return {
getData: function() {... Use webapiurl ...}
}
}
});
Then in your config function, configure your provider:
app.config(function(myServiceProvider){
myServiceProvider.setWebServiceUrl('htt://servername');
});
Finally you can inject the service anywhere it can be injected:
app.controller('ctrl', function($scope, myService) {
$scope.data = myService.getData();
});
I couldn't get the supplied answers to work (no reason to think they wouldn't). here's what I did.
My main app.js (constants section)
(function () {
// Define module and add dependencies inside []
var app = angular.module("haClient", ["ngRoute"]);
//constants
app.constant('mySettings', {
baseURL: 'http://192.192.192.192:8176',
otherSetting: 'XYZ'
});
app.config(function ($routeProvider) {
$routeProvider
// Register routes
// Main route
.when("/main", {
templateUrl: "main.html",
controller: "MainController"//,
//activeTab: 'home'
})
// Device List
.when("/devices", {
templateUrl: "devicelist.html",
controller: "DeviceListController"
})
// Device details (param is device name)
.when("/device/:devicename", {
templateUrl: "device.html",
controller: "DeviceController"
})
// Invalid URL's get sent back to main route
.otherwise({ redirectTo: "/main" });
}); // End App Config
}());
In my service (added mySettings as a dependency and then just used mySettings.baseURL):
(function () {
var deviceCtrl = function ($http, $log, mySettings) {
$log.info("DeviceCtrl - baseURL: " + mySettings.baseURL);
// get device list and return a promise
var getDevices = function () {
return $http.get(mySettings.baseURL + "/devices.json")
.then(function (response) {
return response.data;
});
};
// get details and return a promise
var getDeviceDetails = function (deviceName) {
$log.info("DeviceCtrl - Getting device info for " + deviceName);
return $http.get(mySettings.baseURL + "/devices/" + deviceName + ".json")
.then(function (response) {
return response.data;
});
};
// Public API
return {
getDeviceDetails: getDeviceDetails,
getDevices: getDevices
};
};
var module = angular.module("haClient");
module.factory("deviceCtrl", deviceCtrl);
}());
I'm certainly no expert (as is clear from not being able to get supplied answers working), and I'm not sure (yet) if there are any drawbacks to this method. It allowed me to get on with my project and learn more of Angular, so I went with it.
Regards
Mark
I have used another module and injected it into app module like this
Create constant.js and include that in your index.html & add following code inside that
angular.module('constantsModule', []).constant('BASEURL', 'http://google.com');
Inside app.js, inject 'constantsModule' so that all constants inside it will be available for 'haClient'
angular.module('haClient', [
'constantsModule'
])
.config(function ($routeProvider) {
.when('/', {
templateUrl: 'views/landing.html',
controller: 'landingCtrl'
})
});
Inside landingCtrl, since its in scope of 'haClient', we can inject BASEURL constant from 'constantsModule'
angular.module('haClient').controller('landingCtrl', function ($scope, BASEURL) {
// just to show, how to access it inside controller
$scope.baseurl = BASEURL;
});