AngularJS and IE compatibility mode - javascript

I have angularJS(AngularJS v1.3.0-beta.3) application that crashes in IE10 compatibility mode. It works fine in FF, Chrome and IE11. Here is what I get as an error in console:
Multiple directives [login, login] asking for 'login' controller on: <div>
to set state of application, I create a node:
link: function ($scope, $element, $attrs) {
....
$element.html('<login></login>');
$compile($element.contents())($scope); // crash happens here
....
}
Here is my login directive:
widgets.directive('login', ['$compile', '$http', 'resourceLoader', function ($compile, $http, resourceLoader) {
return {
restrict: 'AE',
replace: true,
template: '<div></div>',
controller: function ($scope, $element) {
$scope.user.isLogged = false;
$scope.user.password = undefined;
$scope.submitLogin = function () {
// code that goes to server
};
},
link: function ($scope, $element, $attrs) {
resourceLoader.get('templates', 'profile', 'unlogged/login', 'jquery.min', function (template) {
$element.html(template);
$compile($element.contents())($scope);
});
}
};
}]);
Any ideas? Thanx.

The main issue is Angular 1.3 does not support older versions of Internet Explorer, more specifically IE8 and less. Putting IE10 in compatibility mode will make the browser act as if it were an older browser for certain layouts and features. The backwards compatability issues are likely the culprit here.
The suggestion by Angular is to remain in a version less than 1.3 to ensure compatability.
References:
See Angular's post on the 1.3 update and review Compatibility Mode settings for further reading on the issues.

Have you tried changing the restriction on the directive from EA to just E, or (probably better for compatability) just A and then using <div data-login="true"></div>?
It looks like something strange is going on with how the html is being parsed - I expect that it's probably adding an attribute for its own use in compatibility, which is screwing everything up.
If this doesn't work, you'd be much more likely to get a correct answer if you provide a plunker or a fiddle to demonstrate the issue more clearly.

Add this line
if ( name === 'login' ) console.log(name, directiveFactory.toString());
at this line
If it prints out twice, you are really loading the directive twice. With the directiveFactory's source code printed out, you will also see if it's the same directive loaded twice or two directives with the same name.

Give id="ng-app" where you are assigning your module name ng-app="module". That will support IE.

Adding below line in index.html's head section solved my problem:
<meta http-equiv="x-ua-compatible" content="IE=edge">
For more info : https://stackoverflow.com/a/46489212/698127

Related

Angular directive gets "Synchronous XMLHttpRequest on the main thread is deprecated"

I have an Angular directive element nested inside a controller, and I get this message
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
WARNING: Tried to load angular more than once.
I kinda understand what it means, but I don't know what is causing this issue. Here's what I have:
audio.html
<div class="audio-top-bar" ng-controller="AudioController">
<div class="audio-tuner-button" ng-click="displayTunerModal()">
<button class="btn">
<p class="audio-tuner-type">{{tunerButton}}</p>
</button>
</div>
<display-tuner-modal ng-show="visibility"></display-tuner-modal>
</div>
AudioController.js
angular.module('app').controller('AudioController', ['$scope', 'TunerService', function($scope, TunerService){
$scope.visibility = false;
if ($scope.tunerButton == (null || undefined))
$scope.tunerButton = 'FM';
$scope.displayTunerModal = function(){
$scope.visibility = true;
};
}]);
displayTunerModal.js
angular.module('app').directive('displayTunerModal', ['TunerService' , function (TunerService){
return {
restrict: 'E',
link: function(scope, elem, attrs){
scope.displayTunerModal = function(){
console.log("CLICKED");
};
},
templateUrl: 'public/templates/displayScreenModal.html'
};
}]);
My apologies the problem was as simple as having an incorrect name for the link within the directive. I should have had "displayTunerModal.html".
Moral of the story: check your links.
As per this question I had the same Synchronous XMLHttpRequest on the main thread is deprecated in an AngularJS directive on my production server while having no such error on my local environment.
In my case this was due to a case mismatch in template name vs js request :
AngularJS (a directive in my situation) was requesting lowercase mytemplate.tmpl.html
the template file was named with camelCase: myTemplate.tmpl.html
What confused the issue was that the directive / template worked fine on the Mac (due to OSX case-insensitivity) - but failed when pushed to a Linux case-sensitive AWS production server.
Changing the template file name to lowercase fixed the issue.
As mentioned in one of the comments above, the AJAX error is misleading as it implies something wrong with a specific call, rather than simply that a file is "missing".

Angular 1.2 to 1.3 (or newer) form removeControll and addControll issues

I'm migratin app from Angular 1.2 to 1.3. And I'm facing to problem with different behaviour of removeControll and addControll functions.
I have a directive like this, which escaping registered elements in form (don't ask why, We just need it).
<div name="es.caped" ng-model="es.caped" esc-dir></div>
link: function link($scope, $element, $attrs, $ctrl) {
var nameAttr = $attrs.name.replace(/([ #;?&,.+*~\':"!^$[\]()=>|\/#\{\}])/g, '\\$1');
$ctrl[1].$removeControl($ctrl[0]);
$ctrl[0].$name = nameAttr;
$ctrl[1].$addControl($ctrl[0]);
}
And Angular 1.2 will give the wanted result {"es\\.caped":{}}.
But 1.3 will give me old value {"es.caped":{}}
For more info, how it works see:
1.2 version - Plunker 1.2 version
1.3 version - Plunker 1.3 version
Plunker will print result form as a json (1.3 output is more bigger, but you can find the values).
I tried to search in docs and for versions 1.2 and 1.3 there are almost nothing, but for 1.5 I found:
Note that the current state of the control will not be reflected on the new parent form. This is not an issue with normal use, as freshly compiled and linked controls are in a $pristine state. However, if the method is used programmatically, for example by adding dynamically created controls, or controls that have been previously removed without destroying their corresponding DOM element, it's the developers responsibility to make sure the current state propagates to the parent form
And I already spent many hours to solve this problem, but with no success. I don't know how to propagate changes. I will be glad for any help.
Note: I tried it also with angular 1.4 and 1.5. And it has same behaviour as 1.3. My final state of migration is 1.5, but I'm following migration guide version by version.
Problem can be solved with this. It will change name of the element and reflect it in the controller.
link: function link($scope, $element, $attrs, $ctrl) {
var nameAttr = $attrs.name.replace(/([ #;?&,.+*~\':"!^$[\]()=>|\/#\{\}])/g, '\\$1');
$attrs.name = nameAttr ;
}

Using $routeProvider with <form action> and <input name> [duplicate]

I'd like to read the values of URL query parameters using AngularJS. I'm accessing the HTML with the following URL:
http://127.0.0.1:8080/test.html?target=bob
As expected, location.search is "?target=bob".
For accessing the value of target, I've found various examples listed on the web, but none of them work in AngularJS 1.0.0rc10. In particular, the following are all undefined:
$location.search.target
$location.search['target']
$location.search()['target']
Anyone know what will work? (I'm using $location as a parameter to my controller)
Update:
I've posted a solution below, but I'm not entirely satisfied with it.
The documentation at Developer Guide: Angular Services: Using $location states the following about $location:
When should I use $location?
Any time your application needs to react to a change in the current
URL or if you want to change the current URL in the browser.
For my scenario, my page will be opened from an external webpage with a query parameter, so I'm not "reacting to a change in the current URL" per se. So maybe $location isn't the right tool for the job (for the ugly details, see my answer below). I've therefore changed the title of this question from "How to read query parameters in AngularJS using $location?" to "What's the most concise way to read query parameters in AngularJS?". Obviously I could just use javascript and regular expression to parse location.search, but going that low-level for something so basic really offends my programmer sensibilities.
So: is there a better way to use $location than I do in my answer, or is there a concise alternate?
You can inject $routeParams (requires ngRoute) into your controller. Here's an example from the docs:
// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
//
// Then
$routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
EDIT: You can also get and set query parameters with the $location service (available in ng), particularly its search method: $location.search().
$routeParams are less useful after the controller's initial load; $location.search() can be called anytime.
Good that you've managed to get it working with the html5 mode but it is also possible to make it work in the hashbang mode.
You could simply use:
$location.search().target
to get access to the 'target' search param.
For the reference, here is the working jsFiddle: http://web.archive.org/web/20130317065234/http://jsfiddle.net/PHnLb/7/
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $location) {
$scope.location = $location;
$scope.$watch('location.search()', function() {
$scope.target = ($location.search()).target;
}, true);
$scope.changeTarget = function(name) {
$location.search('target', name);
}
}
<div ng-controller="MyCtrl">
Bob
Paul
<hr/>
URL 'target' param getter: {{target}}<br>
Full url: {{location.absUrl()}}
<hr/>
<button ng-click="changeTarget('Pawel')">target=Pawel</button>
</div>
To give a partial answer my own question, here is a working sample for HTML5 browsers:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="http://code.angularjs.org/1.0.0rc10/angular-1.0.0rc10.js"></script>
<script>
angular.module('myApp', [], function($locationProvider) {
$locationProvider.html5Mode(true);
});
function QueryCntl($scope, $location) {
$scope.target = $location.search()['target'];
}
</script>
</head>
<body ng-controller="QueryCntl">
Target: {{target}}<br/>
</body>
</html>
The key was to call $locationProvider.html5Mode(true); as done above. It now works when opening http://127.0.0.1:8080/test.html?target=bob. I'm not happy about the fact that it won't work in older browsers, but I might use this approach anyway.
An alternative that would work with older browsers would be to drop the html5mode(true) call and use the following address with hash+slash instead:
http://127.0.0.1:8080/test.html#/?target=bob
The relevant documentation is at Developer Guide: Angular Services: Using $location (strange that my google search didn't find this...).
It can be done by two ways:
Using $routeParams
Best and recommended solution is to use $routeParams into your controller.
It Requires the ngRoute module to be installed.
function MyController($scope, $routeParams) {
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
// $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
var search = $routeParams.search;
}
Using $location.search().
There is a caveat here. It will work only with HTML5 mode. By default, it does not work for the URL which does not have hash(#) in it http://localhost/test?param1=abc&param2=def
You can make it work by adding #/ in the URL. http://localhost/test#/?param1=abc&param2=def
$location.search() to return an object like:
{
param1: 'abc',
param2: 'def'
}
$location.search() will work only with HTML5 mode turned on and only on supporting browser.
This will work always:
$window.location.search
Just to summerize .
If your app is being loaded from external links then angular wont detect this as a URL change so $loaction.search() would give you an empty object . To solve this you need to set following in your app config(app.js)
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider)
{
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}]);
Just a precision to Ellis Whitehead's answer. $locationProvider.html5Mode(true); won't work with new version of angularjs without specifying the base URL for the application with a <base href=""> tag or setting the parameter requireBase to false
From the doc :
If you configure $location to use html5Mode (history.pushState), you need to specify the base URL for the application with a tag or configure $locationProvider to not require a base tag by passing a definition object with requireBase:false to $locationProvider.html5Mode():
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
you could also use $location.$$search.yourparameter
I found that for an SPA HTML5Mode causes lots of 404 error problems, and it is not necessary to make $location.search work in this case. In my case I want to capture a URL query string parameter when a user comes to my site, regardless of which "page" they initially link to, AND be able to send them to that page once they log in. So I just capture all that stuff in app.run
$rootScope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
if (fromState.name === "") {
e.preventDefault();
$rootScope.initialPage = toState.name;
$rootScope.initialParams = toParams;
return;
}
if ($location.search().hasOwnProperty('role')) {
$rootScope.roleParameter = $location.search()['role'];
}
...
}
then later after login I can say
$state.go($rootScope.initialPage, $rootScope.initialParams)
It's a bit late, but I think your problem was your URL. If instead of
http://127.0.0.1:8080/test.html?target=bob
you had
http://127.0.0.1:8080/test.html#/?target=bob
I'm pretty sure it would have worked. Angular is really picky about its #/

AngularJS $window.confirm not working in Chrome

angular.module('myApp')
.controller('pancakeController', ['$scope', '$window', function($scope, $window) {
$scope.panCakes = [];
$scope.removePancake = function(index) {
if($window.confirm('are you sure?')) {
$scope.panCakes.splice(index, 1);
} else {
$scope.panCakes.splice(index, 1);
}
};
}]);
myApp is already defined in another file. I'm using angular.module('myApp') to grab a reference to it.
Trying to use window.confirm() to confirm the user before deleting a panCake but the confrim box does not popup in Chrome 37.0.2062.94 but does work in Chrome Canary.
I'm using the AngularJS $window object, but using regular window.confirm does not work either. Is there something that I'm missing in my code or is just a bug in that particular version of Chrome?
The most probable cause is that you have at some point checked the little checkbox saying that you don't want to see any more alerts/confirms/prompts from this page.
(Among other solutions, closing the tab and reopening the page in a new tab should restore alerts/confirms/prompts.)
I am working on the same version of chrome as you are and the above code was not working in the fiddle as you had syntax error in the definition of the angular.module
It should be
angular.module('myApp',[])
Instead of
angular.module('myApp')
Working Fiddle

Element directive template changes not reflected in browser?

So I've got an element directive defined:
mod.directive("saleList", function() {
return {
restrict: "E",
templateUrl: "templates/sale-list.html"
};
});
and when I load the /search route I see it downloaded in the Network tab (I have cache in the browser disabled):
when('/search/:zip', {
templateUrl: "templates/sale-list.html",
controller: 'SearchController'
}).
However, I do not see one of its referenced element directives. Consider this snippet of HTML from the sale-list.html:
<sales-in ng-show="salesInZip()"></sales-in>
<sale-card ng-repeat="sale in sales.local"></sale-card>
I don't see the sale-card getting downloaded.
Now, the reason I'm going at it from this direction is because if I make a change in the sale-card I don't see them reflected in the browser until I reboot the machine. This probably has something to do with the fact that I use WebStorm as my IDE and it has its own local server that isn't easily configurable or clearble.
What's going on here?
NOTE: this just started happening recently, and I wonder if it's because something changed in the angular libraries; I am using their CDN to download the most recent version:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.1/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.1/angular-sanitize.min.js"></script>
mod.directive("saleList", function() {
return {
restrict: "E",
replace: true,
templateUrl: "templates/sale-list.html"
};
});
Try this.

Categories

Resources