I need current path from url in template (content of $location.path). But not via controller, because I have a lot of controllers (and I do not want to duplicate declaration of $scope.currentUrl = $location.path;). Thanks for the advice.
AngularJS template can only see what is available in a scope so you will need somehow to put $location service in a scope. There is one scope that is always available in AngularJS application called $rootScope so it could be use for your use-case.
What you could do is to use run() method of a module to expose $location in the $rootScope:
var myApp = angular.module('myApp', []).run(function($rootScope, $location) {
$rootScope.location = $location;
});
this would make 'location' available in all templates so later on you could do in your template:
Current path: {{location.path()}}
An alternative is to use the more semantic and versatile ui-router, then in the controller, retrieve the current state, and store it on the $scope:
app.controller('MyCtrl', ['$scope', '$state', function MyCtrl($scope, $state) {
$scope.state = $state.current.name;
...
}
Related
I got problem, I'm trying to get controller instance in my service:
myAppModule.service('modalService',
function ($modal, $controller) {
var controller = $controller('ExperienceDetailsModalCtrl');
});
but I've got error:
TypeError: Cannot read property '$scope' of undefined
Is it possible to access controller (defined in another file) and pass it to modal?
My controller:
myAppIndexModule
.controller('ExperienceDetailsModalCtrl', function ($scope) {
});
You can't access controller scope in service, factory or provider. The data which you wanted to share should be placed inside a service. & make it available to other controller.
I think you do want to pass controller scope to $modal then you can achieve this by doing from controller itself.
$modal.open({$scope: $controller('ExperienceDetailsModalCtrl', {$scope: $scope}), templateUrl: 'abc.html'})
Update
You could do it like below
myAppModule.service('modalService',
function ($modal, $controller, $rootScope) {
var scope = $rootScope.$new(true);
$controller('ExperienceDetailsModalCtrl',{scope: $scope });
//in scope now you will have ExperienceDetailsModalCtrl scope
});
Why this doesn't work ?
Since angular expression doesn't have access to window object, i've used $window, however the below doesn't work.
<button ng-click="$window.alert('Hi There')">Hi There</button>
Angular expressions do not have access to global variables like
window, document or location. This restriction is intentional. It
prevents accidental access to the global state – a common source of
subtle bugs.
A template only has access to variables that are put on its $scope. If you need to access anything on $window from your template you'll need to inject $window into your controller and assign it to $scope there.
For example
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.$window = $window;
}]);
As pointed out in the comments, you probably don't want to expose the entire $window wrapper to your template so a better approach is to use a helper function on $scope.
ng-click="greet('Hi There')"
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.greet = function(message) {
$window.alert(message);
};
}]);
You can only call services in your controllers through scope (this is the idea of separating non-UI logic from the template)
See How to call a service function in AngularJS ng-click (or ng-change, ...)?
$window is a service, and like other services that don't relate directly to the view, they are not accessible in the templates.
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.alert=$window.alert
}]);
My code has the following:
var app = angular.module('app', ['admin', 'home', 'questions', 'ui.compat', 'ngResource', 'LocalStorageModule']);
app.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$state.transitionTo('home');
}]);
Can someone please explain what the two lines starting with $rootScope are doing. Are they needed?
This comment is taken from example source code on projects github page:
It's very handy to add references to $state and $stateParams to the $rootScope
so that you can access them from any scope within your applications. For example,
<li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
to active whenever 'contacts.list' or one of its decendents is active.
You can check it out here.
I don't think this is standard code. The only reason i see these being added to you rootScope it to facilitate binding within the html view everywhere. Else if you want to bind to some property of either $state or $stateParams you would have to inject the $tate and $stateParams service into the controller. Ideally this should be injected into controllers that require this service.
If you remove it any html binding dependent on it would fail. Search html views for bindings with name $state and $stateParams and you would find where they are used.
its so you can pass $state and $stateParams values into your root Controller and be able to access them
example use:
ng-click="$state.transitionTo('stateName', $stateParams)"
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#note-about-using-state-within-a-template
I have this piece of layout html:
<body ng-controller="MainController">
<div id="terminal"></div>
<div ng-view></div>
<!-- including scripts -->
</body>
Now apparently, when I try to use $routeParams in MainController, it's always empty. It's important to note that MainController is supposed to be in effect in every possible route; therefore I'm not defining it in my app.js. I mean, I'm not defining it here:
$routeProvider.when("/view1", {
templateUrl: "partials/partial1.html"
controller: "MyCtrl1"
})
$routeProvider.when("/view2", {
templateUrl: "partials/partial2.html"
controller: "MyCtrl2"
})
// I'm not defining MainController here!!
In fact, I think my problem is perfectly the same as this one: https://groups.google.com/forum/#!topic/angular/ib2wHQozeNE
However, I still don't get how to get route parameters in my main controller...
EDIT:
What I meant was that I'm not associating my MainController with any specific route. It's defined; and it's the parent controller of all other controllers. What I'm trying to know is that when you go to a URL like /whatever, which is matched by a route like /:whatever, why is it that only the sub-controller is able to access the route parameter, whereas the main controller is not? How do I get the :whatever route parameter in my main controller?
The $routeParams service is populated asynchronously. This means it will typically appear empty when first used in a controller.
To be notified when $routeParams has been populated, subscribe to the $routeChangeSuccess event on the $scope. (If you're in a component that doesn't have access to a child $scope, e.g., a service or a factory, you can inject and use $rootScope instead.)
module.controller('FooCtrl', function($scope, $routeParams) {
$scope.$on('$routeChangeSuccess', function() {
// $routeParams should be populated here
});
);
Controllers used by a route, or within a template included by a route, will have immediate access to the fully-populated $routeParams because ng-view waits for the $routeChangeSuccess event before continuing. (It has to wait, since it needs the route information in order to decide which template/controller to even load.)
If you know your controller will be used inside of ng-view, you won't need to wait for the routing event. If you know your controller will not, you will. If you're not sure, you'll have to explicitly allow for both possibilities. Subscribing to $routeChangeSuccess will not be enough; you will only see the event if $routeParams wasn't already populated:
module.controller('FooCtrl', function($scope, $routeParams) {
// $routeParams will already be populated
// here if this controller is used within ng-view
$scope.$on('$routeChangeSuccess', function() {
// $routeParams will be populated here if
// this controller is used outside ng-view
});
);
As an alternate to the $timeout that plong0 mentioned...
You can also inject the $route service which will show your params immediately.
angular.module('MyModule')
.controller('MainCtrl', function ($scope, $route) {
console.log('routeParams:'+JSON.stringify($route.current.params));
});
I have the same problem.
What I discovered is that, $routeParams take some time to load in the Main Controller, it probably initiate the Main Controller first and then set $routeParams at the Child Controller. I did a workaround for it creating a method in the Main Controller $scope and pass $routeParams through it in the Child Controllers:
angular.module('MyModule')
.controller('MainController', ["$scope", function ($scope) {
$scope.parentMethod = function($routeParams) {
//do stuff
}
}]);
angular.module('MyModule')
.controller('MyCtrl1', ["$scope", function ($scope) {
$scope.parentMethod($routeParams);
}]);
angular.module('MyModule')
.controller('MyCtrl2', ["$scope", function ($scope) {
$scope.parentMethod($routeParams);
}]);
had the same problem, and building off what Andre mentioned in his answer about $routeParams taking a moment to load in the main controller, I just put it in a timeout inside my MainCtrl.
angular.module('MyModule')
.controller('MainCtrl', function ($scope, $routeParams, $timeout) {
$timeout(function(){
// do stuff with $routeParams
console.log('routeParams:'+JSON.stringify($routeParams));
}, 20);
});
20ms delay to use $routeParams is not even noticeable, and less than that seems to have inconsistent results.
More specifically about my problem, I was confused because I had the exact same setup working with a different project structure (yo cg-angular) and when I rebuilt my project (yo angular-fullstack) I started experiencing the problem.
You have at least two problems here:
with $routeParams you get the route parameters, which you didn't define
the file where you define a main controller doesn't really matter. the important thing is in which module/function
The parameters have to be defined with the $routeProvider with the syntax :paramName:
$routeProvider.when("/view2/name1/:a/name2/:b"
and then you can retrieve them with $routeParams.paramName.
You can also use the query parameters, like index.html?k1=v1&k2=v2.
app.js is the file where you'd normally define dependencies and configuration (that's why you'd have there the app module .config block) and it contains the application module:
var myapp = angular.module(...);
This module can have other modules as dependencies, like directives or services, or a module per feature.
A simple approach is to have a module to encapsulate controllers. An approach closer to your original code is putting at least one controller in the main module:
myapp.controller('MainCtrl', function ($scope) {...}
Maybe you defined the controller as a global function? function MainCtrl() {...}? This pollutes the global namespace. avoid it.
Defining your controller in the main module will not make it "to take effect in all routes". This has to be defined with $routeProvider or make the controller of each route "inherit" from the main controller. This way, the controller of each route is instantiated after the route has changed, whereas the main controller is instantiated only once, when the line ng-controller="MainCtrl" is reached (which happens only once, during application startup)
You can simply pass values of $routeParams defined into your controller into the $rootScope
.controller('MainCtrl', function ($scope, $routeParams, MainFactory, $rootScope) {
$scope.contents = MainFactory.getThing($routeParams.id);
$rootScope.total = MainFactory.getMax(); // Send total to the rootScope
}
and inject $rootScope in your IndexCtrl (related to the index.html)
.controller('IndexCtrl', function($scope, $rootScope){
// Some code
});
What is correct way of passing variables from web page when initializing $scope?
I currently know 2 possibilities:
ng-init, which looks awful and not recommended (?)
using AJAX request for resource, which requires additional request to server which I do not want.
Is there any other way?
If those variables are able to be injected through ng-init, I'm assuming you have them declared in Javascript.
So you should create a service (constant) to share these variables:
var variablesFromWebPage = ...;
app.constant('initValues', variablesFromWebPage);
With this service, you don't need to add them to the scope in the app start, you can use it from any controller you have, just by injecting it (function MyCtrl(initValues) {}).
Althouhg, if you do require it to be in the scope, then this is one of the main reasons what controllers are meant for, as per the docs:
Use controllers to:
Set up the initial state of a scope object.
Add behavior to the scope object.
Just add this cotroller to your root node:
app.controller('InitCtrl', function($rootScope, initValues) {
$rootScope.variable1 = initValue.someVariable;
$rootScope.variable2 = initValue.anotherVariable;
});
#Cunha: Tnx.
Here some more details how I did it:
In the Webpage:
<script type="text/javascript">
var variablesFromWebPage = {};
variablesFromWebPage.phoneNumber = '#Model.PhoneNumber';
</script>
In the angular application file, registering the service:
var module= angular.module('mymodule', ['ngRoute', 'ngAnimate']);
module.factory('variablesFromWebPage', function () {
return variablesFromWebPage
});
And then the controller:
module.controller('IndexController',
function ($scope, $location, $http, $interval, variablesFromWebPage) {
$scope.phoneNumber = variablesFromWebPage.phoneNumber;
}
);