I'm using ngRoute to create an Angular single-page app. Want to move to a component-based version.
Problem is isolated scopes. I need access to main controller props and methods. Trying to use bindings but does not work. I cannot find the problem with this one. This app works fine without using components. When I try to change the homepage view into a component it crashes. These are the main parts of the code:
framework
<html ng-app="angularModule" >
<body ng-controller="angularController as angCtrl" >
<div ng-show="angCtrl.user.isLoggedIn" >Sign Out</div>
<div ng-hide="angCtrl.user.isLoggedIn" cd-visible="angCtrl.showSignIn">Sign In</div>
<div id="contentLayer" class="contentLayer" ng-view ></div>
homepage template
<h1 class="pageLabel" >HomePage</h1>
<blockquote>This can be anything. No bindings.</blockquote>
angularController
var app = angular.module ('angularModule', ['ngRoute'] );
app.directive ('cdVisible',
function () {
return function (scope, element, attr) {
scope.$watch (attr.cdVisible,
function (visible) {
element.css ('visibility', visible ? 'visible' : 'hidden');
}
);
};
}
);
app.config ( [ '$locationProvider', '$routeProvider',
function config ($locationProvider, $routeProvider) {
$locationProvider.hashPrefix ('!');
$routeProvider
.when ('/sign-in', {
templateUrl: '/ng-sign-in',
controller: signInController
})
... more routes
.when ('/home', {
template: '<home-page showSignIn="angCtrl.showSignIn" menuSelect="angCtrl.menuSelect" ></home-page>'
})
.otherwise ('/home');
}
]);
function homePageController () {
this.menuSelect ('Devices'); // this statement has no effect on angularController.menuSelection chrome shows it as an anonymous function
this.showSignIn = false; // this bombs: Expression 'undefined' in attribute 'showSignIn' used with directive 'homepage' is non-assignable!
}
app.component ('homePage', {
templateUrl: '/ng-homepage',
controller: homePageController,
bindings: {
menuSelect: '&',
showSignIn: '='
}
});
app.controller ('angularController', [ '$http', '$window', '$location',
function ($http, $window, $location) {
var self = this;
this.user = {
"isLoggedIn": false
};
this.showSignIn = true;
this.menuSelection = "";
this.errorMessage = "";
this.menuSelect =
function (selection) {
self.menuSelection = selection;
};
this.setUserData =
function (userData) {
self.user = userData;
};
this.setShowSignIn =
function (show) {
self.showSignIn = show;
};
this.menuSelect ('');
this.getUserData(); // I removed this for this post
}
]);
I added a comment to the spot where it throws an exception. The homepage controller attempts to update the model of the angularController. The first does nothing the second throws an exception. What am I doing wrong?
First of all showSignIn
is a primitive, therefore angular will handle it the exact same way as doing showSignIn="7+2"
. If you need to modify that value inside the component then you should use an object with the showSignIn
property.
Now menuSelect
is a little tougher, probably Chrome console is showing something like
function (locals) {
return parentGet(scope, locals);
}
This is because you're just passing the reference to angCtrl.menuSelect
to the component.
In order to execute the menuSelect
function from inside homePageController
you'd have to do (in the HTML):
<home-page menu-select="angCtrl.menuSelect(myMsg)"></home-page>
And then call it like this in the component's controller:
this.menuSelect({ myMsg:"Devices" })
The (myMsg)
in the HTML is a call to angular to return this reference, then in the execution we pass the parameter { myMsg:"Devices" }
to match the parameter in the reference we just did.
You can check this answer that explains it way more detailed.