I have the following code in a large Angular project:
$stateProvider.state('my-app', {
url : '/',
views: {
'content#': {
templateUrl: '/?page=/home',
controller: 'HomeController'
}
}
});
I know I can remove the controller using delete element['views']['content#']['controller']; and init with element afterwards but how can I check if HomeController exists? After a day of researching. No working solution seems to exist in Angular.
You should not remove controllers. This should be done by angular.
But you could set some injected service variable on controller creation and clear it 'on destroy'. I.e. to do something like:
angular
.module('something')
.controller('HomeController', HomeController);
HomeController.$inject = ['$scope', 'myGlobalService'];
function HomeController($scope, myGlobalService) {
myGlobalService.isHomeControllerPresent = true;
$scope.$on("$destroy", function() {
myGlobalService.isHomeControllerPresent = undefined;
});
}
But it's more a hack than 'Angular style'.
If you have access to $scope and you know controller name (i.e. ng-controller="homeCtrl as HomeController" or controllerAs: 'homeCtrl') then you could just check:
if ($scope.homeCtrl) {
...
}
because any controller is a part of its scope - each controller is 'attached' to its scope by name.
But once again: no one should remove controllers except angular ;)
Related
Is is possible to use property injection in angularJS?
Scenario
I know this will work
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
resolve: {
'boolFlag': function(service){return service.getBoolean();}
}
});
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function(boolFlag) {
this.boolFlag = boolFlag;
this.execute = function(){...};
});
<div ng-show="vm.boolFalg">
simple dynamic content
</div>
<button type="button" ng-click="vm.execute()">Click Me</button>
But it feels leaky. boolFlag` is only used in the view to show/hide content. Is there I way I can resolve the controller and set the boolFlag property on the controller instance? I assume providers or factories would be the way to go, but I'm going around in circles trying to make sense of it.
I envision it would look something like
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
});
})
.provider('ctrlProvider', function(ctrlProvider, service) {
var ctrl = ctrlProvider.$get/invoke/build/create();
ctrl.boolFlag = service.getBoolean();
return ctrl;
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function() {
this.execute = function(){...};
});
I could also be going about this the wrong way. Many controllers will need the boolFlag property. Maybe it should be part of a $parentScope? But I don't know how to code that.
Update
I was thinking about this more last night. The boolFlag doesn't need to be associated to the controller at all. It only needs to be part of the scope. $scope.boolFlag = service.getBoolean();
The question then becomes, how can I populate $scope without the controller?
you could use factory to maintain the value of boolFlag so that it is shared between the controllers. It is better to avoid using $parentScope. This explains how to proceed with that. If you need to set the value of factory initially you could also use it in app.config of your main module to set its value.
I'm starting using angularjs for my new websites, so I'm a beginner.
I have a problem, which I can't change the value from my $scope inside my controller after the view was called.
I'm using ui-router to multiple views.
I explain in this example:
<html lang="pt" ng-app="myApp">{...}
<header ng-controller="siteHeader"><div ng-show="mySlogan"></div></header><div ui-view="content"></div>
In my js:
var app = angular.module('myApp', ['ui.router']); app.config(['$locationProvider','$stateProvider','$urlRouterProvider'
,function($locationProvider,$stateProvider,$urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home',{
url: '/',
views: {
'content':{
templateUrl: 'templates/main.html',
controller: 'siteHeader'
}
}
})
.state('login',{
url: '/login',
views: {
'content':{
templateUrl: 'templates/login.html',
controller: 'siteHeader'
}
}
});}]);
app.controller('siteHeader',['$scope','$location',function($scope, $location){
if($location.path()=="/")
$scope.mySlogan = true;
else
$scope.mySlogan = false;}]);
The controller works at first time as page is load, but when the path was changed my $scope.mySlogan does nothing.
What I'm doing wrong?
Use the $state service instead of $location and get the URL as following :
$state.url
Then, try to create a dedicated controller for each route. This will avoid using extensively conditions just to match specific route's template requirements.
The code is only executed once in your controller assigned to <header>. The controllers assigned to your views are just other instances of siteHeader controller and define a new mySlogan variable in their own scope. So, changing the value there won't affect the value in the header scope.
To solve your problem, you could watch the location path:
$scope.$watch(
function() {
return $location.path();
}, function (newValue) {
if (newValue === '/')
$scope.mySlogan = true;
else
$scope.mySlogan = false;
});
Or a better way would be to listen to the ui-router events:
app.controller('siteHeader',['$scope',
function($scope){
$scope.$on('$stateChangeSuccess',
function(event, toState) {
if ( toState.includes('home') )
$scope.mySlogan = true;
else
$scope.mySlogan = false;
});
}]);
You need to assign the controller only to your sideHeader and not to your actual views.
I have see people post things online about using Angular ui-router and i see these types are code that do not explicitly even show an controller name, so how is it even suppose to know how to call the controller?
state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
Code of mine that I TRIED that does NOT list the controller like above
.state("deviceDetail", {
url: "/devices/:DeviceId", // param is required which specific device id
templateUrl: "app/devices/deviceDetailView.html", // ui elements
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
});
Problem is the controller is NOT hit
But this code explicitly uses the controller name and the controller gets hit, thus i'm having trouble with how this type of code would hit a controller
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
HOW does it know? (doesn't work for me )
You can declare controllers in two ways in UI router states:
//1
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
//2
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: PortfolioController
})
.
.
.
//PortfolioController definition
In method #1, your controller is created for you inline, while in #2, UI Router is looking for an explicitly declared controller called PortfolioController
I need to setup a controller with some data passed from the routeProvider.
I would like the value topic to be passed to my controller VideoCtrl and then assigned to a value such as video.topic
$routeProvider
.when('/video/:topic', {
templateUrl: 'pages/video/video-page.tmpl.html',
controller: 'VideoCtrl',
controllerAs: 'video'
});
It seems like I should just need to call $routeProvider in my controller function:
app.controller('VideoCtrl', function($routeProvider) {
var video = this;
video.topic = $routeProvider.topic; // What do I need to add here?
...
});
But I can't seem to figure out how to inject $routeProvider into the controller.
I think, you need to pass the $routeParams as controller parameter and then you will able to get the passed route value.
app.controller('VideoCtrl', function($routeParams) {
var video = this;
video.topic = $routeParams.topic;
...
});
I have multiple clients on my angular app and I want to create different themes inside angular (only the visual part will change, controllers remain the same.
I have a "security" module which manages the authentication, currentLoggedIn user and so on.
var security = angular.module('security', ['ui.router'])
// .factory('authService', authService);
.service('authService', ['$http', '$q', '$window', 'CONSTANTS', '$location', 'currentUser', '$state', '$rootScope', authService])
.factory('authCheck', ['$rootScope', '$state', 'authService', securityAuthorization])
and authService is basically having these methods/values
service.login = _login;
service.logout = _logout;
service.reset = _reset;
service.isAuthenticated = _isAuthenticated;
service.requestCurrentUser = _requestCurrentUser;
service.returnCurrentUser = _returnCurrentUser;
service.hasRoleAccess = _hasRoleAccess;
How can I get access to currentUser inside templateURL function to modify the URL based on data for currentUser?
AuthService and AuthCheck are empty when accessed in templateURL function.
$stateProvider
.state('home', {
url: '/home',
templateUrl: function(authService, authCheck) {
console.log (authService, authCheck);
return 'components/home/home.html'
},
data: {
roles: ['Admin']
},
resolve: {
"authorize": ['authCheck', function(authCheck) {
return authCheck.authorize();
}],
"loadedData": ['metricsFactory', 'campaignFactory', '$q', '$rootScope', 'selectedDates', loadHomeController]
},
controller: 'HomeController',
controllerAs: 'home'
});
In case, we want to do some "magic" before returning the template... we should use templateProvider. Check this Q & A:
Trying to Dynamically set a templateUrl in controller based on constant
Because template:... could be either string or function like this (check the doc:)
$stateProvider
template
html template as a string or a function that returns an html template
as a string which should be used by the uiView directives. This
property takes precedence over templateUrl.
If template is a function, it will be called with the following
parameters:
{array.} - state parameters extracted from the current
$location.path() by applying the current state
template: function(params) {
return "<h1>generated template</h1>"; }
While with templateProvider we can get anything injected e.g. the great improvement in angular $templateRequest. Check this answer and its plunker
templateProvider: function(CONFIG, $templateRequest) {
console.log('in templateUrl ' + CONFIG.codeCampType);
var templateName = 'index5templateB.html';
if (CONFIG.codeCampType === "svcc") {
templateName = 'index5templateA.html';
}
return $templateRequest(templateName);
},
From the documentation:
templateUrl (optional)
path or function that returns a path to an html template that should be used by uiView.
If templateUrl is a function, it will be called with the following parameters:
{array.<object>} - state parameters extracted from the current $location.path() by applying the current state
So, clearly, you can't inject services to the templateUrl function.
But right after, the documentation also says:
templateProvider (optional)
function
Provider function that returns HTML content string.
templateProvider:
function(MyTemplateService, params) {
return MyTemplateService.getTemplate(params.pageId);
}
Which allows doing what you want.