Angular JS $location.path() getter not updating - angularjs

I want to use the $location.path() method to return the URL path so I can write some conditional statements in an ng-hide directive. I created the following in my controller:
$scope.pathLocation = $location.path();
I then inserted {{pathLocation}} in my html just to make sure it was returning the correct path, which it is. The problem comes when I load a different view. The pathLocation doesn't update. If I manually refresh my browser on the new page view, it does. `
Here is an abbreviated version of my code:
Controller:
(function(){
var amApp = angular.module('amApp', ['ngRoute', 'ngCookies','ngAnimate' ]);
amApp.controller('WelcomeController', ['$scope', '$location', function WelcomeController($scope, $location) {
$scope.pathLocation = $location.path();
}]);
})();
Here is the HTML:
<html lang="en" ng-app="amApp">
<body ng-controller="WelcomeController as welcome">
<nav>menu's here to different views in SPA</nav>
{{pathLocation}}
<div ng-view></div>
</body>

As mentioned in my comment, nothing in your code updates your pathLocation property. Try adding this
$scope.$on('$locationChangeSuccess', function() {
$scope.pathLocation = $location.path();
});

Publish the function on scope:
amApp.controller('WelcomeController', ['$scope', '$location', function WelcomeController($scope, $location) {
//$scope.pathLocation = $location.path();
$scope.locationPathFn = $location.path;
}]);
Then execute the function in the HTML:
<html lang="en" ng-app="amApp">
<body ng-controller="WelcomeController as welcome">
<nav>menu's here to different views in SPA</nav>
{{locationPathFn()}}
<div ng-view></div>
</body>
Then on every digest cycle the $location.path() function will be checked and the view kept up to date.

Related

AngularJS - $templateCache is not defined

I am trying to load a template file in an AngularStrap popover, however I am having trouble using $templateCache. I seem to be a step further back than the other SO questions, hence this seemingly double one.
Following the API docs I added a <script type="text/ng-template" id="popoverTemplate.html"></script> right before the closing </body> tag. When I use <div ng-include="'popoverTemplate.html'"></div> on my page, I get nothing. If I try using console.log($templateCache.get("popoverTemplate.html")) I get "$templateCache is not defined", which leads me to assume I am missing a crucial step. However, I can't find how to do it in the docs or other SO questions.
EDIT:
Injecting the service was the missing link. However, when I inject the service, the controller's other function no longer works, but if you inject al the function's parameters the working code becomes:
(function() {
"use strict";
angular.module("app").controller("managerController", ["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
}]);
})();
To use the template script tag . You have to insert it inside the angular application. That is inside the element with the ng-app attribute or the element used to bootstrap the app if you don't use the ng-app tag.
<body ng-app="myapp">
<div ng-template="'myTemplate.html'"></div>
<script type="text/ng-template" id="myTemplate.html">
// whate ever
</script>
</body>
If you want to retrieve the template on a component of the application then you need to inject the service where you want to consume it:
controller('FooCtrl', ['$templateCache', function ($templateCache) {
var template = $templateCache.get('myTemplate.html');
}]);
Or
controller('FooCtlr', FooCtrl);
FooCtrl ($templateCache) {};
FooCtrl.$inject = ['$templateCache'];
EDIT
Do not register two controllers with the same name because then you override the first one with the last one.
(function() {
"use strict";
angular.module("app").controller("managerController",["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
}]);
})();
Small addition: Although there are few ways to achieve your goals, like wrapping your whole HTML in <script> tags and all that, the best approach for me was to add the $templateCache logic into each Angular directive. This way, I could avoid using any external packages like grunt angular-templates (which is excellent but overkill for my app).
angular.module('MyApp')
.directive('MyDirective', ['$templateCache', function($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('MyTemplate').data,
controller: 'MyController',
controllerAs: 'MyController'
};
}]).run(function($templateCache, $http) {
$http.get('templates/MyTemplate.html').then(function(response) {
$templateCache.put('MyTemplate', response);
})
});
Hope this helps!

$watch for location.hash changes

In my angular app I am trying to change the location to "#home" when the user enters an invalid route.
Here's a short but complete app demonstrating what I did.
<!DOCTYPE html>
<html>
<head>
<script src="angular.js"></script>
<script>
angular.module('app', [])
.controller('ctrl', function($scope, $location, $window) {
$scope.$watch (function() { return $location.url() }, function(url) {
if (url == '')
$window.location = '#home'
})
})
</script>
</head>
<body ng-app="app" ng-controller="ctrl"></body>
</html>
But when I do this I get an infdig error when the page loads. I don't know whats wrong.
Edit:
I don't want to use routing libraries like "ui-router". so answers like this one that uses the "angular-route" library are not helpful.
You can specify that in app.config block using $routeProvider.
app.config(function ($routeProvider) {
$routeProvider
.when('/home', {
template: 'home.html',
controller: 'homeController'
})
.otherwise('/home');
});
.otherwise method will redirect the application to /home, if user tries to access any invalid path which is not specified in config.
Update:
And, If you don't want to use angular routing library you can use native javascript event hashchange on window to listen for hash change events. and redirect accordingly.
see the below example.
function locationHashChanged() {
if (location.hash !== "#/home") {
console.log('hash is other then #home. will redirect to #home');
console.log('Full path before', document.URL);
window.location.hash = '#/home';
console.log('Full path after', document.URL);
}
}
window.addEventListener('load', locationHashChanged);
window.addEventListener('hashchange', locationHashChanged);
Home
<br/>
Some-Site
<br/>
Other

How to read url query parameters with AngularJS?

I'd like to get the value of specific url parameters using AngularJS. I then want to assign a specific value to a specific textbox.
An example URL might look like:
http://example.com/?param1=value1
I've seen examples about $location, routing and services. I don't want to do any of that. I just need the value of param1. Any ideas how that can be done?
Here's a corresponding jsfiddle.net with several attempts: http://jsfiddle.net/PT5BG/211/
Using $location is the angular way
$location.search().param1; should give it if html5mode=true
Otherwise you have to use pure javascript. Check this.
How can I get a specific parameter from location.search?
If you are using ngRoute, look for routeParams
If you using ui-Router, look for stateParams
JS way:
var key = 'param1';
var value = window.location.search.substring(window.location.search.indexOf(key)+key.length+1);
Try this.You need to route concept in angular js
<!DOCTYPE html>
<html lang="en">
<head>
<title>AngularJS Routes example</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular-route.min.js"></script>
</head>
<body ng-app="sampleApp">
Route 1 + param<br/>
Route 2 + param<br/>
{{param}}
<div ng-view></div>
<script>
var module = angular.module("sampleApp", ['ngRoute']);
module.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/route1/:param', {
templateUrl: 'your_url1',
controller: 'RouteController'
}).
when('/route2/:param', {
templateUrl: 'your_url2',
controller: 'RouteController'
}).
otherwise({
redirectTo: '/'
});
}]);
module.controller("RouteController", function($scope, $routeParams) {
$scope.param = $routeParams.param;
alert($scope.param);
})
</script>
</body>
</html>
$routeParams allows you dto the value of parameter

AngularJS timing issues with controller, AJAX, ng-repeat, and directive

I just started a few days ago with AngualrJS (loving it) using the myApp example, but I'm not quite grasping some of the event timing yet.
I have a router that initiates a templateURL and a controller. The templateURL is a partial that uses ng-repeat to loop over JSON that I load with a controller. If the controller contains static JSON it seems to work as I intend but when I load the JSON using $http I start running into the timing issues:
The partial interprets before the $http returns throwing a 404 on things like image paths before they can be replaced -> "{{album.thumbnail}}.jpg"
I also have a directive that fires once after the controller launches the asynchronous $http call and again after the $http call actually completes within the controller (ideally I only want it to fire once after $http completion)
My intention is to use retrieve album JSON data via $http using ng-repeat to loop over a template to build out my gallery. Once it's done looping I'd like to call a final function that gives the gallery a Pinterest-like masonry layout. That's seem like a very typical flow ($http -> ng-repeat -> final function) so I've got to be missing something small at this point.
Looking forward to learning more about AngularJS...
HTML
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<title>myApp</title>
</head>
<body>
<div ng-view></div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular-route.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="js/modernizr-transitions.js"></script>
<script src="js/jquery.masonry.min.js"></script>
</body>
</html>
Module
angular.module('myApp', [
'ngRoute',
'myApp.filters',
'myApp.services',
'myApp.directives',
'myApp.controllers'
])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/gallery', {templateUrl: 'gallery.html', controller: 'ctrlGallery'});
$routeProvider.otherwise({redirectTo: '/gallery'});
}]);
Controller
angular.module('myApp.controllers', [])
.controller('ctrlGallery', ['$scope', '$http', function (lcScope, lcHttp) {
lcHttp.get("/services/gallery.php?start=1&count=12")
.success(function(data, status, headers, config) {
lcScope.albums = data.DATA;
})
}]);
Directive
angular.module('myApp.directives', [])
.directive('postGalleryRepeatDirective', function () {
return function (scope, element, attrs) {
if (scope.$last) {
$('#container').masonry({
itemSelector: '.box',
columnWidth: 250,
isAnimated: !Modernizr.csstransitions,
isFitWidth: true
});
}
};
});
gallery.html partial
<div class="masonry" ng-controller="ctrlGallery">
<div class="box masonry-brick" ng-repeat="album in albums" post-gallery-repeat-directive>
<a href="{{ album.link }}">
<img src="/img/{{ album.thumbnail }}.jpg">
</a>
<p>{{ album.name }}</p>
</div>
</div>
It seems like you've forgotten to set $scope.last to true in your http get call? Were you intending to set that to true after the http call returned? If so, you can do a somewhat similar thing for the partial where you set ng-hide to the value of $scope.last. So it would hide the contents of the boxes (and the broken links pre correct interpolation) until the proper values had been set.

Can I make a function available in every controller in angular?

If I have a utility function foo that I want to be able to call from anywhere inside of my ng-app declaration. Is there someway I can make it globally accessible in my module setup or do I need to add it to the scope in every controller?
You basically have two options, either define it as a service, or place it on your root scope. I would suggest that you make a service out of it to avoid polluting the root scope. You create a service and make it available in your controller like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
$scope.callFoo = function() {
myService.foo();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callFoo()">Call foo</button>
</body>
</html>
If that's not an option for you, you can add it to the root scope like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalFoo = function() {
alert("I'm global foo!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalFoo()">Call global foo</button>
</body>
</html>
That way, all of your templates can call globalFoo() without having to pass it to the template from the controller.
You can also combine them I guess:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.run(function($rootScope, myService) {
$rootScope.appData = myService;
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="appData.foo()">Call foo</button>
</body>
</html>
Though the first approach is advocated as 'the angular like' approach, I feel this adds overheads.
Consider if I want to use this myservice.foo function in 10 different controllers. I will have to specify this 'myService' dependency and then $scope.callFoo scope property in all ten of them. This is simply a repetition and somehow violates the DRY principle.
Whereas, if I use the $rootScope approach, I specify this global function gobalFoo only once and it will be available in all my future controllers, no matter how many.
AngularJs has "Services" and "Factories" just for problems like yours.These are used to have something global between Controllers, Directives, Other Services or any other angularjs components..You can defined functions, store data, make calculate functions or whatever you want inside Services and use them in AngularJs Components as Global.like
angular.module('MyModule', [...])
.service('MyService', ['$http', function($http){
return {
users: [...],
getUserFriends: function(userId){
return $http({
method: 'GET',
url: '/api/user/friends/' + userId
});
}
....
}
}])
if you need more
Find More About Why We Need AngularJs Services and Factories
I'm a bit newer to Angular but what I found useful to do (and pretty simple) is I made a global script that I load onto my page before the local script with global variables that I need to access on all pages anyway. In that script, I created an object called "globalFunctions" and added the functions that I need to access globally as properties. e.g. globalFunctions.foo = myFunc();. Then, in each local script, I wrote $scope.globalFunctions = globalFunctions; and I instantly have access to any function I added to the globalFunctions object in the global script.
This is a bit of a workaround and I'm not sure it helps you but it definitely helped me as I had many functions and it was a pain adding all of them to each page.

Resources