I seem to get odd behavior from gapi.client.drive.files.list where I cannot update the scope from the promise it returns.
angular.module('myApp').controller("folder_controller", function($scope, $q, $http, $rootScope) {
$scope.list_subfolders = function () {
gapi.client.drive.files.list({
'q': "mimeType = 'application/vnd.google-apps.folder'",
'fields': "nextPageToken, files(id, name, parents, mimeType, description, starred, properties)"
}).then(function(response) {
$scope.subfolders = response.result.files;
console.log($scope.subfolders);
}, function(error){
console.log(error);
});
}
});
When I run list_subfolders()... the console.log displays $scope.subfolders fine... but the view never updates with that value - it is empty. I've tried various things including just assigning to $rootScope, but there is no way I can get the view to update with the $scope.subfolders.
Am I approaching this wrong? I can't understand why the variable is not updating.
Try this:
angular.module('myApp').controller("folder_controller", function($scope, $q, $http, $rootScope) {
$scope.list_subfolders = function () {
gapi.client.drive.files.list({
'q': "mimeType = 'application/vnd.google-apps.folder'",
'fields': "nextPageToken, files(id, name, parents, mimeType, description, starred, properties)"
}).then(function(response) {
$scope.subfolders = response.result.files;
// you need to refresh the view:
$scope.$apply();
console.log($scope.subfolders);
}, function(error){
console.log(error);
});
}
});
Related
I realize there are a million of these questions on stack overflow and I've looked at several. It always seems to be a situation where someone is not returning back a promise. That is not the case this time... I am most definitely returning back a promise in my LoginFactory. The error occurs from my controller when I do a login post. I can only assume this is some strange bundling and minification error with my scripts; however, I am new to angular.
Why is angular complaining about not returning a deferred object?
var LoginFactory = function ($http, $q) {
console.log("calling login factory constructor");
return function (emailAddress, password, rememberMe) {
var deferredObject = $q.defer();
$http.post('/Account/Login', {
Email: emailAddress,
Password: password,
RememberMe: rememberMe
}).success(function (data) {
console.log("in success", data);
if (data === "True") {
deferredObject.resolve({ success: true });
} else {
deferredObject.resolve({ success: true });
}
}).error(function () {
console.log("in error");
deferredObject.resolve({ success: false });
});
console.log("returning deferred object", deferredObject);
return deferredObject.promise;
}
}
LoginFactory.$inject = ['$http', '$q'];
var LoginController = function ($scope, $routeParams) {
$scope.loginForm = {
emailAddress: '',
password: '',
rememberMe: false,
returnUrl: $routeParams.returnUrl
};
$scope.login = function () {
var result = LoginFactory($scope.loginForm.emailAddress, $scope.loginForm.password, $scope.loginForm.rememberMe);
console.log("result in login func",result);
result.then(function (result) {
if (result.success) {
if ($scope.loginForm.returnUrl !== undefined) {
$location.path('/routeOne');
} else {
$location.path($scope.loginForm.returnUrl);
}
} else {
$scope.loginForm.loginFailure = true;
}
});
}
}
LoginController.$inject = ['$scope', '$routeParams', '$location', 'LoginFactory'];
Adding minified script. The return promise looks weird. I'm not familiar enough with js to know if that's correct
LoginFactory = function(n, t) {
return console.log("calling login factory constructor"),
function(i, r, u) {
var f = t.defer();
return n.post("/Account/Login", {
Email: i,
Password: r,
RememberMe: u
}).success(function(n) {
console.log("in success", n);
n === "True" ? f.resolve({
success: !0
}) : f.resolve({
success: !0
})
}).error(function() {
console.log("in error");
f.resolve({
success: !1
})
}), console.log("returning deferred object", f), f.promise
}
};
LoginFactory.$inject = ["$http", "$q"];
This is the underside of so-called John Papa style and $inject annotations.
LoginFactory variable results in having the same name in multiple scopes (as in 'function scope', not 'Angular scope'); something that will never happen with anonymous functions and array annotations.
Function annotation and function signature are mismatched:
var LoginController = function ($scope, $routeParams) { ... }
LoginController.$inject = ['$scope', '$routeParams', '$location', 'LoginFactory'];
This results in getting LoginFactory from parent scope, where it is a factory function, not an instance. Thus the result of LoginFactory(...) call is a function, not a promise (this is what console.log outputs).
It should be
var LoginController = function ($scope, $routeParams, $location, LoginFactory) { ... }
Two things can be done to never let this happen again.
The one is to use named functions instead of variables, so $inject annotations could be hoisted. This allows to have annotation and function signature side by side:
LoginController.$inject = ['$scope', '$routeParams', '$location', 'LoginFactory'];
function LoginController ($scope, $routeParams, $location, LoginFactory) { ... }
Another one is TDD. Write unit tests at the same time when you write the app. This allows to spot rookie mistakes on sight and deduce the complex ones by eliminating potential causes. If LoginFactory were already tested at the time when LoginController was written, there would be no doubt that DI just went wrong.
Also, the code above uses deprecated success method and deferred antipattern.
You must return the deferredObject (not deferredObject.promise)
And in result do result.promise.then
The then function will be execute when promise is loaded
I have just about given up with this. But I have a $resource that uses a query() to retrieve a list of items. I then have an Interceptor that runs over those items in order to insert an $interval.
The $interval at a specific point will then get an item again using the $resource's get() function. But its this get() that is not working.
The call happens but I cannot get the response back into the template.
myServices.factory('Items', ['$resource',
function($resource) {
return $resource("/items", {}, {
'query': {
interceptor: MyInterceptor,
url: "/items",
isArray: true
},
})
}]);
myServices.factory('MyInterceptor', function($q, $interval, $injector, $rootScope) {
return {
'response': function(response) {
angular.forEach(response.resource, function (item) {
$interval(function () {
item.something = 1 + item.something;
if(item.something == 10)
{
item = $injector.get("Mine").get({slug: item.id});
}
});
});
return response;
}
};
});
I thought that this line item = $injector.get("Mine").get({slug: item.id}); would work, but it doesn't. The new item is not changed in the template.
So I changed it to something like this, which did not work either;
$injector.get("Mine").get({slug: item.id}, function(data){
item = data;
});
I have tried with $q and $promise too, but I had no luck with those either. Finding decent examples on those subjects was tough too.
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
Based on the above statement I will give an answer.
First things first, remove the interceptor. You won't need it. Instead use a service.
Write a service called ProcessedItems.
angular.module('app')
.service('ProcessedItems', ['Items', '$q', function(Items, $q){
return {
query: function() {
var defer = $q.defer();
Items.query()
.$promise
.then(function(response){
angular.forEach(response.resource, function(i)){
i.s = 1 + i.s;
if(i.s == 10) {
i = $injector.get("Mine").get({slug: i.id});
i.$promise.then(function(){
defer.resolve(response);
}, function(){
defer.reject();
});
};
};
});
return defer.promise;
}
};
}]);
After this service is set up, in your controller you can do
angular.module('app')
.controller('AppController', ['$scope', 'ProcessedItems',
function($scope, ProcessedItems){
$scope.items = [];
ProcessedItems.query().then(function(pitems){
$scope.items = pitems;
});
});
What this will essentially do is first process the data completely and then display it in the view.
thanks in advance for read my post.
I have a service, like this:
angular.module('myApp.services')
.factory('myService', function($http, $q) {
var myService = {
getId: function() {
var defer = $q.defer();
$http.get('http://xxxxxxxxxxxxxxxxxx/id/')
.success( function(data) {
defer.resolve(data);
})
.error( function(data) {
defer.reject(data);
});
return defer.promise;
},
begin : function(jsonBegin) {
var defer = $q.defer();
$http.post('http://xxxxxxxxxxxxxxxxxxxxxxx/begin/?format=json', jsonBegin)
.success( function(data) {
defer.resolve(data);
})
.error( function(data) {
defer.reject(data);
});
return defer.promise;
}
};
return myService;
});
A parent controller (works fine):
angular.module('myApp.controllers')
.controller('controllerA', function($scope, myService) {
myService.getId()
.then(function(data) {
$scope.bID = data.bID;
});
});
And child controller:
angular.module('myApp.controllers')
.controller('controllerB', function($scope) {
console.log($scope.$parent.bID);
});
The console.log value for bID is undefined, do u know why? I am setting that variable in the parent controller trough myService service. I guess my problem is due to the asynchronous call but I'm not sure.
Thanks.
You could do something like this:
angular.module('myApp.controllers')
.controller('controllerA', function($scope, myService) {
$scope.bID = myService.getId()
.then(function(data) {
return data.bID;
});
});
angular.module('myApp.controllers')
.controller('controllerB', function($scope) {
$scope.$parent.bID.then(function(bID) { console.log(bID); });
});
The child scope will wait for the parent to resolve bID. And when parent bID is already resolved, it will immediately return the value in the child scope promise. It's a little messy looking though.
The alternative is to have the myService request resolve before the controller loads. Check this out:
http://www.codelord.net/2015/06/02/angularjs-pitfalls-using-ui-routers-resolve/
Then you can set the parent scope to the resolved value and access it as a normal property in the child scope.
The problem is that the console.log code is excuted when the controller is initialized, in that time the response do't arrive with the results on the parent controller.
Try to put this code on your test controller.
angular.module('myApp.controllers')
.controller('controllerB', function($scope, $timeout) {
// waith 1second for the server response on the parent controller
$timeout(function(){
console.log($scope.$parent.bID);
}, 1000);
});
FYI. This work console.log($scope.bID) check scope inheritance.
I got an issue with Angular JS popup. I am submitting the data from the popup and I want to pass the data to a taskService so that it can call a WebAPI and store on to my DB.
This is my Call from BoardCtrl to open the Modal window
$scope.showAddTask = function () {
modalService.showModal({
templateUrl: "Partials/AddTask.html",
controller: "taskCtrl",
inputs: {
title: "Add Task"
}
}).then(function (modal) {
//debugger;
modal.element.modal();
modal.close.then(function (result) {
});
});
};
Now the user keys in the Task details and Submits. The call is in my taskCtrl
The debugger does hit the code below and I can see the values submitted by the end user. The problem I am facing is that I am getting an error
at taskService.addTask invocation
The error is "Cannot read property 'addTask' of undefined"
fpdApp.kanbanBoardApp.controller('taskCtrl', function ($scope, taskService) {
$scope.close = function () {
debugger;
taskService.addTask($scope.Name, $scope.Desc, $scope.Estimate, 1).then(function (response) {
$scope.result = response.data;
}, onError);
close({
name: $scope.name,
Desc: $scope.Desc,
Estimate: $scope.Estimate,
}, 500); // close, but give 500ms for bootstrap to animate
};
});
Here is my taskService
fpdApp.kanbanBoardApp.service('taskService', function ($http, $q, $rootScope) {
var addTask = function (name, desc, estimate, projectId) {
debugger;
//return $http.get("/api/TaskWebApi/AddTaskForProject").then(function (response) {
// return response.data;
//}, function (error) {
// return $q.reject(error.Message);
//});
};
});
Can some one please help/ guide me whats wrong here.
Note that I have got other method calls working fine in the same service and controller.
Thanks in Advance
Venkat.
You need to expose addTask method in service. Right now it's just a local variable which cannot be accessed from outside. When the service is constructed it should create proper object with necessary methods. So you should set addTask either with this.addTask = addTask or by returning object with such method:
fpdApp.kanbanBoardApp.service('taskService', function ($http, $q, $rootScope) {
var addTask = function (name, desc, estimate, projectId) {
return $http.get("/api/TaskWebApi/AddTaskForProject").then(function (response) {
return response.data;
}, function (error) {
return $q.reject(error.Message);
});
};
return {
addTask: addTask
};
});
Service always returns a singleton object and that can be used by application wide.
You forget to write method inside service context,
change var addTask to this.addTask
Code
fpdApp.kanbanBoardApp.service('taskService', function($http, $q, $rootScope) {
this.addTask = function(name, desc, estimate, projectId) {
return $http.get("/api/TaskWebApi/AddTaskForProject").then(function(response) {
return response.data;
}, function(error) {
return $q.reject(error.Message);
});
};
});
Hope this could help you. Thanks.
I want to display a form with data corresponding to the edited item. I use ui-router for routing. I defined a state:
myapp.config(function($stateProvider) {
$stateProvider.
.state('layout.propertyedit', {
url: "/properties/:propertyId",
views : {
"contentView#": {
templateUrl : 'partials/content2.html',
controller: 'PropertyController'
}
}
});
In PropertyController, I want to set $scope.property with data coming from the following call (Google Cloud Endpoints):
gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
I don't know if I can use resolve because the data are returned asynchronously. I tried
resolve: {
propertyData: function() {
return gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
}
}
First issue, the propertyId is undefined. How do you get the propertyId from the url: "/properties/:propertyId"?
Basically I want to set $scope.property in PropertyController to the resp object returned by the async call.
EDIT:
myapp.controller('PropertyController', function($scope, , $stateParams, $q) {
$scope.property = {};
$scope.create = function(property) {
}
$scope.update = function(property) {
}
function loadData() {
var deferred = $q.defer();
gapi.client.realestate.get({'id': '11'}).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property = deferred.promise;
}
});
You need to read the docs for resolve. Resolve functions are injectable, and you can use $stateParams to get the correct value from your routes, like so:
resolve: {
propertyData: function($stateParams, $q) {
// The gapi.client.realestate object should really be wrapped in an
// injectable service for testability...
var deferred = $q.defer();
gapi.client.realestate.get($stateParams.propertyId).execute(function(r) {
deferred.resolve(r);
});
return deferred.promise;
}
}
Finally, the values for resolve functions are injectable in your controller once resolved:
myapp.controller('PropertyController', function($scope, propertyData) {
$scope.property = propertyData;
});
I think your controller function needs $stateParams parameter from which you can get your propertyId. Then you can use $q parameter and create promise to set $scope.property with something like this:
var deferred = $q.defer();
gapi.client.realestate.get(propertyId).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property=deferred.promise;
Here is description of using promises for handling async calls.
Try this easy way to use resolve in proper way
State code:
.state('yourstate', {
url: '/demo/action/:id',
templateUrl: './view/demo.html',
resolve:{
actionData: function(actionData, $q, $stateParams, $http){
return actionData.actionDataJson($stateParams.id);
}
},
controller: "DemoController",
controllerAs : "DemoCtrl"
})
In the above code I am sending parameter data which I am sending in the url,For examples if i send like this /demo/action/5
this number 5 will go to actionData service that service retrieve some json data based on id.Finally that data will store into actionData You can use that in your controller directly by using that name
Following code return some JSON data based on id which iam passing at state level
(function retriveDemoJsonData(){
angular.module('yourModuleName').factory('actionData', function ($q, $http) {
var data={};
data.actionDataJson = function(id){
//The original business logic will apply based on URL Param ID
var defObj = $q.defer();
$http.get('demodata.json')
.then(function(res){
defObj.resolve(res.data[0]);
});
return defObj.promise;
}
return data;
});
})();
How about this:
function PropertyController($scope, $stateParams) {
gapi.client.realestate.get($stateParams.propertyId).execute(function(resp) {
$scope.property = resp;
});
}