communication between controllers in angularjs - angularjs

The 1st controller has an Id.
The 2nd controller has a function that takes the id and opens a modal.
I want to pass the id from the 1st controller to the 2nd controller and open the 2nd controller's modal while staying in the 1st controller.
There is no relationship between these controllers.
I tried to use $rootScope.broadcast but things are not working as expected.
How can I achieve this feature ?
EDIT:
1st controller:
$scope.getId = function(id){
$scope.id = id;
$rootScope.$broadcast("info", $scope.id);
}
2nd controller:
$scope.viewModal = function(id) {
$scope.id = id;
var modalInstance = $modal.open( {
templateUrl: 'src/web/viewModal.html',
controller: 'viewCtrl',
size: 'lg',
resolve: {
id: function () {
return $scope.id;
}
}
} );
}
$rootScope.$on("info", function(event, id) {
$scope.id = id;
$scope.viewModal(id);
});
I am not sure how the modal from 2nd controller gets invoked while I am clicking on $scope.getId from 1st controller.
I am kind of apprehensive to use Services at this stage because that will involve lot of code change to the existing setup.Please advise.

Communicating between controllers should always be done through a service.
For the modal part you could either figure out your own logic or you could simply use UI-Boostrap Modal. It supports throwing data at it so you could basically have an async request populating in your modal based on an ID you sent it. This is done by giving the modal its own scope and it each modal will have a resolve that you can use to make sure the modal has the correct data in its scope.

Related

Controller 'Init' Function Executed More Than Once

This is my controller
//init
var init = function () {
$scope.getAlbumList();
};
$scope.getAlbumList = function () {
AlbumService.getAlbumList()
.then(function (data) {
$scope.albumList = data;
});
};
$scope.viewAlbum = function () {
AlbumService.getAlbum()
.then(function (data) {
$scope.album = data;
$location.path("/album");
});
};
init();
and this is my routeProvider
when('/albums', {
templateUrl: 'WebApp/albums.html',
controller: 'AlbumController'
}).
when('/album', {
templateUrl: 'WebApp/albumview.html',
controller: 'AlbumController'
}).
So the AlbumController handles both the albums and albumview pages.
When the controller is created, the init function is called which in turns calls the getAlbumList function.
The problem is that the controller is created again when the user clicks an album to go to its albumview page. This ends up executing the init function again which in turns causes an undesired second call to getAlbumList.
So basically, I need getAlbumList to be called when the controller is created for '/albums' but not '/albumview'.
How can I achieve this?
Obviously, I could solve this problem by creating a second controller for /albumview but this seems unnecessary and I'd rather have everything regarding albums in one controller.
You have 2 solutions:
in your controller test if location.path() == 'albums'
in the html page of albums ng-init="getAlbumList()"
Probably the quickest option is to check the path to see if you're in album or albums. Here is sample code of how to do that:
if ($location.url().toLowerCase() === '/albums') {
// call to getAlbumList here
}
It will happen if you have controller: 'AlbumController' in router and your html have ng-controller = "AlbumController". Use only one to avoid double initilization.

How to pass response data from http.get() inside of mdDialog to main controller?

I have a mdDialog inside of my application that makes a call to an API which responds with some data. I need this data response to be accessible in the main controller so that it can be used in another call.
CRUD operation
$scope.add_id = [];
$scope.createAddress = function() {
$http.get('api.php?action=create-address', {
params: {
add1: $scope.newadd.add1,
add2: $scope.newadd.address2,
city: $scope.newadd.city,
state: $scope.newadd.state,
zip: $scope.newadd.zip
}
})
.success(function(response) {
$scope.add_id = response.data[0].address_id;
});
};
mdDialog
$scope.addParcel = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'libs/html/addparcel.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
scope: $scope,
preserveScope: true
});
};
So inside of the addParcel mdDialog I have a button that executes the addAddress function and it turns successful and I can access the data within the dialog. However, even though the dialog is set to preserve the scope, the address_id does not appear at all in the main controller after I close the dialog. Am I supposed to use locals? How can I get the data to be accessible in the main controller? Please be as informative as possible as I am still new to AngularJS. Thanks.
It's because mdDialog creates a new child scope that inherits from your page controller's $scope.
You could do a couple of things,
create an object in your page controller, something called "addressDetails", and access that in your addAddress function
// Page Controller
$scope.addressDetails = {};
// addAddress
$http.get(...).then(function (response) {
$scope.addressDetails.add_id = response.data[0].address_id;
});
use $scope.$parent (not recommended)
You can read this wiki about scope inheritance (it's a great article)
https://github.com/angular/angular.js/wiki/Understanding-Scopes

Passing data to sub-controller for insert or update

I have a main controller for my profile edit page, from where users can add their education, in a modal window. The modal has it's own controller. The user can add as many education items they want and they can modify the existing ones.
Now for my issue. I want the modal controller to take care of both adding a new item and updating an existing one. The difference is that on update, the controller should receive a fully populated education object, which it should update. I don't know how to pass this object from the main controller to the modal controller. I've seen some ways of passing data between controllers via services, but that seems too much of a hassle, and it doesn't make sense to me to use a service in this particular case.
My main controller creates modals like so:
vm.openEducation = function(){
$modal.open({
templateUrl: "some.html",
controller: "SomeController",
controllerAs: "vm",
size: 'lg'
}).result.then(function (education) {
vm.educations.push(education);
});
}
And the child controller (which currently only supports insert, hence the empty init of vm.education):
function SomeController($scope){
var vm = this;
vm.education = {}; // or get from parent controller
vm.save = function () {
$scope.$close(vm.education);
};
vm.close = function () {
$scope.$dismiss();
}
return vm;
}
You can use $rootScope.$broadcast to send data from the modal and then $scope.$on to receive that data anywhere else.
For example:
// in your modal
$rootScope.$broadcast('education:updated', yourDataObj)
// in your controller
$scope.$on('education:updated', function(e, data) {
console.log(data)
})
Note that the $broadcast will be picked up by anything that's listening for it, so if you have multiple controller instances you will get multiple console.log messages

What is a correct way to bind document level key events in AngularJS specific only to a certain route/controller?

I have a single page app that opens a gallery. I want to bind document level keyup event (for keyboard gallery controlls) only when the gallery is open, ie. when route matches
.when('/reference/:galleryId/:imageId/', { templateUrl: '/partials/gallery.htm', controller: 'galleryController', controllerAs: 'reference' })
and I want to unbind it when I leave this route.
One thing that might be a problem is, I block reloading the view between images within the same gallery with this:
.run(['$route', '$rootScope', '$location', function ($route, $rootScope, $location) {
var original = $location.path;
$location.path = function (path, reload) {
if (reload === false) {
var lastRoute = $route.current;
var un = $rootScope.$on('$locationChangeSuccess', function () {
$route.current = lastRoute;
un();
});
}
return original.apply($location, [path]);
};
}])
Demo (Click on "Fotografie" to open the gallery)
http://tr.tomasreichmann.cz/
Angular wiz to the rescue?
Thank you for your time and effort
You could bind a keyup event to $document in your controller's constructor and then unbind the event when the controller's $scope is destroyed. For example:
.controller('galleryController', function ($scope, $document) {
var galleryCtrl = this;
function keyupHandler(keyEvent) {
console.log('keyup', keyEvent);
galleryCtrl.keyUp(keyEvent);
$scope.$apply(); // remove this line if not need
}
$document.on('keyup', keyupHandler);
$scope.$on('$destroy', function () {
$document.off('keyup', keyupHandler);
});
...
});
There will be nothing left behind when the view become inactive.
If you feel it isn't right to do this in the controller, you could move this into a custom directive and place it in a template of the view.
Finally I stuck with
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:og="http://ogp.me/ns#"
xmlns:fb="http://www.facebook.com/2008/fbml"
lang="cz"
ng-app="profileApp"
ng-keyup="$broadcast('my:keyup', $event)" >
Not sure if this is good practice, but it registers within my controller
$scope.$on('my:keyup', function(event, keyEvent) {
console.log('keyup', keyEvent);
galleryCtrl.keyUp(keyEvent);
});
And doesn't do anything when the current route is not active
I found this answer here: https://groups.google.com/forum/#!searchin/angular/document$20level$20events/angular/vXqVOKcwA7M/RK29o3oT9GAJ
There is another way to bind it globally which wasn't my goal, the original code in question did what I needed.

AngularJS push of undefined

Cannot call method 'push' of undefined
I receive that error when my AngularJS runs the following:
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
I declared my $scope.key = []; right after my .controller as I need to be able to use the $scope.key in other parts of the application. Could someone please point out where I should be declaring this?
$scope.ok is my Save Button which pulls the data from my input fields and $scope.plotmarkers is what I am using to pull the data from the inputs that have been pushed.
app.controller('MenuSideController', ['$scope','$modal','$log', function($scope, $modal, $log) {
$scope.key = [];
$scope.createmarker = function () {
var modalInstance = $modal.open({
templateUrl: 'template/modal-add-marker.html',
controller: ModalInstanceCtrl,
resolve: {}
});
modalInstance.result.then(function (selectedItem) {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
$scope.plotmarkers = function() {
console.log($scope.key);
};
}]);
Don't forget to pass a scope to $modal.open! If you don't, it will default to the root scope, which is not a child of the controller's scope, so key is not defined on it or its parents. You can use { scope: $scope.$new(), ... } as a parameter of $model.open to pass it a child of the controller's scope. See the docs for details. Good luck!
I think you are using $modal a bit improperly.
So you have two controller here - one for the application logic and one for the modal window itself. According to best practice you shouldn't interact between different controllers directly (the case with parent-child directive is exclusion but honestly speaking it not direct interaction - rather your linker function used the controller from parent directive). Instead of interaction between controller in general we should use services. It is just additional note.
What is related to your question - you have two things here to keep in mind:
if you want to pass the information from the controller to the modal window you should use resolve property which actually specifies multiple functions which are called to get the data and then injected to the modal windows controller as a function parameter. This way you can pass some data from the main controller.
if you need to pass the result back you should use result property of the modal instance which is the promise (by using your $modalInstance.result.then(function(result){ ... }); ) To pass this object from the modal you can close it with the result as a parameter like this: $modalInstance.close(result);
Hope this helps. For further details you can look at the documentation for the $modal: Angular Bootstrap

Resources