I'm trying to get rootScope value in function from routeChangeSuccess in AngularJs as per following code. But I cannot convince why rootScope value is NULL when I output with console.log.
$rootScope.updateCurrentUser = function () {
$rootScope.loggedUser = "1";
};
$rootScope.$on('$routeChangeSuccess', function () {
var path = $location.path();
$rootScope.showIndex =
path === LANG_PATH + '/';
if ($rootScope.showIndex) {
//undefined result found :(
console.log('rootScope ' + $rootScope.loggedUser);
}
});
$rootScope.updateCurrentUser();
It's hard to tell since your example doesn't show where the handler is attached and where the change to $rootScope.loggedUser is made, but if it's happening in a controller, you probably won't the event.
Controllers don't get created until after the event has been broadcast, and the controller of the current view will be destroyed before the next $routeChangeSuccess event is broadcast, which means you'll miss the event if you're deregistering handlers in your controller's $destroy event.
In the snippet below, you'll see the event being handled (and $rootScope.eventLog updating) in .run as you change routes, but not in the handlers in the controllers.
angular.module('app', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/main', {
templateUrl: 'main.html',
controller: 'MainCtrl'
})
.when('/other', {
templateUrl: 'other.html',
controller: 'OtherCtrl'
})
.otherwise('/main');
}])
.run(['$rootScope', function($rootScope) {
$rootScope.eventLog = [];
$rootScope.$on('$routeChangeSuccess', function(e, curr, prev) {
// we should see this, and $rootScope.eventLog will get updated
$rootScope.eventLog.push({
handler: "Run",
event: e,
curr: curr,
prev: prev
});
});
}])
.controller('MainCtrl', ['$scope', '$rootScope',
function($scope, $rootScope) {
var deregister = $rootScope.$on('$routeChangeSuccess', function() {
// this won't happen: we'll deregister before the event is called
$rootScope.eventLog.push({
handler: "Main",
event: e,
curr: curr,
prev: prev
});
});
$scope.$on('$destroy', function() {
deregister();
});
}])
.controller('OtherCtrl', ['$scope', '$rootScope',
function($scope, $rootScope) {
var deregister = $rootScope.$on('$routeChangeSuccess', function() {
// this won't happen: we'll deregister before the event is called
$rootScope.eventLog.push({
handler: "Other",
event: e,
curr: curr,
prev: prev
});
});
$scope.$on('$destroy', function() {
deregister();
});
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>
<div ng-app="app">
<script type="text/ng-template" id="main.html">
<p>This is the content of the main template</p>
Other
</script>
<script type="text/ng-template" id="other.html">
<p>This is the content of the other template</p>
Main
</script>
<ul>
<li ng-repeat="e in eventLog track by $index">
<h3>{{e.handler}}</h3>
<pre>{{e | json:2}}</pre>
</li>
</ul>
<ng-view></ng-view>
</div>
Related
I want to make a sidebar with list item that can be dynamically changed based on the settings page.
My app request settings.json via factory() and then called it in a controller. The controller will be used by settings.html (ngView) and sidebar.html (ngInclude).
The json will return Boolean value that also can be changed on setting page that contain checkbox which return true if check and false if not checked. I use ngShow on the sidebar to display/hide the list items.
How can I made the sidebar to reflect the changes as I tick the checkbox?
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
return settingsFactory;
}]);
controller
var settingsControllers = angular.module('settingsControllers', ['settingsFactory']);
settingsControllers.controller('SettingsFilterController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
$scope.settings;
$scope.status;
getSettings();
function getSettings() {
SettingsFilterFactory.getSettings()
.then(function (response) {
$scope.settings = response.data;
}, function (error) {
$scope.status = 'Unable to load: ' + error.message;
});
}
}]);
app.js
var app = angular.module('app', ['ngRoute', 'settingsControllers']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/settings', {
title: 'Settings',
templateUrl: 'app/components/settings/settings.html',
controller: 'SettingsFilterController'
}).
otherwise({
redirectTo: '/home'
});
}]);
My index.html is something like this:
...
<body>
<section class="sidebar">
<div ng-include="'app/components/sidebar/sidebar.html'"></div>
</section>
<section class="content">
<div ng-view></div>
</section>
</body>
...
sidebar.html
<ul class="sidebar-menu" ng-controller="SettingsFilterController">
<li ng-show"settings.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settings.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Try something like this (untested):
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
settingsFactory.hiddenMenu= true;
settingsFactory.someOtherSetting = {};
return settingsFactory;
}]);
sidebar controller
settingsControllers.controller('SidebarController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
//do this in each controller, so that the factory becomes a property of $scope and can be seen in the HTML
$scope.settingsFactory = SettingsFilterFactory;
}
sidebar.html
<ul class="sidebar-menu" ng-controller="SidebarController">
<li ng-show"settingsFactory.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settingsFactory.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Essentially, you are binding the settingsFactory object which is a singleton to each $scope that is provided by each controller. Each controller is able to change the property on the factory object, which is then visible in all other controllers that have injected this object.
I'm trying to open a modal using angular material and inject the scope of the parent, where my data is.
I am attempting to loosely follow the custom/advanced example here:
https://material.angularjs.org/latest/demo/dialog
I do not need the dialog to do anything once closed, so I've eliminated the Dialogcontroller, which seems to only handle 'cancel' and 'answer' upon close. (correct me if I'm wrong here)
The ultimate goal here is to have access to survey in the modal, which is an object in the scope of the parent.
My parent HTML has the review button:
<input type="button" value="Review" ng-click="vm.openReview($event)" />
which triggers the function:
(function () {
appModule
// parent controller
.controller('tenant.views.surveys.index', [
'$scope', 'abp.services.app.survey', '$filter', '$mdDialog',
function ($scope, surveyService, $filter, $mdDialog) {
var vm = this;
vm.openReview = function (ev) {
$mdDialog.show({
//controller: DialogController,// not needed
templateUrl: "/App/tenant/views/surveys/review_modal.html",
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: true
})
//.then(function (answer) {// not needed
// vm.modalStatus = 'You said the information was "' + answer + '".';
//}, function () {
// vm.modalStatus = 'You cancelled the dialog.';
//});
};
// not needed
//function DialogController($scope, $mdDialog) {
// $scope.hide = function() { $mdDialog.hide(); };
// $scope.cancel = function() { $mdDialog.cancel(); };
// $scope.answer = function(answer) { $mdDialog.hide(answer); };
//}
}
])
.
// then the modal controller
.controller('tenant.views.surveys.review', [
'$scope', 'abp.services.app.survey',
function ($scope, surveyService) {
var vmModal = this;
}
]);
})();
Now, in my modal, I have some content:
<div ng-controller="tenant.views.surveys.review as vmModal" class="md-padding dialogdemoBasicUsage" id="popupContainer" ng-cloak="">
<div ng-controller="tenant.views.surveys.review" class="md-padding dialogdemoBasicUsage" id="popupContainer" ng-cloak="">
<md-dialog aria-label="Mango (Fruit)">
<form ng-cloak>
{{vmModal.survey}}
</form>
</md-dialog>
</div>
But I think I'm missing a piece. I don't think vmModal actually contains the scope of the parent. I want access to vmModal.survey.
Do I need to / can I inject the scope directly into the modal controller?
I have HTML code:
<div ng-controller="ProfileLeftMenu">
<li ng-class="{'active':selectedTab == 'personal'}" ng-click="selectedTab = 'personal'" class="">Personal
</li>
</div>
And controller:
$scope.selectedTab = 'first';
if ($routeParams.page) {
ajax.get(page, function (CbData) {
$scope.selectedTab = page;
});
}
So, if do:
{{selectedTab}}
in template HTML get always: first
You need to update your $scope variable with the new $routeParams just after the change in route. For that you can listen for the$routeChangeSuccess event. Try this:
DEMO
app.js
var app = angular.module('plunker', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/test/:page', {
templateUrl: function(params) {
return 'pidat.html';
},
controller: 'MainCtrl'
});
}
]);
app.controller('MainCtrl', ['$scope', '$http', '$routeParams', function($scope, $http, $routeParams) {
// when controller is loaded params are empty
console.log('on controller load $routeParams', $routeParams);
$scope.name = 'World';
// only after you have transitioned to the new
// route will your $routeParams change so we
// need to listen for $routeChangeSuccess
$scope.$on('$routeChangeSuccess', function(){
console.log('on $routeChangeSuccess load $routeParams', $routeParams);
if ($routeParams.page) {
$scope.name = $routeParams.page;
}
});
}]);
So for your original example you would probably have to do something like this:
$scope.selectedTab = 'first';
$scope.$on('$routeChangeSuccess', function(){
if ($routeParams.page) {
ajax.get(page, function (CbData) {
$scope.selectedTab = page;
});
}
});
Use the angular $http service ($http.get()), not ajax.get(). Otherwise, Angular isn't aware of the change you make to the scope once the HTTP response comes and the callback is executed, unless you call $scope.$apply().
I have a modal which I call from a function, when I call the modal I want to pass a URL to it like so:
ng-click="showModal('abc','testUrl')"
I want to share testUrl between controllers so I'm trying this:
hmwsApp.controller('mainController', ['$scope', '$http', '$rootScope', 'ModalService', function($scope, $http, $rootScope, ModalService) {
$scope.showModal = function(modalUrl, imageUrl) {
$rootScope.$broadcast('imageUrl', imageUrl); // <--- set global scope var
ModalService.showModal({
templateUrl: '/views/modals/' + modalUrl + '.html',
controller: "ModalController"
})
.then(function(modal) {
modal.element.modal();
modal.close.then(function(result) {
$scope.message = "Image show " + result;
});
});
};
}]);
Then in modal controller:
hmwsApp.controller('ModalController', function($scope, close) {
$scope.$on('imageUrl', function(response) {
$scope.imageUrl = response; // <--- read global scope var
})
$scope.close = function(result) {
close(result, 500); // close, but give 500ms for bootstrap to animate
};
});
The reason I'm doing this is I want to reference the URL in the modal like so:
<div class="modal-content">
<img src="{{ imageUrl }}">
</div>
But it simply doesn't work, the img src is empty, so I guess I'm missing something but not sure what? Any ideas?
UPDATE
Also tried the following but still nothing:
$scope.$on('imageUrl', function(events, args){
console.log(args); // <--- empty
$scope.imageUrl = args;
});
From what I can see, your ModalController isn't instantiated when you do the $rootScope.$broadcast call. You should do it AFTER you've shown the modal, otherwise it won't be "in existence" to intercept it with $scope.$on
I am trying to share informations between two controllers with $scope.$on and $scope.$broadcast.
Here is the view:
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
{{content}}
</div>
</div>
and the controllers:
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$broadcast('someEvent', 'bidule');
}])
.controller('ChildCtrl', ['$scope', function($scope) {
$scope.$on('someEvent', function(event, b) {
$scope.content = b;
});
}])
and the plunker
What am I doing wrong here?
The child controller isn't instantiated yet. Wrapping the broadcast in a timeout works:
http://plnkr.co/edit/MF7P16K1OTv46JNgkkih?p=preview
$timeout(function(){
$scope.$broadcast('someEvent', 'bidule');
});
Child not initiated. Faced the same issue and this was my solution.
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$on('initiateEvent', function(event, b) {
$scope.$broadcast('someEvent', 'bidule');
}
}])
.controller('ChildCtrl', ['$scope', function($scope) {
$scope.$on('someEvent', function(event, b) {
$scope.content = b;
});
$scope.$emit('initiateEvent', null);
}])
Here i'm initiating the child and asking the parent to broadcast someEvent
To broadcast an event, you simply have to call it.
The following code works perfectly :
Your view :
<div ng-controller="ParentCtrl" ng-click="myFunc()">
<div ng-controller="ChildCtrl">
{{content}}
</div>
</div>
and your controllers :
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.myFunc = function() {
$scope.$broadcast('someEvent', 'bidule');
}
}])
.controller('ChildCtrl', ['$scope', function($scope) {
$scope.$on('someEvent', function(event, b) {
$scope.content = b;
});
}])
Hope it helps !