In Angular 1.2.0, there is this funny comment:
// IE stupidity! (IE doesn't have apply for some native functions)
It sits on line 9835 in the functionCall function:
functionCall: function(fn, contextGetter) {
var argsFn = [];
if (this.peekToken().text !== ')') {
do {
argsFn.push(this.expression());
} while (this.expect(','));
}
this.consume(')');
var parser = this;
return function(scope, locals) {
var args = [];
var context = contextGetter ? contextGetter(scope, locals) : scope;
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](scope, locals));
}
var fnPtr = fn(scope, locals, context) || noop;
ensureSafeObject(context, parser.text);
ensureSafeObject(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions)
var v = fnPtr.apply
? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
return ensureSafeObject(v, parser.text);
};
},
I believe it is causing me pain, but no errors are thrown so I'm having a hard time seeing exactly what native function it might be trying (and failing) to call apply on. Ever since I implemented $q library to use promises to handle async REST calls, IE9 doesn't even make an attempt to call the services (according to the network tab in dev tools). Instead, the promise is immediately rejected. I tried googling for an answer, and looking at angular's docs on using IE, but I'm getting nowhere.
Has anyone had a similar issue with getting promises to work on IE9 using angular's "q-lite"? Does anyone know what this silly comment is referring to specifically?
I believe I figured it out through a ton of trial and error. It seems the issue, in my case, was that I was using the built-in q library for promises... specifically q.all([]):
$q.all([
firstRequest.$promise,
secondRequest.$promise,
thirdRequest.$promise,
moreRequets.$promise
]).then(function() {
//do stuff
});
While I still have not found out what specific operations the angular code refers to when it says some native functions, I found that the docs for function.apply() have the following caveat:
Note: Most browsers, including Chrome 14 and Internet Explorer 9, still do not accept array-like objects and will throw an exception.
Whatever the specifics, removing my reference to $q.all solved it for me. I hope this helps anyone who has this issue in the future. If someone happens to encounter another case where this IE behavior chokes up angular, perhaps they would be so kind as to comment below or add an answer.
FYI, I am currently at angular 1.2.14.
Related
My question is about discovering possible spelling mistakes in angular expressions, in particular spelling mistakes in the function name.
Consider the snippet bellow:
I have two buttons there, the first one with correct spelling, the second with a spelling mistake in the angular expression. Clicking the second button does nothing and gives no hints about a potential error.
My question is now: are there ways to detect erroneous calls to function that don't exist (while executing the application)?
I am not looking for some checking possibility in the build or unit test process but rather would like to see a way I could get aware of such a potential issue when running the erroneous expression in the browser when the application is executed.
angular.module("myApp", [])
.controller("TestController", function($scope){
$scope.myFunction = function() {
console.log("Hello World");
};
});
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<section ng-controller="TestController">
<button ng-click="myFunction()">myFunction</button>
<button ng-click="myFunctio()">myFunctio</button>
</section>
I'm not familiar with a built in option in angular to do that (using binding to an "undefined" object is a legit UC as things may become "undefined" during program run) - but you may write your own "ng-click" directive which, in case not finding the function to bound to, raise an error (exception or better - console error / warning).
This is an extremely common complaint about Angular. Even when writing code for the Closure compiler, with all the type annotations and everything, these still fall right through the cracks.
You can kluge something together, I've seen things like bussing all events to a common broker and looking for the target handler in the bound scope, and so on. But it always appears to be more trouble than it's worth.
Your unit tests are where you catch this sort of thing. It's why being able to test template code via triggering events is such an important thing for an Angular developer to master. If you trigger that button click and your test fails (e.g. your spyOn the handler never gets called), check the template.
Protractor (and other end to end testing frameworks) will do that for you.
I'm not sure if this would work for function calls or not, but it would solve part of the problem of misspelling something. In Scott Allen's AngularJS Playbook course on Pluralsight, he suggests creating a decorator for the $interpolate service to see if any bindings are potentially incorrect. Here is the code for that:
(function(module) {
module.config(function ($provide) {
$provide.decorator("$interpolate", function ($delegate, $log) {
var serviceWrapper = function () {
var bindingFn = $delegate.apply(this, arguments);
if (angular.isFunction(bindingFn) && arguments[0]) {
return bindingWrapper(bindingFn, arguments[0].trim());
}
return bindingFn;
};
var bindingWrapper = function (bindingFn, bindingExpression) {
return function () {
var result = bindingFn.apply(this, arguments);
var trimmedResult = result.trim();
var log = trimmedResult ? $log.info : $log.warn;
log.call($log, bindingExpression + " = " + trimmedResult);
return result;
};
};
angular.extend(serviceWrapper, $delegate);
return serviceWrapper;
});
});
}(angular.module("common")));
in all documentation and tutorials for HTTP request i found that recomanded usage is something like:
var ax = {
getCaseData: function() {
api.cases.getCase(caseManager.data.id).then(function(res){
// puting my response to $scope.something
$scope.something = res.data;
});
},
}
I really don't feel like senior so please tell me if I am wrong but I thing many times you don't need to store something like this on scope, for example you want to just resolve promise once and display data (you are not doing anything else with it)
So I was thinking if is there an option to make a promise as:
var ax = {
getCaseData: function() {
return api.cases.getCase(caseManager.data.id);
},
}
and after this call tempalte something like:
<li ng-repeat="ax.getCaseData()"></li>
This was handled automatically in old version of AngularJS (<1.2), but was removed since then.
Some posts state that the feature can be re-enabled manually by adding this line in your ".config" function :
$parseProvider.unwrapPromises(true);
But this is not advised as a solution. You are currently doing it the right way.
If you have plenty of cases like this, you can probably create your own "promises wrapper" function, and use it from your template.
See : https://stackoverflow.com/a/19472065/1636977
I think I'm losing my mind
I'm working on a medium-large angular app. I came in and rebooted this morning and now angular seems to be executing every ng-click function when the state loads (I'm using $stateProvider). Was definitely not happening yesterday.
Doesn't happen in canary, or ie, or FF.. Just vanilla chrome (I'm on 43.0.2357.124 m)
If I step out of one of the ng-click calls it dumps me into angular.js:10555
functionCall: function(fn, contextGetter) {
var argsFn = [];
if (this.peekToken().text !== ')') {
do {
argsFn.push(this.expression());
} while (this.expect(','));
}
this.consume(')');
var parser = this;
return function(scope, locals) {
var args = [];
var context = contextGetter ? contextGetter(scope, locals) : scope;
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](scope, locals));
}
var fnPtr = fn(scope, locals, context) || noop;
ensureSafeObject(context, parser.text);
ensureSafeObject(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions)
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvRIGHT HEREvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
var v = fnPtr.apply
? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^RIGHT HERE^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return ensureSafeObject(v, parser.text);
};
}
(Since the problem is in chrome it's hitting fnPtr.apply(context, args) which is running my ng-clicks)
Once it's loaded all the way, everything works as expected. It's as though when it links my ng-clicks it's executing them!
It's 'running' the ng-clicks in every scope. Both in the 'master controller' down to directives in whichever state I've loaded (it makes datepickers go absolutely nuts)
Going back and forth between scopes doesn't re-fire the ng-clicks that weren't cleaned up (nav bar buttons for instance). But any new elements that load are susceptible.
I undid changes far back enough that I'm sure anything weird I did should be gone.
I'm going to poke around another few hours then try to re-create in a minimal example, but I'm hoping it's a chrome-update (although none of my other angular apps are having this problem :/ )
My impression is the functionCall method above should be running, and returning a function that'll get called when I actually click, but it's executing it as well as 'linking' it for later executing.
Update #1 - I stuck a console.log in the above function to spit out what it was trying to parse out
ok, in canary (and I assume FF and IE): only non-click-y angular-y things get run through the above functionCall function (like
<button title="{{'Edit '+myCoolName.replace('_', ')}}"... </button>
buut, in chrome: the above AND ng-clicks are getting run through that function.
Loaded: btTypeName.replace('', ' ')
Loaded: ((!newModel.author) ? "Create New " : ("Edit "+newModel.author+"'s ")) + btTypeName.replace('', ' ')
Loaded: btTypeName.replace('', ' ')
Loaded: ((!newModel.author) ? "Create New " : ("Edit "+newModel.author+"'s ")) + btTypeName.replace('', ' ')
Loaded: openReports(); <---- this shouldn't be here :o
Sweet lord I figured it out.
AngularJS Batarang released a new update and it killed everything. Removed it and everything's good as new (good as old?) anyways...
Looks like version: 0.8.0 released yesterday (6/18/15) is responsible.
I filed an issue in github, https://github.com/angular/angularjs-batarang/issues/227
This is a simple service I've built for Firebase for an application. I've had to jury-rig some elements and I've made absolutely sure I am using the latest versions of firebase and angular fire since it seems to be changing pretty fast. These first few lines are pretty straightforward,
app.factory('Ship', function ($routeParams, $firebase, FIREBASE_URL) {
var ref = new Firebase(FIREBASE_URL + 'ships');
The problems begin here. Depending on what I intend to do with the firebase object, at times it needs to be $asObject, at other times not. It depends on the tutorial and the most recent ones would seem to indicate that
var shipsObj = $firebase(ref).$asObject(); // Is this necessary
var ships = $firebase(ref); // in the most modern version?
var Ship = {
all: shipsObj, // This works fine
create: function (ship) {
return shipsObj.$add(ship); // This also works fine
},
find: function (shipId) {
console.log($routeParams.shipId); // <--this ID appears as the correct numerical ID
Then, there is the next six lines, NONE of which work. They all produce an error indicating that they are undefined.
console.log(shipsObj.$child(shipId));
console.log(ships.$child(shipId));
console.log(shipsObj.$getRecord(shipId));
console.log(ships.$getRecord(shipId));
console.log(ships.$keyAt(shipId));
console.log(shipsObj.$keyAt(shipId));
},
I won't bore you with repeating the next method multiple times as well, but $remove isn't working either.
delete: function (shipId) {
return ships.$remove(shipId);
}
};
return Ship;
});
Assuming your using v0.8 of AngularFire you'll want to use $asObject() or $asArray() to get at the actual data. Here's the official blog post that discusses the changes in v0.8: https://www.firebase.com/blog/2014-07-30-introducing-angularfire-08.html
So to access a ship by its id you could do:
var shipsObj = $firebase(ref).$asObject();
console.log(shipsObj[shipId]);
You may also want to take a look at the API docs for AngularFire: https://www.firebase.com/docs/web/bindings/angular/api.html
A lot changed in v0.8 and it just came out (July 2014) so if you're basing your code on anything older than that then it probably won't work
How can I apply animation in ui-views?
I found the code
// Unfortunately there is no neat way to ask $injector if a service exists
var $animator; try { $animator = $injector.get('$animator'); } catch (e) { /* do nothing */ }
But how can I inject an animator?
This is not a relevant question. If you're using a sufficiently recent version of AngularJS (i.e. 1.1.5), it works by itself.
See the documentation for details: https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-animate-ui-view-with-ng-animate