How to access parent property using Controller As notation - angularjs

I'm using the Controller as in my view as follows:
<body ng-controller="MainCtrl as main">
<div ng-controller="ChildCtrl as child">
{{ main.parentValue }} + {{ child.childValue }}
</div>
</body>
Defining my controller like this:
app.controller('MainCtrl', function($scope) {
this.parentValue = 'Main';
});
app.controller('ChildCtrl', function($scope) {
this.childValue = 'Child';
// I want to access the property of the parent controller here
});
How can the ChildCtrl set the name property of the MainCtrl? Here the Plunkr.
Using the $scope notation, I could have accessed $scope.parentValue from the child controller. How can the same functionality be achieved using the Controller As notation?

Since your using the "controller as" notation, witin your ChildCtrl you can access the MainCtrl using $scope.main, for example $scope.main.name.
See my snippet below.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
this.name = 'Main';
this.test = {};
});
app.controller('ChildCtrl', function($scope) {
this.name = 'Child';
alert($scope.main.name);
});
<html ng-app="app">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-controller="MainCtrl as main">
<div ng-controller="ChildCtrl as child">
{{ main.name }} + {{ child.name }}
</div>
</body>
</html>

You should not mix up "controller as" and $scope usage. To update data in a parent-scope you could/should use services.
Example: Changing the page-title from within any Child-Controller:
app.service("SiteService", function () {
return {
title: "Page title";
}
}
app.controller ("ParentCtrl", function (SiteService) {
this.site = SiteService;
}
app.controller ("ChildCtrl", function (SiteService) {
SiteService.title = "New Title";
}
And your template
<html ng-app="someApp" ng-controller="ParentCtrl as site">
<head>
<title>{{site.title}}</title>
</head>
</html>
The main advantage of this approach: You separate public mutable from private properties.

Putting the data in $scope is the angular way to do this. You could also set/get your data from a service which is then easy to include into either controller.
Check out this video: https://egghead.io/lessons/angularjs-sharing-data-between-controllers

Related

Async variable stored in $rootScope not available in other controllers

I am using Angular 1.x for my stack and when I make an API call and store the response in the $rootScope, it is not accessible in other controllers' view.
My controller:
angularApp.controller('mainController', ['$scope', '$rootScope', '$http', function($scope, $rootScope, $http){
var checkIfAuthenticated = function(){
return $http.get('/api/isAuthenticated');
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
}]);
Now, in another controller's view I use it like this:
<div ng-if="userLoggedIn" class="py-1 pl-1 pr-1">
<span><b>Message Board</b></span>
<div class="form-control" readonly id="lockTextbox">Show something</div>
</div>
The problem is, the API call /api/isAuthenticated does provide the right response (status 200) but the ng-view gets it wrong.
I am sure this is to do with $rootScope.userLoggedIn being a response from an async call (as I get Success in my console) but how do I solve it?
Thanks in advance!
EDIT
After I posted the question, I noticed that in the mainController's view, the ng-if logic doesn't work either. What is very strange is that when I open up the browser's debug console, it starts working! I think this is just an async issue but I don't know how to solve it. How do I tell the view that the variable is on it's way?
OK, to solve the timing issue, I'll rework the answer completely. This should be a - quick and dirty but - working example:
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="plunker" ng-cloak>
<div ng-controller="MainCtrl as $ctrl">
<h1>Hello {{$ctrl.name}}</h1>
<p>Start editing and see your changes reflected here!</p>
<div ng-if="$ctrl.name === 'Angular.js'"><b>Message Board</b</div>
</div>
</body>
</html>
script.js
import angular from 'angular';
angular.module('plunker', []).controller('MainCtrl', function($scope, $http) {
const self = this;
self.name = 'Plunker';
console.log('hello');
function checkIfAuthenticated(){
console.log('get');
return $http.get('https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js');
};
checkIfAuthenticated().then(function(res) {
if(res.status===200){
console.log('Success');
self.name = 'Angular.js'; // insert any logic here
} else {
console.log('Failed');
}
})
});
Console
hello
get
Success
Does it work for you?
Working Example
The below demo shows the $rootScope variable available in both controllers after being set from a promise delayed by two seconds.
angular.module("app",[])
.controller('mainController', function($scope, $rootScope, $timeout) {
var checkIfAuthenticated = function(){
return $timeout(function() {
return { status: 200 };
}, 2000);
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
})
.controller('otherController', function($scope) {
console.log("other controller");
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<fieldset ng-controller="mainController">
MAIN Controller userLoggedIn={{userLoggedIn}}<br>
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
<fieldset ng-controller="otherController">
OTHER Controller userLoggedIn={{userLoggedIn}}
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
</body>
I found the problem. I updated my Angular from 1.6.4 to 1.7.9 (and all modules like angular-sanitize etc) and that did the trick. Should have been the first thing I did but I missed it entirely

AngularJS: How to escape JSON key that has operator in it?

This is an AngularJS syntax question.
{ "key-part": "value-part" } is a valid JSON object. However, in AngularJS, {{ x.key-part }} is an expression. It does not return "value-part" but returns 0. Any idea how to escape the '-'?
Yes, you may ask why use "key-part" instead of "key_part". Well, I have no control over that.
Thanks for any help.
You can access attributes of an object like a standard Javascript object : https://www.w3schools.com/js/js_properties.asp
So <span>{{myObject['attr']}}</span> work
"use strict";
angular.module("demo", [])
.controller("MainController", MainController);
function MainController() {
var vm = this;
vm['da-ta'] = "data";
};
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo" ng-controller="MainController as main">
<div ng-bind="main['da-ta']"></div>
</div>
Try {{ x["key-part"] }} instead of {{ x.key-part }}.
Working Demo
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.x = { "key-part": "value-part" };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
{{ x["key-part"] }}
</div>

Angular 1.5 Pass data from component to controller

I have component inside of the controller and i'm binding the data to it. How to make changes in component visible in the controller.
I have that code:
JS
app.controller('TheCtrl', function($scope) {
$scope.changeable = 'earlier';
});
app.component('innerComponent', {
bindings: {
changeable: '='
},
controller: function() {
this.changeable = 'later';
}
}
HTML
<div ng-controller="TheCtrl">
<inner-component changeable="val"></inner-component>
<p>
{{changeable}}
</p>
</div>
And it doesn't change the 'changeable' value in the view of the controller (it show "earlier" value). Why? How to make the changes visible in the controller?
It does work as expected.
function appCtrl() {
this.value = "test";
}
var inner = {
bindings: {
changeable: '='
},
controller: function() {
this.changeable = 'later';
}
};
angular.module('app', []);
angular.module('app')
.controller('appCtrl', appCtrl)
.component('inner', inner);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="appCtrl as ctrl">
<inner changeable="ctrl.value"></inner>
<p>
{{ctrl.value}}
</p>
</div>
</div>
However you shouldn't use components for building MVW apps.
You either go all the way and create a component root with children components or use "controlled divs".
But hybrid MVC/components apps are bad architecture.

Angular directive controller scope visibility

Question
Why isn't monkey and selected visible to the template?
Plunk
http://plnkr.co/edit/djS0KWyfJNKD0tfZ0IiV?p=preview
Code
<head>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.3/angular.js"></script>
<script type="text/javascript">
angular
.module('toruSelect', [])
.directive('toruSelect', function () {
return {
restrict: 'AE', // Allow usage as A - attribute, E - element
scope: { // Isolated scope
selected: '=' // Bi-directional binding to selected attribute,
},
controller: ['$scope', function ($scope) {
$scope.monkey = 'MONKEY';
console.log('toruSelect.controller.$scope', $scope);
}]
}
});
var app = angular.module('app', ['toruSelect']);
app.controller('AppCtrl', function($scope) {
$scope.val = 'initial';
$scope.appData = 'App data';
});
</script>
</head>
<body ng-controller="AppCtrl">
<h1>Directives and scopes..</h1>
<div toru-select selected="val">
<div style="color: red">RESULT: toruSelect.controller.monkey: {{monkey}}</div>
<div>EXPECTED: toruSelect.controller.monkey: MONKEY</div>
<div style="color: red">RESULT: toruSelect.controller.selected: {{selected}}</div>
<div>EXPECTED: toruSelect.controller.selected: initial</div>
</div>
</body>
Result
Directives and scopes..
RESULT: toruSelect.controller.monkey:
EXPECTED: toruSelect.controller.monkey: MONKEY
RESULT: toruSelect.controller.selected:
EXPECTED: toruSelect.controller.selected: initial
As you pointed it out on the comment of your directive, it has an isolated scope, so that value attached with monkey key is available on directive scope, not on controller one.
For selected, you have to display {{val}} and not {{selected}} as it's the variable concerned by the bi-directional binding on the directive scope.

can i inherit a parent controller's variables?

Here is my code:
function ParentCtrl($scope) {
$scope.people = ["Tom", "Dick", "Harry"];
$scope.count = $scope.people.length;
}
function ChildCtrl($scope) {
$scope.parentpeople = ParentCtrl.people //this is what I would like to do ideally
}
I am nesting one angular controller inside of another one. I would like to pass variables of the first controller to the second one. Does anyone know how to do this?
NOTE
I cannot do something like
ChildCtrl.prototype = new ParentCtrl();
because I will overwrite the people property of the ChildCtrl.
By default, child scopes prototypically inherit from the parent scope (see Scope), so you already have access to the parent controller's $scope properties in the child. To prove it:
function ChildCtrl($scope) {
alert($scope.people)
}
You're getting things wrong. You are mixing controller inheritance with scope inheritance, and they are different and loosly coupled in AngularJS.
What you actually want is:
function ParentCtrl($scope) {
$scope.people = ["Tom", "Dick", "Harry"];
$scope.count = $scope.people.length;
}
function ChildCtrl($scope) {
$scope.parentpeople = $scope.$parent.people;
}
And it will work for the case:
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
</div>
</div>
But as Mark and ganaraj noticed above, this has no sense, as you can access your property of $scope.people from both
ParentCtrl and ChildCtrl.
If you want to inherit controllers from each other, you need to use prototype inheritance of controller functions themselves.
The $scope inheritance is based upon where you reference your controllers using ng-controller.
If you have something like
<div ng-controller="ParentController">
<div ng-controller="ChildController">
</div>
</div>
Then yes, the child controller will inherit the properties of the parent controller.
Note : The child controller need not be defined on the direct child in the html. It can be any child within.
Also you can get the Scope of any controller by DOM:
$needleScope = angular.element(aDomElement).scope()
Using jQuery:
$needleScope = $('#aDomElementId').scope()
Or get all Scope in the document:
$allScopes = $('.ng-scope').scope()
It might help you!!!
Scope is a special JavaScript object that connects controller with views. Scope contains model data. In controllers, model data is accessed via $scope object.
<script>
var mainApp = angular.module("mainApp", []);
mainApp.controller("shapeController", function($scope) {
$scope.message = "In shape controller";
$scope.type = "Shape";
});
</script>
Scope Inheritance
Scope is controller-specific. If we define nested controllers, then the child controller inherits the scope of its parent controller.
<script>
var mainApp = angular.module("mainApp", []);
mainApp.controller("shapeController", function($scope) {
$scope.message = "In shape controller";
$scope.type = "Shape";
});
mainApp.controller("circleController", function($scope) {
$scope.message = "In circle controller";
});
</script>
Live example as give below.
<html>
<head>
<title>Angular JS Forms</title>
</head>
<body>
<h2>AngularJS Sample Application</h2>
<div ng-app="mainApp" ng-controller="shapeController">
<p>{{message}} <br/> {{type}} </p>
<div ng-controller="circleController">
<p>{{message}} <br/> {{type}} </p>
</div>
<div ng-controller="squareController">
<p>{{message}} <br/> {{type}} </p>
</div>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script>
var mainApp = angular.module("mainApp", []);
mainApp.controller("shapeController", function($scope) {
$scope.message = "In shape controller";
$scope.type = "Shape";
});
mainApp.controller("circleController", function($scope) {
$scope.message = "In circle controller";
});
mainApp.controller("squareController", function($scope) {
$scope.message = "In square controller";
$scope.type = "Square";
});
</script>
</body>
</html>

Resources