Why is pushState disrupted when using ng-include? - angularjs

When using ng-include, the pushState function is disrupted.
The following is a simple example of this.
<div ng-app="angularApp" ng-controller="angularCtrl">
<div ng-include="templateUrl"></div>
<button ng-click="bye()">BYE</button>
</div>
and
let app = angular.module('angularApp', []);
app.controller('angularCtrl', async function ($scope) {
$scope.templateUrl = '/assets/hi.html';
$scope.bye = function () {
$scope.templateUrl = '/assets/bye.html';
history.pushState({},'','/finish.html')
};
});
We want to change the body value after pressing the BYE button using ng-include and also change the page address by pushState.
This example is part of a larger project that has been simplified here as much as possible.
Note: According to the reviews, the url is changed by pushState but immediately returns its value. It is ignored during the ng-include process.

Changing the browser location is inherently tricky in AngularJS. It is best to try to use the $location service as much as possible as this understands and works with the $scope digest machinery better.
Try using the following in your app:
let app = angular.module('angularApp', []);
app.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
app.controller('angularCtrl', function ($scope, $timeout, $location) {
$scope.templateUrl = '/assets/hi.html';
$scope.bye = function () {
$scope.templateUrl = '/assets/bye.html';
$location.url('/finish.html');
};
});
Note that you will need to provide a base[href] for html5 mode support:
<html lang="en">
<head>
...
<base href="/">
</head>
<body>
<div ng-app="angularApp" ng-controller="angularCtrl">
<div ng-include="templateUrl"></div>
<button ng-click="bye()">BYE</button>
</div>
</body>
</html>

Related

Async variable stored in $rootScope not available in other controllers

I am using Angular 1.x for my stack and when I make an API call and store the response in the $rootScope, it is not accessible in other controllers' view.
My controller:
angularApp.controller('mainController', ['$scope', '$rootScope', '$http', function($scope, $rootScope, $http){
var checkIfAuthenticated = function(){
return $http.get('/api/isAuthenticated');
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
}]);
Now, in another controller's view I use it like this:
<div ng-if="userLoggedIn" class="py-1 pl-1 pr-1">
<span><b>Message Board</b></span>
<div class="form-control" readonly id="lockTextbox">Show something</div>
</div>
The problem is, the API call /api/isAuthenticated does provide the right response (status 200) but the ng-view gets it wrong.
I am sure this is to do with $rootScope.userLoggedIn being a response from an async call (as I get Success in my console) but how do I solve it?
Thanks in advance!
EDIT
After I posted the question, I noticed that in the mainController's view, the ng-if logic doesn't work either. What is very strange is that when I open up the browser's debug console, it starts working! I think this is just an async issue but I don't know how to solve it. How do I tell the view that the variable is on it's way?
OK, to solve the timing issue, I'll rework the answer completely. This should be a - quick and dirty but - working example:
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="plunker" ng-cloak>
<div ng-controller="MainCtrl as $ctrl">
<h1>Hello {{$ctrl.name}}</h1>
<p>Start editing and see your changes reflected here!</p>
<div ng-if="$ctrl.name === 'Angular.js'"><b>Message Board</b</div>
</div>
</body>
</html>
script.js
import angular from 'angular';
angular.module('plunker', []).controller('MainCtrl', function($scope, $http) {
const self = this;
self.name = 'Plunker';
console.log('hello');
function checkIfAuthenticated(){
console.log('get');
return $http.get('https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js');
};
checkIfAuthenticated().then(function(res) {
if(res.status===200){
console.log('Success');
self.name = 'Angular.js'; // insert any logic here
} else {
console.log('Failed');
}
})
});
Console
hello
get
Success
Does it work for you?
Working Example
The below demo shows the $rootScope variable available in both controllers after being set from a promise delayed by two seconds.
angular.module("app",[])
.controller('mainController', function($scope, $rootScope, $timeout) {
var checkIfAuthenticated = function(){
return $timeout(function() {
return { status: 200 };
}, 2000);
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
})
.controller('otherController', function($scope) {
console.log("other controller");
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<fieldset ng-controller="mainController">
MAIN Controller userLoggedIn={{userLoggedIn}}<br>
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
<fieldset ng-controller="otherController">
OTHER Controller userLoggedIn={{userLoggedIn}}
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
</body>
I found the problem. I updated my Angular from 1.6.4 to 1.7.9 (and all modules like angular-sanitize etc) and that did the trick. Should have been the first thing I did but I missed it entirely

Play Framework + Angular Issue with JSON render on Page

I have simple Scala Play Framework and Angular application. I tried to render JSON data on play's "xxx.scala.html" template but don't know what is the problem it is not rendering as expeted.
#main("Welcome to Play") {
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"> </script>
<script>
app.controller('NamesCtrl', function($scope) {
// get names using AngularJS AJAX API
$http.get('/getNames').success(function(data){
$scope.names = data;
});
});
</script>
<div ng-app="app" ng-contoller="NamesCtrl">
<ul>
<li ng-repeat=" name in names">{{name}}</li>
</ul>
</div>
}
My route entry
GET /getNames controllers.HomeController.getNames
Scala Controller:
def getNames = Action {
val names = List("Bob","Mike","John")
Ok(Json.toJson(names)).as(JSON)
}
When I am calling my page using url
http://localhost:9000/getNames
I was getting response on page as below,
["Bob","Mike","John"]
Please can you explain what am I doing wrong here?
Thanks !!
There are some problems in the code. The correct one is this:
#main("Welcome to Play") {
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.19/angular.min.js"></script>
<div ng-app="myApp" ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">{{name}}</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('NamesCtrl', function($scope, $http) {
$http.get('/getNames').success(function(data){
console.log(data);
$scope.names = data;
});
});
</script>
}
What has changed:
AngularJS 1.3.19 instead of 1.2.10
AngularJS module - which is referenced in the div
injected the $http service in the controller
your ng was wrong - it should be ng-controller instead of ng-contoller (a typo I guess)
The result is what you would expect:

Angularjs href attribute remove if value is mailto:email

I need to show link in page having attribute value mailto.
normal href attribute value working fine but if value is email than it removed
Code:
myCtrl.link = '<code>Email</code>';
<code><span ng-bind-html="myCtrl.link"></span></code>
rendered output:
<code><a target="_blank">Email</a></code>
Please suggest how to handle anchor having href value like mailto:sulok#atlogys.com
You are running into a "security" issue,
please have a look at this doc-page...
Just say "this is safe" to angular:
function TestCtrl(vm, $sce) {
'use strict';
var htmlString = '<code>Email</code>';
vm.link = $sce.trustAsHtml(htmlString);
}
angular
.module('test', [])
.controller('TestCtrl', ['$scope', '$sce', TestCtrl])
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<article ng-app="test">
<div ng-controller="TestCtrl">
<code><span ng-bind="link"></span></code>
</div>
</article>

why my routeprovider not working?

I am learning routing with angular java script ,
this is my index.html
<html lang="en" ng-app="eventsApp">
.....
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li>Create Event</li>
</ul>
</div>
</div>
<ng-view></ng-view>
</div>
This is my app.js,
var eventsApp = angular.module('eventsApp', ['eventsApp', 'ngRoute'])
eventsApp.config(function($routeProvider) {
$routeProvider.when('/newEvent',
{
templateUrl:'templates/NewEvent.html',
controller: 'EditEventController'
}).....
When i click on the button it do nothing and also doesn't load new event .
and get this error
"Error: [$injector:modulerr] Failed to instantiate module eventsApp due to:"
here is the controller
'use strict';
eventsApp.controller('EditEventController',
function EditEventController($scope, eventData) {
$scope.event = {};
$scope.saveEvent = function (event, form) {
if(form.$valid) {
eventData.save(event);
}
};
$scope.cancelEdit = function () {
window.location = "/EventDetails.html";
};
}
);
One of the things I'm noticing looks wrong is that you're passing in eventsApp as a dependency of itself.
var eventsApp = angular.module('eventsApp', ['eventsApp', 'ngRoute'])
Should be:
var eventsApp = angular.module('eventsApp', ['ngRoute'])
Secondly, you may want to check that you've included ngRoute into the page:
See installation instructions here: http://docs.angularjs.org/api/ngRoute
Check out a working example on Plunkr:
http://plnkr.co/edit/VZgTnBvi0Q8qtcpOUTx0

How/when to use ng-click to call a route?

Suppose you are using routes:
// bootstrap
myApp.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when('/about', {
templateUrl: 'partials/about.html',
controller: 'AboutCtrl'
});
...
And in your html, you want to navigate to the about page when a button is clicked. One way would be
<a href="#/about">
... but it seems ng-click would be useful here too.
Is that assumption correct? That ng-click be used instead of anchor?
If so, how would that work? IE:
<div ng-click="/about">
Routes monitor the $location service and respond to changes in URL (typically through the hash). To "activate" a route, you simply change the URL. The easiest way to do that is with anchor tags.
Go Home
Go to About
Nothing more complicated is needed. If, however, you must do this from code, the proper way is by using the $location service:
$scope.go = function ( path ) {
$location.path( path );
};
Which, for example, a button could trigger:
<button ng-click="go('/home')"></button>
Here's a great tip that nobody mentioned. In the controller that the function is within, you need to include the location provider:
app.controller('SlideController', ['$scope', '$location',function($scope, $location){
$scope.goNext = function (hash) {
$location.path(hash);
}
;]);
<!--the code to call it from within the partial:---> <div ng-click='goNext("/page2")'>next page</div>
Using a custom attribute (implemented with a directive) is perhaps the cleanest way. Here's my version, based on #Josh and #sean's suggestions.
angular.module('mymodule', [])
// Click to navigate
// similar to <a href="#/partial"> but hash is not required,
// e.g. <div click-link="/partial">
.directive('clickLink', ['$location', function($location) {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(function() {
$location.path(attrs.clickLink);
});
});
}
}
}]);
It has some useful features, but I'm new to Angular so there's probably room for improvement.
Remember that if you use ng-click for routing you will not be able to right-click the element and choose 'open in new tab' or ctrl clicking the link. I try to use ng-href when in comes to navigation. ng-click is better to use on buttons for operations or visual effects like collapse.
But
About
I would not recommend. If you change the route you might need to change in a lot of placed in the application. Have a method returning the link. ex:
About. This method you place in a utility
I used ng-click directive to call a function, while requesting route templateUrl, to decide which <div> has to be show or hide inside route templateUrl page or for different scenarios.
AngularJS 1.6.9
Lets see an example, when in routing page, I need either the add <div> or the edit <div>, which I control using the parent controller models $scope.addProduct and $scope.editProduct boolean.
RoutingTesting.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Testing</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-route.min.js"></script>
<script>
var app = angular.module("MyApp", ["ngRoute"]);
app.config(function($routeProvider){
$routeProvider
.when("/TestingPage", {
templateUrl: "TestingPage.html"
});
});
app.controller("HomeController", function($scope, $location){
$scope.init = function(){
$scope.addProduct = false;
$scope.editProduct = false;
}
$scope.productOperation = function(operationType, productId){
$scope.addProduct = false;
$scope.editProduct = false;
if(operationType === "add"){
$scope.addProduct = true;
console.log("Add productOperation requested...");
}else if(operationType === "edit"){
$scope.editProduct = true;
console.log("Edit productOperation requested : " + productId);
}
//*************** VERY IMPORTANT NOTE ***************
//comment this $location.path("..."); line, when using <a> anchor tags,
//only useful when <a> below given are commented, and using <input> controls
$location.path("TestingPage");
};
});
</script>
</head>
<body ng-app="MyApp" ng-controller="HomeController">
<div ng-init="init()">
<!-- Either use <a>anchor tag or input type=button -->
<!--Add Product-->
<!--<br><br>-->
<!--Edit Product-->
<input type="button" ng-click="productOperation('add', -1)" value="Add Product"/>
<br><br>
<input type="button" ng-click="productOperation('edit', 10)" value="Edit Product"/>
<pre>addProduct : {{addProduct}}</pre>
<pre>editProduct : {{editProduct}}</pre>
<ng-view></ng-view>
</div>
</body>
</html>
TestingPage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.productOperation{
position:fixed;
top: 50%;
left: 50%;
width:30em;
height:18em;
margin-left: -15em; /*set to a negative number 1/2 of your width*/
margin-top: -9em; /*set to a negative number 1/2 of your height*/
border: 1px solid #ccc;
background: yellow;
}
</style>
</head>
<body>
<div class="productOperation" >
<div ng-show="addProduct">
<h2 >Add Product enabled</h2>
</div>
<div ng-show="editProduct">
<h2>Edit Product enabled</h2>
</div>
</div>
</body>
</html>
both pages -
RoutingTesting.html(parent), TestingPage.html(routing page) are in the same directory,
Hope this will help someone.
Another solution but without using ng-click which still works even for other tags than <a>:
<tr [routerLink]="['/about']">
This way you can also pass parameters to your route: https://stackoverflow.com/a/40045556/838494
(This is my first day with angular. Gentle feedback is welcome)
You can use:
<a ng-href="#/about">About</a>
If you want some dynamic variable inside href you can do like this way:
<a ng-href="{{link + 123}}">Link to 123</a>
Where link is Angular scope variable.
just do it as follows
in your html write:
<button ng-click="going()">goto</button>
And in your controller, add $state as follows:
.controller('homeCTRL', function($scope, **$state**) {
$scope.going = function(){
$state.go('your route');
}
})

Resources