Passing data to sub-controller for insert or update - angularjs

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

Related

Triggering function on another component in the same module

I have a module called LegalModule, there are three components that subscribe to the same module, basically:
Both components have their own folder and each have an index.js file where they bootstrap like:
angular.module('LegalModule')
.component('person', require('person.component.js')
.controller('PersonController', require('person.controller.js');
and another file like
var component = {
templateUrl: 'person-tamplate.html',
controller: 'PersonController',
bindings: {info: '<'}
}
module.exports = component;
Then in that controller i have something like :
var controller = ['PersonRepository','$stateParams', function(PersonRepository, $stateParams)
{
var vm = this;
//other code
function Save(){
//code that saved
}
function onSuccess(){
//Let another component know this happened and call its refresh function.
}
}];
Other component / controller
angular.module('LegalModule')
.component('buildings', require('buildings.component.js')
.controller('BuildingController', require('buildings.controller.js');
and the component
var component = {
templateUrl: 'building-template.html'
controller: 'BuildingController'
}
Controller
var controller = ['BuildingReader',function(BuildingReader){
function refreshBuildings(){
//this needs to be called on success of the save of the Person Repository
}
}];
On the main tamplate:
<div class="LegalFacilities">
<person></person>
<buildings></buildings>
</div>
So i am new to components and i am not sure how to make in a way that when something is saved in the person controller, on it's success, that it can trigger the refresh function in the building controller to fire.
I really do not want to use $scope or anything like that , there is gotta be a cleaner way?. (not sure but i would appreciate any inputs).
Since you have two components that are not on the same DOM element, your methods of communicating between them are more limited. You still have several ways that you can do it:
onSuccess() emits an event on the $rootScope and all interested controllers listen for that event (just make sure to unsubscribe to the event on $destroy).
Create one or more services that contain the all the non-UI shared application state. All controllers that need access to state inject the service that contains that state. And controllers can also $watch a variable on the service to be notified when something changes and something needs to be refreshed.
Pass state around using the parent scope. Ie- each child scope declares a scope variable that is bound to the same variable in the parent scope. And if the state changes in one of the child scopes, the $digest cycle will ensure that the state is propagated to the other child scope.
In general, my preference is #2. The reason is that this keeps a clear separation between application state and UI state. And it becomes very easy to ensure that all parts of your application can share bits that they need to.
In your case, since you need to notify that an action happened, you can trigger this through changing a successHash number (an opaque number that just gets incremented on every save such that all watchers are notified).
Edit: a very simple example of sharing state using services.
angular.module('mymod').service('myService', function() {
this.val = 9;
});
angular.module('mymod').directive('dir1', function(myService, scope) {
scope.doSomething().then(res => myService.val = res);
});
angular.module('mymod').directive('dir2', function(myService, scope) {
scope.$watch(() => myService.val, () => console.log(`It happened! ${myService.val});
});

communication between controllers in 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.

View updates everywhere but in current controller - angularJS

EDIT: As asked, I'll explain a bit more efficiently !
I've been sitting in front of an annoying problem recently, which is that whenever I update a value inside a directive, the controllers I'm not currently "in" are the only ones to be updated properly.
Scenario example: Profile page is made of two controllers. Navbar_controller which is just currently displaying the user name :
<div ng-if="Auth.isAuthenticated">Hello, {{Auth.getCurrentUser().name}}</div>
The second controller , Profile_controller is here to update user values. This is a simple function in the angular first controller, which updates CurrentUser:
$scope.updateUser = function (type, form) {
if (!$scope.modif)
return ;
$http.put('/api/users/' + Auth.getCurrentUser()._id + '/update', {type:type, modif:$scope.modif})
.success(function (data, status) {
$scope.user = Auth.setNewUser(data);
})
.error(function () {
console.log("error");
});
};
When I update, for example, the name. I can see that the database has been modified properly. And indeed, navbar_controller got the update because a new name is printed in the div. However, Profile_controller doesn't get the update: the name printed in the profile page didn't change.
Here are the two basic functions in Auth.service.js :
getCurrentUser: function() {
return currentUser;
},
// 'user' is the data retrieved in http put request dot success
setNewUser: function(user) {
currentUser = user;
$rootScope.$broadcast(); // Navbar_controller is updated with or without this line
return currentUser;
}
Anyway, if I look at the navbar and its controller, which is calling Auth.getCurrentUser() method, the user values are instantly modified. I'e been using an ugly method consisting in modifying the controller values manually or by refreshing the page... But this isn't the way to go, right ?
There must be something with "$rootScope.$broadcast();", but I'm really new to Angular and other questions on stackoverflow are too specific to help me understand properly.
Thank you !
Your question was a little difficult to understand, but I think the problem is that you are reference a changing object in your various controllers. Here is an example to explain:
Service:
var myObject = { ... };
return {
getObject() { return myObject; }
setObject(obj) { myObject = obj; }
};
Controller 1:
$scope.myObjA = Service.getObject();
Controller 2:
$scope.myObjB = Service.getObject();
Now on initialisation both controllers will be referencing the same object, so if you changed a property inside either controller (eg. $scope.myObjB.name = 'bob';), then the other controller would also see the name.
However if you changed the object itself in a controller (eg. Service.setObject(newObj);), then the controller will be referencing the new object, while the other controller will still be referencing the old one.
You can fix this by wrapping your service object in a container:
var cont = {
user: ...
};
function getContainer() { return cont; }
function setNewUser(user) { cont.user = user; }
Then inside your controllers, get the container (not the user):
$scope.cont = Service.getContainer();
And inside your html:
<div>{{cont.user.name}}</div>
Now when you update the user, all attached controllers will be updated.
Well I'd try to change and store the user information in $rootScope, for your scenario could be a good fit.
getCurrentUser: function() {
$rootScope.currentUser===undefined ? 'no User': $rootScope.currentUser;
},
setNewUser: function(user) {
$rootScope.currentUser = user;
//$rootScope.$broadcast(); no need to broadcast
return getCurrentUser();
}
in that way currentUser will be updated in different scopes as needed!
I'll quote AnuglarJs FAQ regarding to $rootscope:
$rootScope exists, but it can be used for evil
Occasionally there are pieces of data that you want to make global to
the whole app. For these, you can inject $rootScope and set values on
it like any other scope. Since the scopes inherit from the root scope,
these values will be available to the expressions attached to
directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly,
like you would (hopefully) use with global variables in any language.
In particular, don't use it for code, only data. If you're tempted to
put a function on $rootScope, it's almost always better to put it in a
service that can be injected where it's needed, and more easily
tested.
Conversely, don't create a service whose only purpose in life is to
store and return bits of data.

Modal window did not update when model changes

I have a list of groups in my application. when i click on the group, a modal window opens using the modalController with the group related data. form this modal user can make changes to the group. now when i make any change in the group and save it. the changes reflect in the list in the background but not in the modal box.
What i can do here?
:(( suplicate::((
Modals in anuglar , regularily has their own instance and controller , they dont get their parrent scope(scope of your view)
So you have to Send this scope(scope of your view) with a resolve property like this :
There should be a resolve property in your modal instance , where you can resolve anything you want ;
yourApp.controller('yourViewController',function($scope,...){
$scope.openModal = function (size) {
var modalInstances = $modal.open({
templateUrl: 'yourModalTemplateURL.html',
controller: 'YourModalController',
size: "YourModalSize",
resolve:{
info :function(){
// you want to see your scope later in your modal
// so you have to resolve it and sent it via this property
return $scope;
}
}
});
};
})
And in your ModalController you can inject that info
app.controller('YourModalController',function($scope,info){
console.log(info); // will log the scope of your controller
});

How do I store a current user context in AngularJS?

I have an AuthService, which logs in a user, it returns back a user json object. What I want to do is set that object and have all the changes reflected across the application (logged in/logged out state) without having to refresh the page.
How would I accomplish this with AngularJS?
The easiest way to accomplish this is by using a service. For example:
app.factory( 'AuthService', function() {
var currentUser;
return {
login: function() { ... },
logout: function() { ... },
isLoggedIn: function() { ... },
currentUser: function() { return currentUser; }
...
};
});
You can then reference this in any of your controllers. The following code watches for changes in a value from the service (by calling the function specified) and then syncs the changed values to the scope.
app.controller( 'MainCtrl', function( $scope, AuthService ) {
$scope.$watch( AuthService.isLoggedIn, function ( isLoggedIn ) {
$scope.isLoggedIn = isLoggedIn;
$scope.currentUser = AuthService.currentUser();
});
});
And then, of course, you can use that information however you see fit; e.g. in directives, in templates, etc. You can repeat this (customized to what you need to do) in your menu controllers, etc. It will all be updated automatically when you change the state on the service.
Anything more specific depends on your implementation.
I would amend the good response of Josh by adding that, as an AuthService is typically of interest of anyone (say, anyone but the login view should disappear if nobody is logged), maybe a simpler alternative would be to notify interested parties using $rootScope.$broadcast('loginStatusChanged', isLoggedIn); (1) (2), while interested parties (such as controllers) would listen using $scope.$on('loginStatusChanged', function (event, isLoggedIn) { $scope.isLoggedIn = isLoggedIn; }.
(1) $rootScope being injected as an argument of the service
(2) Note that, in the likely case of a asynchronous login operation, you'll want to notify Angular that the broadcast will change things, by including it in a $rootScope.$apply() function.
Now, speaking of keeping the user context in every/many controllers, you might not be happy listening for login changes in everyone of them, and might prefer to listen only in a topmost login controller, then adding other login-aware controllers as children/embedded controllers of this one. This way, the children controller will be able to see the inherited parent $scope properties such as your user context.

Resources