ng-map: Cannot read property 'apply' of undefined when setting zoom level - angularjs

I'm using ngMap in an angularjs app. I'm running into an error when I try to set the zoom on a map element. Here's the error message from the Chrome console:
TypeError: Cannot read property 'apply' of undefined
at Wk.eventFunc (ng-map.js:205)
at Object.T.trigger (main.js:18)
at kf (main.js:22)
at Wk.N.set (main.js:21)
at Wk.setZoom (main.js:29)
at ng-map.js:1568
at angular.js:6837
at forEach (angular.js:323)
at Object.$get.Attributes.$set (angular.js:6835)
at interpolateFnWatchAction (angular.js:8157)
This is being triggered when the zoom attribute of my map element:
<map center="{{initCenter.lat}}, {{initCenter.lng}}" zoom="{{zoom}}" on-dragend="dragend()" on-zoom_changed="zoomchanged()">
is being bound to my angular controller:
app.controller("mapCtrl", function ($scope, $location, dataContext) {
$scope.markers = [];
$scope.initCenter = dataContext.center();
$scope.zoom = dataContext.zoom();
dataContext is a service that exposes various state variables in the app. I've also tried setting $scope.zoom through a function, with the same resulting error.
The error takes place on line 1568 of the ng-map.js file:
var observeAndSet = function(attrs, attrName, object) {
attrs.$observe(attrName, function(val) {
if (val) {
void 0;
var setMethod = parser.camelCase('set-'+attrName);
var optionValue = parser.toOptionValue(val, {key: attrName});
void 0;
if (object[setMethod]) { //if set method does exist
/* if an location is being observed */
if (attrName.match(/center|position/) &&
typeof optionValue == 'string') {
_this.getGeoLocation(optionValue).then(function(latlng) {
object[setMethod](latlng);
});
} else {
// this is the line that causes the problem
object[setMethod](optionValue);
}
}
}
});
};
In trying to trace through what's happening I followed the execution for setting center and for setting zoom (setting center doesn't cause a problem). Setting the center executes line 1568 and continues on its merry way.
Setting zoom (via the setZoom function) causes code at line 196 in ng-map.js to execute:
var eventFunc = function(attrValue) {
var matches = attrValue.match(/([^\(]+)\(([^\)]*)\)/);
var funcName = matches[1];
var argsStr = matches[2].replace(/event[ ,]*/,''); //remove string 'event'
var args = scope.$eval("["+argsStr+"]");
return function(event) {
function index(obj,i) {return obj[i];}
var f = funcName.split('.').reduce(index, scope);
// this next line causes an exception in google map main.js
f.apply(this, [event].concat(args));
scope.$apply();
}
}
Line 205 leads to the exception that results in the error inside google map's main.js file.
Any idea what's causing this? And how to solve it :)?
Possible Solution
Playing around with this a bit more I noticed that it isn't setting the zoom level per se which causes the problem. It's actually the result of the zoomchanged event firing >>after<< the zoom is changed.
The first time zoom is set on the map, line 204:
var f = funcName.split('.').reduce(index, scope);
evaluates to null. I'm not sure why that is, because I don't understand what that line is doing.
However, the future changes to zoom have that same line 204 resolving to a non-null value for f.
So, as a workaround, I bracketed lines 205 and 206 like this:
if (f != null) {
f.apply(this, [event].concat(args));
scope.$apply();
}
and the error went away.

I think this has the same problem with this issue, https://github.com/allenhwkim/angularjs-google-maps/issues/181
and it's resolved with the release of 1.7.7.
I used $timeout, instead of $apply.

Related

Ace-Editor Ignore/whitelist some tag/string

Is there any way to avoid Ace-Editor to raise an error if I use some tag like <#input "asd">?? Like a whitelist to suggest AceEditor to ignore it... Anyway, It's an internal key and I cannot avoid using it.
I'm using react.
Thanks
I'm thinking of two ways to solve the issue:
1) Maybe you can bind the ">", get the last entered values and then clear the errors:
editor.commands.addCommand({
name: "dotCommand1",
bindKey: { win: ".", mac: "."},
exec: function () {
var pos = editor.selection.getCursor();
var session = editor.session;
var curLine = (session.getDocument().getLine(pos.row)).trim();
var curTokens = curLine.slice(0, pos.column).split(/\s+/);
//You can build a logic using the curTokens array and then when you find <#input "asd"> clear the errors.
// If we assume curTokens[0] to have the value
if(curTokens[0] === '<#input "asd">') {
editor.session.setAnnotations([]); // This would remove the being shown error
}
}
});
2) Use the on change event and use the above similar logic and then clear the error
editor.getSession().on('change', function () {
// same logic as above
})

Mock document.activeElement in Jasmine test

I have the following function:
function focusIsNotInInput() {
// If the element currently in focus is of a certain type, then the key handler shouldn't run
var currentlyInFocus = $window.document.activeElement;
var blacklist = ['INPUT', 'TEXTAREA', 'BUTTON', 'SELECT', 'IFRAME', 'MD-OPTION'];
return !blacklist.some(function (nodeName) {
return nodeName === currentlyInFocus.nodeName;
});
}
And I need to mock that the element currently in focus is of one of the specified types, but can't get it to work.
I've tried injecting window, like this:
beforeEach(function() {
var $windowMock;
inject(function(_$window_) {
$windowMock = _$window_;
$windowMock.document.activeElement.nodeName = 'INPUT';
});
});
But when the code above runs, the active element is always still body. It's getting overwritten. I have also tried creating an element and setting focus on it:
var elementInFocus = $('<input>');
this.elem.append(elementInFocus);
elementInFocus.triggerHandler('focus');
elementInFocus.focus();
But it's the same, body is always in focus, what ever I do.
I had some trouble with this too, a possible solution (worked for me) is to add a spyOn(element, 'focus') -- here's a reference: How do I check if my element has been focussed in a unit test
My successful solution:
const htmlItem = fixture.nativeElement;
const searchBar = htmlItem.querySelector('.search-box');
let focusSpy = spyOn(searchBar, 'focus');
searchBar.focus();
expect(focusSpy).toHaveBeenCalled();

Failing To Check if Element is Disabled

I have a dropdown with elements that get disabled when conditions are met. In the test, I check them for being disabled, but all tests fail and always return the element state as enabled (Clearly incorrectly. I have ensured that this is not a timing issue - refreshed the page and gave ample wait time with browser sleep - the elements are clearly disabled on the screen). There is an anchor within a list item. Please see image:
I have tried checking both the list item and the anchor, like so:
var actionDropDownList = $$('[class="dropdown-menu"]').get(1);
var checkOutButtonState = actionDropDownList.all(by.tagName('li')).get(6);
actionsButton.click();
actionDropDownList.all(by.tagName('li')).count().then(function(count){
console.log('THE NUMBER OF ELEMENTS IN THE DROPDOWN IS...............................................................' + count);
}) //verify that I have the correct dropdown - yes
checkOutButtonState.isEnabled().then(function(isEnabled){
console.log('CHECKING checkOutButton BUTTON STATE: ' + isEnabled);
}) //log state - shows incorrectly
I have also tried checking the button itself for disabled state (the element below is what I tried checking instead of the list element):
var checkOutButton = $('[ng-click="item.statusId !== itemStatus.in || checkOut()"]');
This failed as well.
Not sure which one I should check and why both are failing. How do I correct this and get it to show that the disabled button is...well, disabled.
TEMPORARY ADD ON EDIT:
For simplicity's sake, I am trying:
var hasClass = function (element, cls) {
return element.getAttribute('class').then(function (classes) {
return classes.split(' ').indexOf(cls) !== -1;
});
var checkOutButtonState = actionDropDownList.all(by.tagName('li')).get(6);
expect(hasClass(checkOutButtonState, 'disabled')).toBe(true);
It still fails, however, despite the element clearly having the class. Alec - your solution throws "function is not defined," I am not sure if I need something else for it to see jasmine. Tried, but can't find anything wrong with it, not sure why I can't get it to work.
Edit:
If I run...since it only appears to have one class:
expect(checkOutButtonState.getAttribute('class')).toBe('disabled');
I get "expected 'ng-isolate-scope' to be 'disabled'"
In a quite similar situation I've ended up checking the presence of disabledclass:
expect(checkOutButtonState).toHaveClass("disabled");
Where toHaveClass() is a custom jasmine matcher:
beforeEach(function() {
jasmine.addMatchers({
toHaveClass: function() {
return {
compare: function(actual, expected) {
return {
pass: actual.getAttribute("class").then(function(classes) {
return classes.split(" ").indexOf(expected) !== -1;
})
};
}
};
},
});
});

UnknownError: unknown error: Element is not clickable at point (713, 6). Other element would receive the click: <div class="container">...</div>

I am getting really weird error. In my test, I first navigate to angularjs.org. Then I sendKeys() to an input field called "JavaScript Projects" which has filters in it. After that I click a checkbox and mark a todo item as Done. However, on doing this, it get the error,
UnknownError: unknown error: Element is not clickable at point (713, 6). Other element would receive the click: ...
(Session info: chrome=43.0.2357.81)
If I reverse the order of my execution above, no error occurs.
Here is my Code
var util = require ('util');
describe ("Page object text", function() {
var homepage = require('../pages/angularjs_page.js');
it ("Should mark an item done", function() {
homepage.get();
browser.sleep(2000);
homepage.searchText('jquery');
homepage.markDoneTodo(0);
});
});
Here is the page object code:
var angularjs_page = function() {
this.get = function() {
browser.get('http://www.angularjs.org');
};
this.markDoneTodo = function(index) {
element.all(by.repeater('todo in todoList.todos'))
.get(index)
.element(by.model('todo.done'))
.click();
};
this.searchText = function(txt) {
element(by.model('projectList.search')).sendKeys(txt);
};
};
module.exports = new angularjs_page();
Maximizing the browser window did not work. Sleep() does not seem to be causing this issue. Inserting a sendKey() method in between works fine.
browser.sleep(2000);
homepage.searchText('jquery');
homepage.enterName("Hello World");
homepage.markDoneTodo(0);
So, what is wrong with executing searchText() and markDoneTodo() methods in sequence?
Problem Found
Turns out that the static top navigation menu bar was overlapping the checkboxes. Is scrolling the best way to solve it, and how?
Using scroll solved the problem.
browser.executeScript('window.scrollTo(0,document.body.scrollHeight)');

$compile an HTML template and perform a printing operation only after $compile is completed

In a directive link function, I want to add to document's DIV a compiled ad-hoc template and then print the window. I try the following code and printer preview appears, but the data in preview is still not compiled.
// create a div
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
// Trying to add some template to div
scope.someVar = "This is print header";
var htmlTemplate = "<h1>{{someVar}}</h1>";
var ps = angular.element(printSection);
ps.append(htmlTemplate);
$compile(ps.contents())(scope);
// What I must do to turn information inside printSection into compiled result
// (I need later to have a table rendered using ng-repeat?)
window.print();
// ... currently shows page with "{{someVar}}", not "This is print header"
Is it also so that $compile is not synchronous? How I can trigger window.print() only after it finished compilation?
you just need to finish the current digestion process to be able to print
so changing
window.print();
to
_.defer(function() {
window.print();
});
or $timeout, or any deferred handler.
will do the trick.
The other way (probably the 'right' approach) is to force the newly compilated content's watchers to execute before exiting the current $apply phase :
module.factory("scopeUtils", function($parse) {
var scopeUtils = {
/**
* Apply watchers of given scope even if a digest progress is already in process on another level.
* This will only do a one-time cycle of watchers, without cascade digest.
*
* Please note that this is (almost) a hack, behaviour may be hazardous so please use with caution.
*
* #param {Scope} scope : scope to apply watchers from.
*/
applyWatchers : function(scope) {
scopeUtils.traverseScopeTree(scope, function(scope) {
var watchers = scope.$$watchers;
if(!watchers) {
return;
}
var watcher;
for(var i=0; i<watchers.length; i++) {
watcher = watchers[i];
var value = watcher.get(scope);
watcher.fn(value, value, scope);
}
});
},
traverseScopeTree : function(parentScope, traverseFn) {
var next,
current = parentScope,
target = parentScope;
do {
traverseFn(current);
if (!(next = (current.$$childHead ||
(current !== target && current.$$nextSibling)))) {
while(current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
}
} while((current = next));
}
};
return scopeUtils;
});
use like this :
scopeUtils.applyWatchers(myFreshlyAddedContentScope);

Resources