By running the code below, the authentication window pops-up and the user confirms the login. This part works. Once the authorize button is clicked, this redirects to the previous tab in the same window (in the pop-up not in the parent window). How I can close this pop-up window after authorization is confirmed by the user and how can I get back the authorization code from the url? For instance in the code below, the first "console.log(event.url);" is not executed.
var redirectUri = "http://localhost:8100/callback";
var ref = window.open('https://www.example.com/oauth/authorize?client_id=' + clientID + '&redirect_uri=' + redirectUri + '&scope=write&response_type=code&approval_prompt=force', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
ref.addEventListener('loadstart', function (event) { // THIS IS NOT TRIGGERED FOR SOME REASON
console.log(event.url);
if ((event.url).indexOf(redirectUri) === 0) {
requestToken = (event.url).split("code=")[1];
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
$http({
method: "post",
url: "https://www.example.com/oauth/token",
data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&code=" + requestToken
})
.success(function (data) {
$scope.data = data;
console.log(event.url);
})
.error(function (data, status) {
deferred.reject("Problem authenticating");
});
}
});
Below are the tabs used in the application. How can I return to my tab.example after callback?
// setup an abstract state for the tabs directive
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
})
.state('tab.example', {
url: '/example',
views: {
'tab-example': {
templateUrl: 'templates/tab-example.html',
controller: 'ExampleAPICtrl'
}
}
})
.state('callback', {
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/');
You need a service to wrap the 3rd party Provider so it can listen for the event Call back.
a great library implementation I've used:
mrzmyr Angular-Google-Plus
The heart of the librarie's approach is in the following code snippet:
NgGooglePlus.prototype.login = function () {
deferred = $q.defer();
gapi.auth.authorize({
client_id: options.clientId,
scope: options.scopes,
immediate: false,
approval_prompt: "force",
max_auth_age: 0
}, this.handleAuthResult);
return deferred.promise;
};
When you want to do login with 3rd party, i advice you to create a login service which will perform the login by sending the good information to the login system (other API, web application with url...).
Like that, your service can emit event that can be used in your application to perform more action
$scope.$broadcast('loadstart', {
someProp: 'send parameter to your event in the data object'
});
$scope.$on('loadstart', function (event, data) {
//your function the tretment of the reply
});
If you want to go forward with the event you can follow this link : https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
to return in your tab.example you can try the $state in the .success part of your http request. This service allow you to choose on which state you want to be :
$state.go('tab.example');
Related
so I am creating Google Chrome Extension and at first I was injecting my html, angular and javascripts via content script. No I need to inject it by my own. I made that! But the problem is that when it was injected via content script my login method worked just fine in return I got token (that's what I needed), but when injected by myself my login function does not work anymore and it throws this error:
XMLHttpRequest cannot load http://www.cheapwatcher.com/api/Authenticate. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://anywebsitename.com' is therefore not allowed access. The response had HTTP status code 400.
This is my login method (I haven't changed anything since changed the injection type):
angular.module('app').controller('LoginController', LoginController);
LoginController.$inject = ['$scope', '$http', '$location', '$state'];
function LoginController($scope, $http, $location, $state) {
$scope.login = function (user) {
user.grant_type = 'password';
return $http({
method: 'POST',
url: 'http://www.cheapwatcher.com/api/Authenticate',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Access-Control-Allow-Origin': '*'
},
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: user
}).success(function (result) {
console.log(result);
$(".cheap-watcher").fadeOut(1500, function () {
$(".cheap-watcher").fadeIn($state.go("logout"), {}, { location: false }).delay(2000);
})
}).error(function (result) {
console.log(result);
});
};
};
Can I do something without making CORS on server side? Because as I said injecting via content script works just fine
UPDATE!
So I changed my login method from $http to $.ajax. Everything is the same just instead of $http I wrote $.ajax and removed headers section. In chrome source control the error is the same, but now in fiddler I can see that my request was successful. How is that possible?
now login method looks like this:
angular.module('app').controller('LoginController', LoginController);
LoginController.$inject = ['$scope', '$http', '$location', '$state'];
function LoginController($scope, $http, $location, $state) {
$scope.login = function (user) {
user.grant_type = 'password';
return $.ajax({
url: "http://www.cheapwatcher.com/api/Authenticate",
crossDomain: true,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
method: 'POST',
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: user
}).success(function (result) {
console.log(result);
$(".cheap-watcher").fadeOut(1500, function () {
$(".cheap-watcher").fadeIn($state.go("logout"), {}, { location: false }).delay(2000);
})
}).error(function (result) {
console.log(result);
});
};
};
UPDATE NR. 2!
I saw that error is coming from http://anywebsitename.com 's index page. So I assume that my login request is running not from my extension but from website content. Are there any communication possible from injected script to background script?
Take a look at Requesting cross-origin permissions, you can add http://www.cheapwatcher.com/api/Authenticate to permissions sections.
By adding hosts or host match patterns (or both) to the permissions section of the manifest file, the extension can request access to remote servers outside of its origin.
To use CORS within Angular, we need to tell Angular that we’re using CORS. We use the
.config() method on our Angular app module to set two options.
We need to tell Angular to use the XDomain and
We must remove the X-Requested-With header from all of our requests
angular.module('myApp')
.config(function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers
.common['X-Requested-With'];
});
I'm using AngularJS 1.3 and UI-Router. I have an state in which i have a resolve and a templateProvider.
What i'm trying to accomplish is that the information retrieved from database in the resolve can be used by the templateProvider. Right now, I have to get the information twice, once from resolve and another from templateProvider, and that's annoying.
The code:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', function ($templateFactory, $stateParams, $http) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
}).then(function(article) {
if ( article.data.template )
return $templateFactory.fromUrl('articles/views/templates/' + article.data.template + '.html');
else
return $templateFactory.fromUrl('articles/views/templates/news.html');
});
}],
controller: 'ArticlesViewController'
})
As you can see, according to article's kind i load a different template in the templateProvider. Besides, i use the article's information in the controller which has been previously got in the state's resolve.
Is there any way to use in the templateProvider the information previously fetched in the resolve avoiding this way another call to database?
Right now, it is doing 2 calls to database per connection...
Thanks!
app.factory('article', function ($cacheFactory){
var articleCache = $cacheFactory('article');
return function (url) {
return articleCache.get(url) || articleCache.put(url, $http({
method: 'GET',
url: '/articles/' + url
})
);
};
});
Use it as article($stateParams.articleUrl).then(...) in both places, that will keep the things DRY. You may get better control over the cache (e.g. expiration) by replacing $cacheFactory with angular-cache.
$http own caching may be successfully used as well instead of explicit caching:
If there are multiple GET requests for the same URL that should be
cached using the same cache, but the cache is not populated yet, only
one request to the server will be made and the remaining requests will
be fulfilled using the response from the first request.
I think you can inject directly the resolved variables, so you could inject article in templateProvider:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', 'article', function ($templateFactory, $stateParams, $http, article) {
// Now here you can use article without the need to re-call it
}],
controller: 'ArticlesViewController'
})
I'm fairly new to Angular.js. I try to keep my code organized, in particular, my controllers thin. My current setup is as follows:
User events get picked up by my controller, e.g. a button click will invoke $scope.upgrade() in controller.
$scope.upgrade = function() {
$scope.submitting = true // disable upgrade button
payment.chargeCreditCard($scope);
};
Then the payment service takes over and does the actual work which involves making AJAX call to backend. In my payment service module, the code is something like:
payment.chargeCreditCard = function($scope) {
$http({
method: 'post',
url: endpoint,
data: data
}).success(function() {
// do things...
}).error(function() {
$scope.error = "Something wrong happened."; // $scope was passed in from controller
$scope.submitting = false; // Re-enable the button
});
};
My question is, it feels wrong to pass $scope from controller to service. Services should be independent and should not care about things like setting the value of $scope.error and $scope.submitting.
What is the Angular way of doing this?
== UPDATE ==
So one solution I came up with is make the AJAX call in the service but return the result to controller. Something like:
// in controller
$scope.upgrade = function() {
// call service...
var response = payment.chargeCreditCard($scope);
response
.success(function() {
// ...
})
.error(function() {
// set $scope.error etc...
});
};
But then the controller is still pretty fat and the only thing that's in service is just the AJAX call itself which makes it seem not very worthwhile to separate the code.
Return the result of the $http call in the service and use the success handler in your controller.
Controller
$scope.upgrade = function() {
$scope.submitting = true // disable upgrade button
payment.chargeCreditCard().success(function() {
// do stuff
}).error(function() {
// Do other stuff
});
};
Service
payment.chargeCreditCard = function() {
return $http({
method: 'post',
url: endpoint,
data: data
});
};
Another way of doing it if this is not what you are after is using events. So in your service have the $rootScope has dependencies and use the $broadcast method.
Controller
$scope.upgrade = function() {
$scope.submitting = true // disable upgrade button
payment.chargeCreditCard();
$scope.$on("chargeCreditCard:success", function(ev, data) {
// Do stuff
});
$scope.$on("chargeCreditCard:error", function(ev, err) {
// Do other stuff
});
};
Service
payment.chargeCreditCard = function() {
return $http({
method: 'post',
url: endpoint,
data: data
}).success(function(data) {
$rootScope.$broadcast("chargeCreditCard:success", data);
}).error(function(err) {
$rootScope.$broadcast("chargeCreditCard:error", err);
});
};
But I guess the controller is as "fat", also not sure what is wrong with the controller containing the code that affect the property of its scope. By passing the scope you are making the service dependent on the controller or at least a set of properties on a scope/object which kind of go against the separation of concern.
The role of the service in this case would be to get the data and return it, one way or the other, but the service role should not be to modify controller properties.
I have a $http service in angular js which has a cache enable for it. When first time load the app service gets call and get cache. Now when i call the service anywhere from the same page the data comes from cache but when i change the page route and again call the service from another page the data come from server(i am just changing the route not refreshing the page)
Edit =>
Code was working !! On route the data also came from cache but it took more time as there are few other call as. It just took more time then i accepted the cache to respond .If i call same from any click event then it will take 2ms to 3ms
here is my service
commonServicesModule.service('getDetails', ['$http', 'urlc',
function($http, urlc) {
return {
consumer: {
profile: function(id) {
return $http({
url: urlc.details.consumer.profile,
cache: true,
params: {
'consumer_id': id,
'hello': id,
},
method: 'GET',
}).success(function(result) {
return result;
});
},
}
}
}
])
Call from controller :
start = new Date().getTime();
/*get user information */
getDetails.consumer.profile('1').then(function(results) {
console.log('time taken for request form listCtrl ' + (new Date().getTime() - start) + 'ms');
});
when i call this from anywhere else after route it take the same time.
Try moving the consumer object into the body of the function, and return a reference to it, like so:
commonServicesModule.service('getDetails', ['$http', 'urlc', function($http, urlc) {
var getConsumer = {
profile: function(id) {
return $http({
url: urlc.details.consumer.profile,
cache: true,
params: {
'consumer_id': id,
'hello': id,
},
method: 'GET',
}).success(function(result) {
return result;
});
}
};
return { consumer: getConsumer };
}]);
I'm using angularjs 1.2.0 with $resource. I would like to have some PUT/POST instance actions that doesn't send the whole object to the server but only some fields and in some cases totally no data.
Is it possible? I searched everywhere but couldn't find anything
UPDATE:
It also happens with DELETE requests:
Given this code:
group.$deleteChatMessage({messageId: message.id}, function(){
var i = _.indexOf(group.chat, message);
if(i !== -1) group.chat.splice(i, 1);
});
The request is this:
See how the whole model is sent (under "Request Payload").
This is the resource:
var Group = $resource(API_URL + '/api/v1/groups/:gid',
{gid:'#_id', messageId: '#_messageId'},
{
deleteChatMessage: {method: "DELETE", url: API_URL + '/api/v1/groups/:gid/chat/:messageId'},
});
This works for me:
$resource(SERVER_URL + 'profile.json',
{},
{
changePassword :
{
method : 'POST',
url : SERVER_URL + 'profile/changePassword.json',
// Don't sent request body
transformRequest : function(data, headersGetter)
{
return '';
}
}
});
You could customise exaclty what is sent to the server by implementing your own code in the transformRequest function. In my example I was adding a new function to the REST client, but you can also overwrite existing functions. Note that 'transformRequest' is only available in version 1.1+
You can use $http for that specifically. However, I have one case I use for a project that might help. Also my example is returning an array from the server but you can change that.
In my service:
app.factory('mySearch', ['$resource', function($resource) {
return $resource('/api/items/:action', {}, {
search: { method: 'POST', isArray: true,
params: { action: 'search' }
}
});
}
]);
In my Controller:
I can build up custom params to post to server or if its only two fields I need from a table row the user selects.
var one = "field_one";
var two = "field_two";
$scope.search({one: one, two: two});
Then I can post those through an event and pass the custom params
$scope.search = function(customParams) {
mySearch.search({query: customParams}, function(data) {
$scope.items = data;
}, function(response) {
console.log("Error: " + response.status);
})
};
Hopefully this was some help. Let me know if this is close to what your looking for and I can help more.
POST
DELETE