Angular ui router undefined function in controller - angularjs

I have a ui route that looks like the following
.state('question', {
url: '/question',
templateUrl: 'views/templates/question.view.htm',
controller: 'QuestionController',
resolve: {
questions_preload: function(Question) {
return Question.query();
}
}
});
The Question.query() function looks like
Question.query = function () {
var deferred = $http.get(HATEOAS_URL);
return SpringDataRestAdapter.processWithPromise(deferred).then(function (data) {
Question.resources = data._resources("self");
return _.map(data._embeddedItems, function (question) {
return new Question(question);
});
});
};
and the controller that should have the questions preloaded at the start beings like this
angular.module('myapp').controller('QuestionController', function ($scope, Question) {
$scope.questions = questions_preload;
Unfortunately while the Question.query() method runs correctly the questions_preload which I figured would house the array is undefined when the controller executes.
I believe it's something to do with the deferred in the query function?
Would anyone have any idea?
Thanks,
Mark.

You have to inject the questions_preload value into your controller:
angular.module('myapp')
.controller('QuestionController',
function ($scope, Question, questions_preload) {
...
$scope.questions = questions_preload;
As you had it you were simply accessing an undefined variable.
The use of a promise in Question.query() doesn't matter here as angular ui-router will wait until the promise has resolved before it instantiates your controller.

Related

Pass data from modal to another page in angularjs

I need to pass data from modal to another page. Could you please help me to accomplish this task?
$scope.productdetails = function (size,selectedproduct)
{
var modalInstance = $uibModal.open({
templateUrl: 'ProductDetails.html',
controller: function ($scope, $uibModalInstance, product) {
$scope.product = product;
$scope.buynow = function (path) {
$uibModalInstance.close($scope.product);
$location.path(path); // Need to pass $scope.product to the new page
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
},
size: size,
resolve: {
product: function () {
return selectedproduct;
}
}
});
Be sure to read the docs on $uibModal: https://github.com/angular-ui/bootstrap/tree/master/src/modal/docs
the open() method will return an object with some useful stuff in it. Currently you're not doing anything with that object, but that's where the magic is.
To pass data to the controller that opened the modal, use the modalInstance.result promise like this:
modalInstance.result.then(function(data) { /*... do something with the data*/ });
Once that is set up, you can use the $close() function that this modal library places on the scope to resolve the result promise.
var data = {info: 'information to be returned to the parent controller'};
$scope.$close(data);

AngularJS Two way data binding

In my controller I have the following data:
(function() {
'use strict';
angular
.module('myappl.mymodule')
.controller('MyController', MyController);
MyController.$inject = ['$scope', 'myService'];
function MyController($scope, 'myService') {
$scope.vm = this;
var vm = this;
vm.myService = myService;
vm.userManagement = userManagement.data;
vm.userManagementSomeDataObjects = vm.userManagement.someDataObjects;
Somewhere in this controller I have a function which first gets data from backend and than invoke showModal:
function modalForUserInteraction() {
vm.myService.getData(parameters).success(function(data) {
vm.modalService.showModal(data, vm.userManagement, vm.userManagementSomeDataObjects);
}).error(function(data) {
console.log('error');
});
}
The modal- controller looks like this:
...
function showModalService($modal, $stateParams, otherService) {
var service = {
showModal: showModal
};
return service;
////////////
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
userManagement: function() {
return userManagement;
},
userManagementSomeDataObjects: function() {
return userManagementSomeDataObjects;
}
},
templateUrl: 'url/to.html'
});
return myModal;
and in the modal controller there is a method like this one:
function ModalController(userManagement, userManagementSomeDataObjects) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
userManagement = data;
userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}
If I do it like this:
userManagement = data; and userManagementSomeDataObjects = data.someDataObjects; than the new data is not set.
If I set each property separately of the objects than it works more often than not but somethimes it does not.
My question now would be what I can do in order to get it work.
Currently I do not have a $scope- variable in my modal and actually I don't know
if $scopeOfModal.$apply() would help and I also don't know how to get access from modal to MyController - $scope.
I would be glad for any hint in this direction.
Thanks a lot!
[EDIT]
Here is an image of my currently viewed (right) an on the left side the object, which should be shown after setting in modal- function.
[EDIT]
is there any posibility to pass parameters to this function in the modal controller:
this.previewArchivedSchedule = function(hereINeedParamerts) {
alert('archivedScheduleIntervalContainerId: ' + hereINeedParamerts);
};
This looks to me just like it may have nothing to do with angular, just some confusion with javascript variable references.
First you pass userManagement from showModalService.showModal to ModalController via resolve.
So now ModalController has a reference to the same object as showModalService.
However, in ModalController, you reassign the userManagement variable to point to data instead. So now the userManagement variable inside ModalController isn't pointing at the injected object anymore, because you've reassigned it. This has nothing to do with angular two-way data binding, it's just javascript variable assignment. You've lost your reference to the original object.
showModalService still has a reference to the instance that it sent in via resolve, it has no idea that you swapped the reference out in the ModalController.
I'd try sending over an object encapsulating the data you want to share to fix this problem.
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
sharedData: function() {
return {
userManagement: userManagement,
userManagementSomeDataObjects: userManagementSomeDataObjects
}
},
templateUrl: 'url/to.html'
});
return myModal;
Then manipulate the properties on the shared object instead of overwriting references.
function ModalController(sharedData) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
sharedData.userManagement = data;
sharedData.userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}

How get access to global variable from Factory Angular JS?

I tried to write factory method in Angular JS:
.factory('FriendsFactory', function(){
var friend = {};
friend.delete = function(id) {
notificationsModal.show('danger', messages_info[86]);
notificationsModal.confirm(function() {
this.deleteAjax(event, id, type);
})
}
friend.deleteAjax = function (event, id, type){
var target = angular.element(event.target);
var request = $http({
method: "POST",
url: "/subscribe/deletesubscriber",
data: $.param({ id : id, type : type }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
request.success(function () {
target.closest('.notif-item').remove();
$scope.counter--;
});
request.error(function () {
// TODO
});
}
return friend;
})
This code has two methods: friend.delete() also friend.deleteAjax()
Calling functions from Factory:
.controller('FriendsController', ['$scope','$http', 'friendsFactory', function($scope, $http) {
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser);
}
}])
I need decrement variable $scope.counter in friend.deleteAjax() ajax response, regardless controller from was called factory.
I can do duplicate in each controller:
$scope.counter = 10; and after call factory, but it is not good
Although the answer suggested by #JBNizet is absolutely correct but if you are bound to use the code in the way it is, then you can do two things. First is to simply pass the $scope from controller to service call (which is not a cleaner approach is not recommended):
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser, $scope);
}
And you can use the scope inside the factory.
The second option to use current controller's scope to root scope and then use this in the factory.
In your controller
$scope.deleteUser = function (idUser) {
$rootScope.callingControllerScope = $scope;
friendsFactory.delete(idUser);
}
In your factory
friend.deleteAjax = function (event, id, type){
console.log($rootScope.callingControllerScope.counter);
// your code
}
And you also need to fix your dependency injection:
.controller('FriendsController', ['$scope','$http', 'FriendsFactory', function($scope, $http, friendsFactory) {
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser, $scope);
}
}]);
You're doing many, many things wrong:
Using friendsFactoryinstead of FriendsFactory:
.controller('FriendsController', ['$scope','$http', 'friendsFactory'
here --------^
Forgetting to declare friendsFactory as an argument of the controller function:
.controller('FriendsController', ['$scope','$http', 'friendsFactory', function($scope, $http) {
here ----^
Accessing an undefined $scope variable in the service:
$scope.counter--;
^--- here
Doing DOM manipulation in a service...
The service responsibility is not to manipulate the DOM and the controller scope.
The DOM should be modified using directives in the html template.
The controller scope should be managed by the controller, not by the service. Return the promise request from the deleteAjax() function, and let the controller register a success callback, rather than doing it in the service. This callback will then be able to access the controller scope.
Note that most errors are basic JavaScript error that should be signalled by a good JavaScript editor, or at least by looking at errors in the console of your browser.

AngularJS factory not working

I have abstracted my working code from a controller into a factory, but it doesn't seem to be working and I can't find what's wrong. I opted for a factory rather than a service because I wanted to execute some code that defined the variable before returning that variable; I want to get result.station (a part of the data returned by the API), not the full result.
This is my code:
var app = angular.module("myApp", []);
app.factory('api', ['$http',
function($http) {
var station_list = [];
$http({
method: 'GET',
url: 'http://api.irail.be/stations/?format=json&lang=nl'
})
.success(function(result) {
station_list = result.station;
});
return {
Stations: function() {
return station_list;
}
};
}
]);
app.controller("myController", ['api', '$scope',
function(api, $scope) {
$scope.station_list = api.Stations();
$scope.title = "Stations";
}
]);
and a working example.
Try this:
.success(function(result) {
angular.copy(result.station, station_list);
});
You had a small error, you were replacing the array instead of populating it. I used angular.copy instead of the assignment in your factory and it works
http://plnkr.co/edit/sqgKcFZAcClmkfdXHhrz
The problem is that you are dealing with asynchronous nature of AJAX.
I would suggest to have a delegate method in controller, which will be called when the service call is complete.
Something like the following:
app.controller("myController", ['api', '$scope',
function(api, $scope) {
api.Stations( function(station_list) {
$scope.station_list = station_list;
});
$scope.title = "Stations";
}
]);
The following is a service method excerpt:
return {
Stations: function(delegate) {
if (delegate)
delegate(station_list);
return station_list;
}
};

Angular ui-router get asynchronous data with resolve

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;
});
}

Resources