AngularJS Microsoft Edge Screen doesn't reflect the DOM - angularjs

I'm using AngularJS 1.5.0 and Microsoft Edge browser screen doesn't reflect the DOM.
I would like some suggestion how this can be fixed.
I can't really apply a fix for each element as the application is somewhat big with dynamic user content including Angular equations.
Also the app include a lot of dynamic bindings linked to input boxes.
Selecting the text with the mouse turn the 0 into a 2 which is the right value in the example below.
Also changing the position style back and forth seem to force Edge to redraw the element but it's somewhat an ugly fix that I don't like very much and it need to be trigger at so many places (Ajax request, input changes and so on...)
The page start with a value of 0. Then an Ajax call is made and it go fetch the real data. After some experimentation the bug only appears if the new data is 1 character (ex: 2 or 9). And it happens every time. If it's a 2 digit number (ex: 26) then the good number appears.
Any help on this matter would be gladly appreciated.

We were facing the same problem and it stopped occurring after removing the text-transform: uppercase style from the elements that are not updating.
#Sampson, it looks like this is a bug in Edge.

I was able to make a temporary fix with the use of Mutation Observer.
Little script that I made that works for AngularJS elements created server side.
Currently those are the only one causing trouble in my app.
<script>
$(document).ready(function(){
var ua = navigator.userAgent;
var re = new RegExp("Edge/");
if (re.exec(ua) != null){
console.log('edgeMutationObserver ON!!')
var edgeMutationConfig = { characterData: true, subtree: true };
var edgeMutationObserver = new MutationObserver(function(mutations) {
var rgb_p = /rgb\((\d+), (\d+), (\d+)\)/g;
var rgba_p = /rgba\((\d+), (\d+), (\d+), (\d.?\d*)\)/g;
mutations.forEach(function(mutation){
if(mutation.target.nodeType == 3){
var that = mutation.target.parentNode;
// Save the background color
var bgc = that.style.backgroundColor;
// Get applied style from style or css
var bgc_css = bgc || $(that).css('backgroundColor');
var bgc_temp,match;
if(match = rgb_p.exec(bgc_css)){
// rgb transformed in rgba triggers update
bgc_temp = 'rgba('+match[1]+','+match[2]+','+match[3]+',1)';
}else if(match = rgba_p.exec(bgc_css)){
// Slightly modify transparency
var alpha = match[4] == 0 ? 0.01 : match[4] - 0.01 ;
bgc_temp = 'rgba('+match[1]+','+match[2]+','+match[3]+','+alpha+')';
}else{
// If either inline style or css is already equal to transparent the redraw is not made so i choose an alternate color with 0 opacity
bgc_temp = bgc_css != 'transparent' ? 'transparent' : 'rgba(0,0,0,0)';
}
// Change background color to force redraw
that.style.backgroundColor = bgc_temp;
setTimeout(function(){
// Apply back previous style
// Doesn't redraw without a timeout in Edge
that.style.backgroundColor = bgc;
},0);
}
});
});
$('.ng-binding').each(function(){
edgeMutationObserver.observe(this, edgeMutationConfig);
});
}
});
</script>

Related

ui-scroll: Disable update/fetch while scrolling

I am trying to use ui-scroll to provide dynamic scrolling through a large dataset.
The get method calls a back end function which queries a database. The query is "slow" so I'd like to disable queries while the scroll thumb is moving.
For example, suppose the viewport shows 10 rows and there are 100,000 rows in the database and we fetch 100 rows at a time.
If you quickly drag the thumb from the top to the bottom, ui-scroll currently makes lots and lots of requests for data. I'd like to skip all of the intermediate requests and only fetch data when the thumb stops moving.
I've fiddled with isLoading on the adapter but couldn't find a way to make this work.
Suggestions? Angular 1.4.1.
$scope.datasource = { };
$scope.datasource.get = fetchData
$scope.datasource.minIndex = 0;
$scope.datasource.maxIndex = 10553;
$scope.scrollAdaptor = {};
var loadCount = 0;
function fetchData(desc, successCb)
{
var start = desc.index;
var end = desc.index + desc.count - 1;
var url = "scrollTest.php?start=" + start + "&end=" + end;
$http.get(url)
.then(function(goodResp)
{
console.log("got data, loadCount", loadCount);
successCb(goodResp.data);
},
function(badResp)
{
});
};
There is a discussion on GitHub related to the question. In short, angular-ui-scroll can't skip intermediate requests out-of-box, but following workaround might help.
Set min/max indexes on start to make the viewport relevant to
100,000 rows
$scope.datasource.minIndex = 1
$scope.datasource.maxIndex = 10000
Bind additional scroll event to intercept user scrolling over the viewport
Check scroll position delta between two events, and if it is more than some fixed value (say, 500px), rebuild the viewport
Rebuild means adapter.reload(indexToReload) method call and setting min/max indexes
The last thing is indexToReload, it should be calculated by current scroll position and item height:
indexToReload = Math.floor(scrollTop/itemHeight)

Listen for click / hover on individual axis labels

I'm looking for a way to change the value (or format) for an individual series label when hovering it in anycharts.
Currently I'm only able to access the entire axis and I can find no getter method for individual labels so as to attach a listener.
xAxis.labels().listen('mouseOver', function(e) {
console.log(this, e.target);
});
This jsfiddle is as far as I got (see console log), this as well as the event.target reference the entire axis but not the label:
https://jsfiddle.net/robstarbuck/pbhd4b7L/9/
Indeed, there was a little bug with cache and format() function, our dev team made the fix, so please check the working sample:
var labelIndex = e.labelIndex;
var label = this.getLabel(labelIndex);
var value = xAxis.scale().ticks().get()[labelIndex];
label.format(value * 2);
https://jsfiddle.net/pbhd4b7L/13/ – it also shows how to work with tick values:
Currently it takes the js from branch, but this fix will be included in the upcoming release – 7.14.0 version (ETA: May 2017)
Our API is a little bit complicated here, but we're working hard to improve it. Does this what you're looking for?
var labelIndex = e.labelIndex;
var label = this.getLabel(labelIndex);
label.fontColor('red');
label.draw();
https://jsfiddle.net/pbhd4b7L/10/
This issue was fixed in the 7.14.0 release, use this code:
xAxis.labels().listen('mouseOver', function(e) {
var labelIndex = e.labelIndex;
var label = this.getLabel(labelIndex);
var value = xAxis.scale().ticks().get()[labelIndex];
label.format(value * 2);
label.fontColor('red');
label.draw();
});
with the latest version: https://jsfiddle.net/2t08ahkg/3/

Textangular HTML mode clears model value

We decided to change our backend CMS to use text-angular's WYSIWYG editor. The content pulls from the database just fine, it gets rendered just fine, but the instant we go to view the HTML source, the text is there for an instant and then it disappears. I've turned off sanitization with the ta-unsafe-sanitizer="true" . The weird thing is, if I manually step through the angular code that does the digesting, eventually the text is rendered and it stays on the screen. If I run it without breakpoints, it clears the text.
I'm not sure if it is sanitization or some sort of race condition inside Angular. Anyone else run into this?
View
<div text-angular ta-toolbar="[['h1','h2','h3'],['bold','italics','underline'],['ul','ol'],['outdent','indent'],['html'],['insertImage']]" ng-model="updatePageTranslation.Content" ta-unsafe-sanitizer="true"></div>
Controller
$scope.updatePageTranslation.Content = 'large html portion here';
The scope of the form is set as follows:
<div class="widget" ng-controller="PageController">
Everything gets loaded fine and other fields of the form show the values correctly. The initial render of the content is correct. It is just when switching to HTML view that it goes blank. Clicking Html again switches back to the visual view, which is correct. But if I save, the value sent to the server is now blank.
I can even copy and paste the value into textangular.com site's demo textbox and have the same issue.
This was a strange one to figure out, but thanks to #HanletEscaño, I was able to find my way. When returning the content from the server, I had to do the following in order to pre-sanitize it so that you could switch back and forth between the HTML and rendered view:
Content.Replace(Environment.NewLine, "").Replace("\n", "").Replace("\r", "").Replace("\t", "").Replace(" ", "");
The important one is that last replace, where we replace two spaces with nothing. This seemed to be the final trick. We were coming from a previous WYSIWYG editor where we could make the HTML look nice, but with this editor, everything has to be condensed.
As mentioned above, this seems to be due to the parser struggling to handle whitespace and newlines correctly.
If you take a look at the textAngular source, in taBind.js, _taBlankTest service seems to returns true when the bound value contains spaces, newlines etc, which in turn causes the model to get set to undefined.
I replaced mine with the following to avoid that situation:
angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'])
.service('_taBlankTest', [function(){
var INLINETAGS_NONBLANK = /<(a|abbr|acronym|bdi|bdo|big|cite|code|del|dfn|img|ins|kbd|label|map|mark|q|ruby|rp|rt|s|samp|time|tt|var)[^>]*(>|$)/i;
return function(_defaultTest){
return function(_blankVal){
if(!_blankVal) return true;
var _matchVal = _blankVal.replace(/( |\t|\n)/gm, '');
// find first non-tag match - ie start of string or after tag that is not whitespace
var _firstMatch = /(^[^<]|>)[^<]/i.exec(_matchVal);
var _firstTagIndex;
if(!_firstMatch){
// find the end of the first tag removing all the
// Don't do a global replace as that would be waaayy too long, just replace the first 4 occurences should be enough
_matchVal = _matchVal.toString().replace(/="[^"]*"/i, '').replace(/="[^"]*"/i, '').replace(/="[^"]*"/i, '').replace(/="[^"]*"/i, '');
_firstTagIndex = _matchVal.indexOf('>');
}else{
_firstTagIndex = _firstMatch.index;
}
_matchVal = _matchVal.trim().substring(_firstTagIndex, _firstTagIndex + 100);
// check for no tags entry
if(/^[^<>]+$/i.test(_matchVal)) return false;
// this regex is to match any number of whitespace only between two tags
if (_matchVal.length === 0 || _matchVal === _defaultTest || /^>(\s| |\n|\t)*<\/[^>]+>$/ig.test(_matchVal)) return true;
// this regex tests if there is a tag followed by some optional whitespace and some text after that
else if (/>\s*[^\s<]/i.test(_matchVal) || INLINETAGS_NONBLANK.test(_matchVal)) return false;
else return true;
};
};
}])
Hopefully that may help someone out there!

Getting scroll position with sencha architect extjs

I have been trying to get the scroll position of a tree grid in and ext.js application using sencha architect. Originally I wanted to keep the scroll position after a reload so when a user deletes a post it will scroll back to that same spot after reload. I have attempted to use various methods I have searched for but nothing is working. I'm currently testing by having a java script timeout function give me the scroll position after a few seconds, long enough for me to scroll down and console.log the position but it gives me nothing and I'm not showing anything but zero when I go into the dom and look at the tree view object. I have already tried the following
1st: var newcompheight = comboboxeample.getHeight();
2nd: getScroll: function() {
var me = this,
dom = me.dom,
doc = document,
body = doc.body,
docElement = doc.documentElement,
left, top;
if (dom === doc || dom === body) {
// the scrollLeft/scrollTop may be either on the body or documentElement,
// depending on browser. It is possible to use window.pageXOffset/pageYOffset
// in most modern browsers but this complicates things when in rtl mode because
// pageXOffset does not always behave the same as scrollLeft when direction is
// rtl. (e.g. pageXOffset can be an offset from the right, while scrollLeft
// is offset from the left, one can be positive and the other negative, etc.)
// To avoid adding an extra layer of feature detection in rtl mode to deal with
// these differences, it's best just to always use scrollLeft/scrollTop
left = docElement.scrollLeft || (body ? body.scrollLeft : 0);
top = docElement.scrollTop || (body ? body.scrollTop : 0);
} else {
left = dom.scrollLeft;
top = dom.scrollTop;
}
return {
left: left,
top: top
};
3rd: var test = window.pageYOffset;
I have also attempted to setting the keepscroll position property in Sencha architect to true and other things I have googled but nothing is working. Any help would be appreciated.
Use this:
treeGrid.getEl().getScrollTop()

Extjs 4.0.7 How do I re render my treepanel to see changes

I am creating an item selector with two boxes that move things back and forth within an extjs application. On the right box, I am creating buttons that serve to move items up and down. Essentially I am swapping the item with one above or below it. So, my code is straight forward in that regard
MoveUp: function(button, event, eOpts){
var theChosen = Ext.getStore('storeId').getRootNode().findChild('text', 'Chosen folder');
var chosenPanel = Ext.ComponenetQuery.query('#chosenTreePanel')[0];
var selected = chosenPanel.getSelectionModel().getSelection();
for( var i = 1; i < theChosen.childNodes.length; i++){
if(Ext.Array.contains(selected, theChosen.childNodes[i]) && (!Ext.Array.contains(selected, theChosen.childNodes[i-1]){
var temp = theChosen.childNodes[i];
theChosen.childNodes[i] = theChosen.childNodes[i-1];
theChosen.childNodes[i-1] = temp;
}
}
}
All of this code seems to work fine, because after clicking my button, and checking the DOM in firebug, I can see that the selected nodes have moved in the array correctly, however, this effect is never shown within my treepanel. ???How do I make the treepanel update when it's elements change. ???
TreePanel heirarchy looks like this just to clarify
Root Node
'Chosen Folder Node'
Array of items I am moving up and down within the 'folder'
I am USING VERSION 4.0.7
Attempting to use replaceChild() to fire an event to rerender does not behave as I expected
Changing:
var temp = theChosen.childNodes[i];
theChosen.childNodes[i] = theChosen.childNodes[i-1];
theChosen.childNodes[i-1] = temp;
To:
var temp = theChosen.childNodes[i];
theChosen.replaceChild(theChosen.childNodes[i-1], theChosen.childNodes[i]);
theChosen.replaceChild(temp, theChosen.childNodes[i-1]);
Results in odd behavior in which some nodes go missing. Certaintly not what I was looking for. Where am I going wrong here?
Tried the following code using the datachanged and (undocumented)refresh event:
Ext.getStore('storeId').fireEvent('datachanged', Ext.getStore('chosen') );
Ext.getStore('storeId').fireEvent('datachanged', Ext.getStore('chosen') );
This does not reload anything...
SOLUTION:
Use the insertChild method of nodeInterface....
I have noticed something strange in how insertChild works in that I need to change my index more based on moving up or down will explain with code below.
To move Up:
theChosen.insertChild( (i-1), theChosen.childNodes[i]);
To move down:
theChosen.insertChild( (i+2), theChosen.childNodes[i]);
Although the -1 vs +2 they both effectively move the item by one in the appropriate direction.
If you want to update the view of the nodes, I recommend using yourTree.getView().refresh();
But you can avoid that by using parentNode.insertChild(index, childNode); where index is where you want the node to show up and parentNode is the parent to the nodes you are reordering. ChildNode can be a config for a new node or any other nodeinterface that already exists. If the node does already exist and you use the insertChild method to insert it, it will automatically remove that node from whereever else it is.
So as you provided in your question in response to my answer, your code will work with something like (this is probably how I'd do it, but this is untested):
MoveUp: function(button, event, eOpts){
var chosenPanel = Ext.ComponenetQuery.query('#chosenTreePanel')[0];
var selectedNodes = chosenPanel.getSelectionModel().getSelection();
for( var i = 0; i < selectedNodes.length; i++){
var currentNode = selectedNodes[i];
if(!currentNode.isFirst())
{
var parentNode = currentNode.parentNode;
var newIndex = parentNode.indexOf(currentNode) - 1;
parentNode.insertChild(newIndex, currentNode);
}
}
}
Edit:
Back to the the responsible event question...
You need to fire the
'datachanged'
'refresh'
Events on the store with the store as only param. That should cause a UI update. But please note that I just had a glimpse into the sourcecode and I am sure this can all be done much smarter. At least a DD solution for this exists already.
If I find some time I my look into this again, but I guess you should be fine with these events for the first.
You never see anything cause you just do it without the appropriate methods that then fire the responsible events that cause rerender. Take a look at
appendChild()
removeChild()
replaceChild()
There may also be more methods that can help on the Ext.data.NodeInterface. I recommend you to use these instead of doing it under hood without any responsible event fired.
In addition to my second comment (just a wild guess without knowing if that is exactly what you want):
MoveUp: function(button, event, eOpts){
var target = Ext.getStore('storeId').getRootNode().findChild('text', 'Chosen folder');
var selected = chosenPanel.getSelectionModel().getSelection();
target.appendChild(selected);
}

Resources