Angular in chrome auto-firing ng-click - angularjs

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

Related

Protractor - Unable to access element due to fixed Top navigation bar

I'm facing the following issue in protractor with jasmine
Click/mouse hover not working because of fixed top navigation bar in my application. I need to click/perform mouse hover on a web page.
Unfortunately that element is displaying behind that fixed navigation bar. So scroll till element present & click by x & y coordinates are not working.
My dependencies are :
protractor version 5.2.2
node 8.9.3
selenium standalone 3.13
chrome driver-2.40
chromebrowser v67
OS- Windows 10
Thanks in advance
Try using prototype executeScript
Just try clicking that element from the browser console using id,name or xpath.
For example :
var el = element(by.module('header'));
var tag = browser.executeScript('return arguments[0].click()', el).then(function() {
expect(something).toMatch(something);
});
Another way, along the same lines as what Bharath Kumar S and knowing JeffC's caveat that this approach is cheating, I had a similar issue where the App-Header kept getting in my way of clicking, and I knew I was willing to never need it (so, for instance, to find other ways to navigate or log out and not check for stuff that was on it). I, therefore, did the following, which solved the problem. Note if you refresh the screen, you have to call it again. Also note I am using a number of functions from https://github.com/hetznercloud/protractor-test-helper, which do what you would expect from their names.
var removeAppHeaderIfAny = async function() {
//this function hides the app header
//it is useful to avoid having covers there when Protractor worries that something else will get the click
let found = false;
try {
found = await waitToBeDisplayed(by.className("app-header"), 2000);
} catch (e) {
let s: string = "" + e;
if (s.search("TimeoutError") != 0) flowLog("presumably fine, cover already removed: " + e);
found = false;
}
if (!found) return;
if (found) {
let coverElement = await element(by.className("app-header"));
browser.executeScript(
"arguments[0].style.visibility='hidden';",
coverElement
);
await waitToBeNotDisplayed(by.className("app-header"), 10000);
}
return;
//note after this is called you will not see the item, so you cannot click it
};
As I look at the code, it strikes me one can probably remove the if (found) and associated brackets at the end. But I pasted in something I know has been working, so I am not messing with that.
As indicated up front, I knew I was willing to forego use of the app-header, and it is a bit crude.

Cortana ran into an issue

I have created a javascript application (aka UWA) in order to play with my Belkin wemo and then turn on or turn off the ligth with Cortana. The following function is well called but Cortana ends up with an issue. If I remove the call to the HTTP call, the program works fine. Who can tell me what's wrong with the following function because no more details are exposed unfortunately (of course in the real program is replaced with the right URL):
function setWemo(status) {
WinJS.xhr({ url: "<url>" }).then(function () {
var userMessage = new voiceCommands.VoiceCommandUserMessage();
userMessage.spokenMessage = "Light is now turned " + status;
var statusContentTiles = [];
var statusTile = new voiceCommands.VoiceCommandContentTile();
statusTile.contentTileType = voiceCommands.VoiceCommandContentTileType.titleOnly;
statusTile.title = "Light is set to: " + status;
statusContentTiles.push(statusTile);
var response = voiceCommands.VoiceCommandResponse.createResponse(userMessage, statusContentTiles);
return voiceServiceConnection.reportSuccessAsync(response);
}).done();
}
Make sure that your background task has access to the WinJS namespace. For background tasks, since there isn't any default.html, base.js won't be getting imported automatically unless you explicitly do it.
I had to update winjs to version 4.2 from here (or the source repository on git), then add that to my project to update from the released version that comes with VS 2015. WinJS 4.0 has a bug where it complains about gamepad controls if you try to import it this way (see this MSDN forum post)
Then I added a line like
importScripts("/Microsoft.WinJS.4.0/js/base.js");
to the top of your script's starting code to import WinJS. Without this, you're probably getting an error like "WinJS is undefined" popping up in your debug console, but for some reason, whenever I hit that, I wasn't getting a debug break in visual studio. This was causing the Cortana session to just hang doing nothing, never sending a final response.
I'd also add that you should be handling errors and handling progress, so that you can periodically send progress reports to Cortana to ensure that it does not time you out (which is why it gives you the error, probably after around 5 seconds):
WinJS.xhr({ url: "http://urlhere/", responseType: "text" }).done(function completed(webResponse) {
... handle response here
},
function error(errorResponse) {
... error handling
},
function progress(requestProgress) {
... <some kind of check to see if it's been longer than a second or two here since the last progress report>
var userProgressMessage = new voiceCommands.VoiceCommandUserMessage();
userProgressMessage.DisplayMessage = "Still working on it!";
userProgressMessage.SpokenMessage = "Still working on it";
var response = voiceCommands.VoiceCommandResponse.createResponse(userProgressMessage);
return voiceServiceConnection.reportProgressAsync(response);
});

Sometimes svg chart doesn't fit in the container but it works when screen refresh

Sometimes svg chart appear more small than normal. This problem is solved Reloading Screen.
//Values Graphic
$scope.$watch(vm.dataGraphic.watch, function () {
var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;
setTimeout(sleep,500); //patch to "solve" this issue
function sleep(){
vm.dataValues = getDataValues(data);
}
});
function getDataValues(data) {
vm.dataGraphic = data || dataGraphicTest;
if (vm.dataGraphic.values.length == 0) {
return [];
} else {
vm.dataKeyValues = transformForKeyValues(vm.dataGraphic.values, vm.dataGraphic.accumulated);
vm.barValues = transformBarValues(vm.dataGraphic.values, vm.dataGraphic.limit);
var lineValues = transformLineValues(vm.barValues, vm.dataGraphic.limit, vm.dataGraphic.accumulated, vm.dataGraphic.startMonthlyLimit);
vm.maxY = calculateMaxY(vm.barValues, lineValues);
return [
{
"key": vm.dataGraphic.labelX.name,
"bar": true,
"color": _graphicsColors.bar,
"values": vm.barValues
},
{
"key": _graphicsValorPorDefecto,
"color": _graphicsColors.line,
"values": lineValues
}
];
}
}
SVG element with the following html tags appears with wrong dimensions.
<g class="nvd3 nv-wrap nv-linePlusBar" transform="translate(35,10)">
This problem does not always happen, but when it happens is arranged refreshing the screen.
I think this patch is a bad idea and I would like to understand what is happening .
Thanks
I think your problem ultimately stems from your watch:
$scope.$watch(vm.dataGraphic.watch, function () {
var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;
setTimeout(sleep,500); //patch to "solve" this issue
function sleep(){
vm.dataValues = getDataValues(data);
}
});
The problem I see is that you're using setTimeout to call your sleep function a half second after the watch fires. As it is now, this will be fine if your $digest is still running in 500ms. setTimeout is a vanilla JavaScript function, so calling it won't notify angularjs that there are any changes after the $digest has ended. Angular only triggers a $digest after 'user-initiated' events like ajax, clicks, entering data, etc. (read more here). If the $digest is running, you'll get lucky and angularjs will just happen to see those changes. You'll need to inform angular of your changes using $scope.$apply:
$scope.$watch(vm.dataGraphic.watch, function () {
var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;
setTimeout(sleep,500); //patch to "solve" this issue
function sleep(){
vm.dataValues = getDataValues(data);
$scope.$apply();
}
});
There are a lot of ways to use $scope.$apply, so be sure to check out the documentation. Also do a search on something like 'when to use $scope.$apply' and you'll find a lot of people for and against it. It does trigger another $digest loop, which can be costly if you have a lot of bindings or if you're at the end of the final digest loop and apply causes it to start over.
I don't think it's actually a good idea to be updating your model using setTimeout from within a watch function. Does this not work without it? You may have some other async code that needs to be applied in the same fashion. Generally, you want $scope.$apply to be as close to the non-angular asynchronous code as possible.

Angular and IE9 stupidity where native methods

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.

How can I use AngularJS data binding with CKEditor?

How should I go about this?
I was able to get the data into CKEditor by using a textarea with the name attribute matching my model and a script tag with ng:bind-template to call CKEDITOR.replace.
I then made a CKEditor plugin that detects changes and writes them back to the textarea. The problem is that the textarea looses its event handlers when CKEditor initializes and CKEditor doesn't pickup changes to the textarea. This makes me think that I am approaching this the wrong way.
Next I tried using ng:eval-order="LAST" ng:eval="setupCKEditor()" and setting up the editor from the setupCKEditor() function. This didn't work because even with ng:eval-order="LAST" the function is still run before the nodes are created.
I have found that adding a setTimeout(function () {...},0) around the CKEDITOR.replace helps. Now the only problem is that when it changes the model it doesn't repaint the screen until another field is edited.
scope.$root.$eval(); seems to fix that.
Update
We ended up abandoning this since we could never get it to reliably work. We switched to TinyMCE with Angular-UI for a while and then ended up building something custom.
This sort of works with the onchange plugin from http://alfonsoml.blogspot.com/2011/03/onchange-event-for-ckeditor.html.
angular.directive("my:ckeditor", function (expression, compiledElement) {
return function fn(element) {
var scope = this;
setTimeout(function () {
CKEDITOR.replace("editor-" + index, {extraPlugins: 'onchange'});
scope.$watch("layers[" + index + "].src", function () {
if (!CKEDITOR.instances["editor-" + index]) return;
if (scope[expression] == CKEDITOR.instances["editor-" + index].getData()) return;
CKEDITOR.instances["editor-" + index].setData(scope[expression]);
});
CKEDITOR.instances["editor-" + index].on("change", function () {
scope[expression] = CKEDITOR.instances["editor-" + index].getData();
scope.$root.$eval();
});
}, 0);
};
});
Update
This has only been tested on v0.10.6
For completeness, attached is a module to provide an angular directive. I've not used it yet, so I can not comment on how well it works/integrates.

Resources