AngularJS referencing parent controller - angularjs

I am trying to implement the following behavior. I am including a template using ng-inculde where I am doing something like this: {{something}}
I want that 'something' to have double binding with another variable in the parent controller's scope and to have the ability to set the name of property of the parent controller's scope. So on one include, something will reference an apple and on other include, something will reference an orange.
What I do is, that I wrote a custom controller which has a lookAt(v) method and i am calling this method in ng-init of the div where i use ng-include and ng-controller. In this method, I am trying to set the binding but it does not work. I assume, that parent scope already has that variable defined.
Here is my code:
mod.controller('FooController', ['$scope', function($scope) {
$scope.lookAt = function (variable) {
$scope.something=$scope[variable];
}
}]);
Thanks for any suggestion on how to solve this.

In your template that is pulled in with ng-include, instead of {{something}} use something(), where the something method is defined on the parent controller.
You can still init your variable name if you want:
mod.controller('FooController', ['$scope', function($scope) {
$scope.initMethod = function(variable) {
$scope.variable = variable;
}
$scope.something = function () {
return $scope[$scope.variable];
}
}]);

When using Controller As Syntax you can't just do $scope.$parent in the consumer (constructor for child) you need to address the parent's data-object like so: $scope.$parent.vmParent.

Related

angular 1.5: watch doesn't call when i toggle the ui

I my template i have:
<md-checkbox ng-model="$ctrl.isAdOps" aria-label="isAdOps">
isAdOps {{ $ctrl.isAdOps }}
</md-checkbox>
In my component:
(function (app) {
app.component('homeComponent', {
templateUrl: 'partials/home-partial.html',
bindings: {
isAdOps: '='
},
controller: ['$scope', '$state', function ($scope, $state) {
var self = this;
$scope.$watch(
self.isAdOps,
function (isAdOps) {
$scope.$broadcast('isAdOpsToggled', isAdOps);
}
);
}]
});
})(myApp);
why doesn't the watch called when i toggle the md-checkbox?
Angular $watch first parameter should of type string/function
string: Evaluated as expression
function(scope): called with current scope as a parameter.
replace self.isAdOps with 'isAdOps' in $watch or alternativly use a function syntax with $scope.
You are better off using the ng-change directive:
<md-checkbox ng-model="$ctrl.isAdOps"
ng-change="$ctrl.isAdOpsChanged($ctrl.isAdOps)"
aria-label="isAdOps>
isAdOps {{ $ctrl.isAdOps }}
</md-checkbox>
It avoids using $scope and adding a watcher.
For more information, see Writing Components without Watchers.
See also, AngularJS Developer Guide - Component-based application architecture.
Components should follow a few simple conventions:
Inputs should be using < and # bindings. The < symbol denotes one-way bindings which are available since 1.5. The difference to = is that the bound properties in the component scope are not watched, which means if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent and component scope reference the same object, so if you are changing object properties or array elements in the component, the parent will still reflect that change. The general rule should therefore be to never change an object or array property in the component scope. # bindings can be used when the input is a string, especially when the value of the binding doesn't change.
Outputs are realized with & bindings, which function as callbacks to component events.
This will make the migration to Angular 2+ easier.

ionic, pass scope variable to popover scope

I would like to pass a variable from the view scope ($scope.sort) into the popover scope.
Once set in the popover, the variable 'goes out' of it with no issue though, with code below in$scope.closeSortPopover`.
I thought the popover scope was the same as the scope of the controller it comes from.
But it seems it's not the case.
Where is the popover scope ?
If I understand correctly from here the popover scope is a child of the controller scope. So how can I access it... ?
FYI my popover is a list of <ion-radio>s offering different sorting opions for the big list in my main view.
html:
<button ng-click="openSortPopover($event, sort)">
controllers.js
$ionicPopover.fromTemplateUrl('templates/computeSortPopover.html', {
scope: $scope
}).then(function(popover) {
$scope.sortPopover = popover;
});
$scope.openSortPopover = function($event, sort) {
$scope.sortPopover.show($event);
};
$scope.closeSortPopover = function(sort) {
$scope.sortPopover.hide();
$scope.sort = sort;
};
In the controller js, you are assigning the scope of the controller to the scope of the popover. Thus all the methods in the controller scope are accessible via the popover view. In other words, both the controller view and popover view share the same scope.
In fact you might think this a violation of mvc as one controller cannot own two views, but with angular, you actually can't otherwise.
To create isolated scope, your only option is directives.
I discovered that due to javascript inheritence, the scope variable must be contained into an object to be passed to inherited scopes.
So instead of this:
$scope.sort = ....
I declared this in my controller:
$scope.data={};
$scope.data.sort = ....
and used in my html (both in the initial view and in the popover templates):
data.sort
Documented here apparently: https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance

Pass value to Angular controller through ng-include

From a server side code I am loading and angular template as follows:
<div ng-include="'/views/signup.html'" onload="init('premium')"></div>
The view is, for now, the following:
<form ng-controller="AccountSignupController as controller">
</form>
The controller is simply:
function AccountSignupController($scope) {
$scope.plan = '';
$scope.init = function (plan) {
$scope.plan = plan;
console.log(plan);
};
};
On the console plan is always "undefined".
How can I pass a value to the controller through the template?
Do I need to set the controller on ng-include div and use ng-init?
The scope of the controller is inside the form where 'ng-controller' is. Thus $scope.init function is only "available" inside that form.
Both directives should be at the same level. Try moving 'ng-controller' to the div or 'ng-init' to the form (if possible).
You are instantiating your controller using the controller as binding syntax. In that case properties and functions are bound to the this of your controller instead of $scope. Functions should be called using the binding.
Your template should be:
<div ng-include="'/views/signup.html'"
ng-controller="AccountSignupController as controller"
onload="controller.init('premium')"></div>
Your controller:
function AccountSignupController() {
var self = this;
this.plan = '';
this.init = function (initString) {
self.plan = initString;
console.log(initString);
};
};
For more information on the controller as binding syntax, see the AngularJS ngController API

Angular scope variable is null when using ng-include (added a plunkr)

The model in the $scope isn't there when I try to access it in the save function.
As per the code below: desc has the value from the text input, however desc2 does not.
Is there anything wrong with the code, or am I missing something when it comes to the use of the $scope?
Html:
<input type="text" ng-model="myModel.description">
JavaScript
MyApp.controller('MyCtrl', ['$scope', function($scope) {
$scope.save = function() {
var desc = this.myModel.description; // This has a value
var desc2 = $scope.myModel.description; // This does NOT have a value
};
}]);
EDIT:
Added plunker: http://plnkr.co/edit/3l9ZE7O2wpbfnH7XPA9X?p=preview
This problem seem to be related to using ng-include.
If I don't use ng-include then it seem to work fine.
ng-include element introduces a child scope. Now your myModel is attached to a child, but your controller's $scope is the parent. Parent scopes can't see their children's variables, but children can see their parents'.
You can add myModel = {}; to your controller and it should work fine. That way the child scope references his parents' myModel and doesn't create his own.

Modify $rootscope property from different controllers

In my rootscope I have a visible property which controls the visibility of a div
app.run(function ($rootScope) {
$rootScope.visible = false;
});
Example HTML:
<section ng-controller='oneCtrl'>
<button ng-click='toggle()'>toggle</button>
<div ng-show='visible'>
<button ng-click='toggle()'>×</button>
</div>
</section>
Controller:
var oneCtrl = function($scope){
$scope.toggle = function () {
$scope.visible = !$scope.visible;
};
}
The above section works fine, the element is shown or hide without problems. Now in the same page in a different section I try to change the visible variable to show the div but it doesn't work.
<section ng-controller='otherCtrl'>
<button ng-click='showDiv()'>show</button>
</section>
Controller:
var otherCtrl = function($scope){
$scope.showDiv = function () {
$scope.visible = true;
};
}
In AngularJS, $scopes prototypically inherit from their parent scope, all the way up to $rootScope. In JavaScript, primitive types are overwritten when a child changes them. So when you set $scope.visible in one of your controllers, the property on $rootScope was never touched, but rather a new visible property was added to the current scope.
In AngularJS, model values on the scope should always "have a dot", meaning be objects instead of primitives.
However, you can also solve your case by injecting $rootScope:
var otherCtrl = function($scope, $rootScope){
$scope.showDiv = function () {
$rootScope.visible = true;
};
}
How familiar are you with the concept of $scope? It looks to me based on your code that you're maintaining two separate $scope variables called "visible" in two different scopes. Do each of your controllers have their own scopes? This is often the case, in which case you're actually editing different variables both named "visible" when you do a $scope.visible = true in different controllers.
If the visible is truly in the rootscope you can do $rootScope.visible instead of $scope.visible, but this is kind of messy.
One option is to have that "otherCtrl" code section in a directive (you should probably be doing this anyway), and then two-way-bind the directive scope to the parent scope, which you can read up on here. That way both the directive and the page controller are using the same scope object.
In order to better debug your $scope, try the Chrome plugin for Angular, called Batarang. This let's you actually traverse ALL of your scopes and see the Model laid out for you, rather than just hoping you're looking in the right place.

Resources