Angular - How to watch for ngResource values in parent controller scope? - angularjs

In my angular app, i've got a controller parent child relationship like the one shown below. My parent controller is loading a resource using ngResource and doing some rendering in the view. I'd like to use the resource from the parent scope in my child controller, but because the ngResource call is asynchronous, the ImageUrls value in the scope isn't defined yet. I thought that setting up a watcher in the child controller was the way to go but it hasn't worked.
Does anyone have any idea how to get around this? Thanks!
template.html
<div ng-controller="ParentController">
<div ng-controller="ChildController">
<div ng-repeat='img in myResource.ImageUrls'>
<div ng-show="doShowImg(img)">
</div>
</div>
</div>
</div>
script.js
function ParentController($scope, $routeParams, MyResource) {
$scope.myResource = MyResource.get({recipeId:$routeParams.recipeId}, function() {
$scope.imageUrls = $scope.myResource.ImageUrls;
});
}
function ChildController($scope) {
$scope.$watch('imageUrls', function(imageUrls) {
$scope.doShowImg = function(img) {
if($scope.imageUrls.length = 5) {
...
}
}
}
}
It's worth mentioning that although I'm getting the javascript errors because my value in the scope is undefined, my app is still working. It appears as though the function in the child controller is called, then the resource is loaded, then the repeater is executed and the function is called again on the images in the list.

Related

AngularJS | Handling $broadcast & $on for specific instance of the same controller

I have a utility controller build to manage documents attachments for reusing across my application.
<div ng-controller="someController">
<div ng-controller="documentController as temp1"></div>
<div ng-controller="documentController as temp2"></div>
</div>
Under the parent controller i.e. someController I have a broadcast method..
var module = angular.module("MyModule");
module.controller("someController",
function ($scope) {
$scope.$broadcast("callSomeFunctionInDocumentsController");
});
module.controller("documentController",
function($scope) {
$scope.$on("callSomeFunctionInDocumentsController", function() {
//do something here
});
});
Now the problem I am facing is that since the documentController is added twice to the view, the $on method is executed twice as well. Whereas based on some condition I would want to call the $on method either in temp1 or temp2 instance and not both.
I am not sure if what I wish to achieve is possible but any help will be much appreciated.
Thanks.
The $broadcast works simply: everyone who registered is notified through $on.
In your example, both controllers are registered.
So why do you use the same controller twice? Maybe worth to switch to component?
What about this one:
<div ng-controller="someController">
<div ng-controller="documentController as temp1"></div>
<div ng-if="oneCtrlGotNotification" ng-controller="documentController as temp2"></div>
</div>
where oneCtrlGotNotification is some flag (maybe under $rootScope).
So you will display second controller only when 1st already notified.
But it is a workaround.
One approach is to give a unique id to each element with a controller:
<div ng-controller="someController">
<div id="temp1" ng-controller="documentController as temp1"></div>
<div id="temp2" ng-controller="documentController as temp2"></div>
</div>
Then use the $attrs local to differentiate:
app.controller("documentController", function($scope, $attrs) {
$scope.$on("callSomeFunctionInDocumentsController", function() {
if ($attrs.id == "temp1") {
//do something specific to "temp1" controller
});
});
})
For more information, see
AngularJS Comprehensive Directive API Reference - controller
AngularJS $attrs Type API Reference

How to avoid using `$parent.$parent.$parent` in nested scopes

How can I know the Parent controller name in controller chain of Angular?
I'm new to Angular so I have one basic question.
Suppose, In Angular, I have a controller's chain like below.
<div ng-controller="parentController">
... something in the parent element
<div ng-controller="childController">
... something in the child element
</div>
</div>
Is there any way to write the code in the child element so that I can know the parent controller name in the output (In this case output should be 'parentController')?
I need this because I have a too big project and want to know the parent of each controller because someone has wrote the code like
googleOAuth= $scope.$parent.$parent.$parent.$parent.status.googleOAuth
and I'm not able to understand so want to know the parent of each $scope.
On approach is to use "controller as" syntax:
<div ng-controller="parentController as top">
<!-- ... something in the parent element -->
<div ng-controller="childController">
<!-- ... something in the child element -->
{{top.status.googleOAuth}}
</div>
</div>
This requires the controller be written using the this context instead of $scope.
Another approach is to use a property of an object in the parent scope:
app.controller("parentController", function($scope) {
$scope.top = {status: {} };
$scope.top.status.googleOAuth = value;
});
<div ng-controller="parentController">
<!-- ... something in the parent element -->
<div ng-controller="childController">
<!-- ... something in the child element -->
{{top.status.googleOAuth}}
</div>
</div>
Because scopes use prototypical inheritance, the top property is available to child scopes.
See AngularJS Developer Guide - Scope Hierarchies.
As georgeawg said, using $parent is not optimal because it relies on a constant number of scopes.
Instead, you could write a service to deal with your googleOAuth.
The service can then be injected in each controller and will function as a single source of truth because services are singletons in AngularJS.
e.g. something like this
angular.module('appModule', [])
.factory('googleOAuthService', [function() {
var googleOAuth = {
// your googleOAuth stuff here
};
return {
get: get,
set: set,
stuff: stuff
}
function get () {
return googleOAuth;
}
function set (newGoogleOAuth) {
googleOAuth = newGoogleOAuth;
}
function stuff () {
// Do stuff to googleOAuth
}
}])
.controller('parentController', ['googleOAuthService', function(googleOAuthService) {
googleOAuthService.stuff();
}])
.controller('childController', ['googleOAuthService', function(googleOAuthService) {
googleOAuthService.stuff();
}]);
For more info, see https://docs.angularjs.org/guide/services

Programmatically creating new instances of a controller

So here is my problem, I have some functions/variables in a parent controller
function parentController($scope) {
$scope.numberOfChildren = $scope.numberOfChildren + 1 || 1;
console.log($scope.numberOfChildren);
$scope.someFunction = function(argument) {
// do stuff
$scope.someVariable = result of the function
}
}
I am calling this controller in two other controllers that are directives controllers and are called in the same view
function firstChildController ($scope, $controller) {
var aVariable = 1;
$scope.otherVariable = 10;
$controller('parentController', {$scope: $scope});
$scope.someFunction(aVariable);
}
function secondChildController ($scope, $controller) {
var aVariable = 6;
$scope.otherVariable = 11;
$controller('parentController', {$scope: $scope});
$scope.someFunction(aVariable);
}
What I want, is not to share the parent scope for the two children.
Right now, there is only one instance of the parent controller and so when I call two directives depending on it on the same view, I get $scope.numberOfChildren === 2.
What I want is this parent controller to be loaded twice but have separated scopes ($scope.numberOfChildren === 1 in each child controller)
I managed to do this using ng-controller in the view template and deleting the $controller calls but I want to do it programmatically. (I don't want to have to write the same ng-controller code each time I am calling the directive).
<div ng-controller="parentController">
<first-directive></first-directive>
</div>
<div ng-controller="parentController">
<second-directive></second-directive>
</div>
Finally, to keep homogeneity in the code of the project, I'd rather not use the this and vm stuff to do the job if it possible.
parentController does NOT have its own scope, it operates on the $scope you're passing to it when you instantiate it this way $controller('parentController', {$scope: $scope}).
Checkout this simple demo fiddle.
The problem in your case might be caused by directives sharing the same scope and, thus, passing the same scope to the parent controller.
What you expect is exactly same with the way system run: scope is not sharing between two controller.
When you use a ng-controller in html, a new scope (controller instance) will be created. From your code above, two controller instance will be created. You can see it by adding {{$id}} to html and see id of scope instance.
<div ng-controller="parentController">
{{$id}}
<first-directive></first-directive>
</div>
<div ng-controller="parentController">
{{$id}}
<second-directive></second-directive>
</div>
If you see {{numberOfChildren == 2}} mean that your code is wrong in somewhere, not by sharing scope issue.

Angular: Service to Pass Data To Another Controlelr

So, I have a ng-repeated list of items as such.
<li><a ng-click="{{person.id}}">Name of Person</a></li>
I would like to create a service wherein, on click, I can collect that person.id and pass it to another controller in a different route.
This would normally be very simple by just using the url and route params, however, in this case it is important that the person.id not be exposed within the browser url.
-- More Context
Whether service or not, I am needing to extract a {{person.Id}} that is data available via an ng-repeat on a list page of persons.
On click, I move from a persons controller to a new route with a "person" controller. I need that "person" controller to be able to pull the {{Person.ID}} that was clicked on the previous route in order to look up that person in a DB.
Any help would be really great!
Services aren't meant to interact directly with DOM elements. DOM should interact with directives/controllers. Controller should interact with models.
This example below demonstrates sending data from controller 1 to myFactory and then controller 2 gets it the value from myFactory.
angular
.module('app', [])
.factory('myFactory', myFactory)
.controller('myCtrl1', myCtrl1)
.controller('myCtrl2', myCtrl2);
function myFactory() {
var fromSender = null;
return {
setSender: function(sender) {
fromSender = sender;
},
getSender: function() {
return fromSender;
}
};
}
function myCtrl1(myFactory) {
var vm = this;
vm.setSender = myFactory.setSender;
}
function myCtrl2(myFactory) {
var vm = this;
vm.getSender = myFactory.getSender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<div ng-controller="myCtrl1 as ctrl1">
Controller 1: <br>
<button ng-click="ctrl1.setSender('from controller 1')">Send to myFactory</button>
</div>
<hr>
<div ng-controller="myCtrl2 as ctrl2">
Controller 2: <br>
value from ctrl1 via myFactory: {{ctrl2.getSender()}}
</div>
</div>
All services in Angular are singletons. So if you inject personService or something like that, in multiple controllers, then those controllers will be using the exact same object. So if you set a value on that service, then the other controllers will be able to see it.
With more code and context, I'll be able to give a more specific example.

Call function on child state angularjs

In my view have a link that calls a function like: <a href=/albums ng-click='findAll()'>, and it all works fine, but when u type directly to the URL /albums nothing happens, findAll() function doesn't trigger.
My question is, Is there anything that I can do using states in Angular that when this state is executed to automatically call that function findAll() and render the view with the data.
Kinldy Regards,
For this you can add your function to $rootScope. Inject $rootScope for your parent controller.
app.controller('parentCtrl', function($rootScope, $scope){
$rootScope.findAll = function(){
/* your code here */
};
});
<div ng-controller='parentCtrl' ng-init='findAll()'></div> <!-- parent Div -->
or you can use run function
app.run(function($rootScope) {
$rootScope.findAll = function(){
/* your code here */
};
$rootScope.findAll(); // Call when angular bootstrap
});
For both in your child controllers you can use ng-init to call it automatically
<div ng-controller='childCtrl' ng-init='findAll()'></div> <!-- child Div -->

Resources