I am working in the Malhar widget framework, which is based on jQuery sortable widgets. ex/ https://github.com/DataTorrent/malhar-angular-dashboard
I am working on some DOM manipulation on each widget (maximize/minimize/refresh), and running into some Angular $scope.$apply exceptions below.
Function details:
The $scope.grabSouthResizer function (working fine) is the Mahlar function that came with the framework; I just modified it slight to also refresh the Kendo UI charts.
The $scope.maxResizer function is my custom function, which is throwing $rootScope:inprog exceptions every time is hits my $scope.$apply();.
$scope.grabSouthResizer = function (e) {
var widgetElm = $element.find('.widget');
e.stopPropagation();
e.originalEvent.preventDefault();
// get the starting horizontal position
// .. code ommitted for brevity
// sets new widget width on mouseup
var mouseup = function (e) {
// calculate height change
var curY = e.clientY;
var pixelChange = curY - initY;
var widgetContainer = widgetElm.find('.widget-content');
var diff = pixelChange;
var height = parseInt(widgetContainer.css('height'), 10);
var newHeight = (height + diff);
$scope.widget.setHeight(newHeight + 'px');
$scope.$emit('widgetChanged', $scope.widget);
$scope.$apply(); // *** NO EXCEPTIONS THROWN ***
$scope.$broadcast('widgetResized', {
height: newHeight
});
// kendo chart - refresh height
var chart = widgetElm.find('.k-chart').data("kendoChart");
if (chart != undefined) {
chart.setOptions({ chartArea: { height: newHeight - (newHeight * .10) } });
chart.resize($(".k-chart"));
}
};
};
$scope.maxResizer = function (e) {
// TODO: properly restore the window to original position..
var widgetElm = $element.find('.widget');
e.stopPropagation(); // testing - same as grabSouthResizer() below
e.originalEvent.preventDefault();
var pixelHeight = widgetElm.height();
var pixelWidth = widgetElm.width();
// fyi: '.k-tree' will auto-resize, so no need to find that
var chart = widgetElm.find('.k-chart').data("kendoChart");
var treelist = widgetElm.find('.k-treelist').data("kendoTreeList");
// height differential (reduce height of container if inner widget is a treelist)
var ht_diff = (chart != undefined ? 200 : 600);
var newHeight = window.innerHeight - ht_diff;
if (!widget.maximized) {
// widget container maximize
widget.maximized = true;
$scope.widget.setWidth(window.innerWidth);
$scope.widget.setHeight(newHeight); //window.innerHeight - ht_diff);
$scope.$emit('widgetChanged', widget);
$scope.$apply(); // *** THROWS $rootScope:inprog EXCEPTIONS !!! ***
$scope.$broadcast('widgetResized', {
width: window.innerWidth,
height: newHeight
});
if (chart != undefined) {
// refresh Kendo chart
chart.setOptions({ chartArea: { height: widgetElm.height()*.9, width: widgetElm.width()*.95 } });
chart.resize($(".k-chart"));
}
}
kendoRefreshTimer(); // this work-around used instead of $scope.$apply()
}
var timer;
function kendoRefreshTimer() {
timer = $timeout(function () {
refreshKendo();
}, 1);
}
function refreshKendo() {
// Kendo chart refresh here...
}
Big question: why is $scope.$apply(); causing errors in my maxResizer function, but not in the Malhar original grabSouthResizer function ? I also understand that $scope.$apply() is NOT recommended, but it seems to be widely used as a work-around.
I would create an online plunk, but I still haven't set up this Malhar widget framework online as of yet. It's a bit complicated to set up.
Your advice is appreciated.
regards,
Bob
* UPDATE *
I updated my post to show how I've worked around this scope.apply issue by using a $timeout function, but I don't like the split-second delay in the UI. i.e. You can see the Kendo chart resizing itself, so it doesn't look so smooth.
Related
I have the following protractor test listed below. It runs fine. But I need to add some code that opens the browser to full screen since my test is sensitive to pixel location of gridster tiles. How can this be done?
describe('DragAndDrop Test', function () {
require('protractor');
require('jasmine-expect');
beforeAll(function () {
context = new Context();
context.get();
browser.waitForAngular();
browser.driver.manage().window().maximize();
});
it('should drag and drop Application Experience tile', function () {
//a = angular.element(document).find('h3')[1]
//target is where we are dragging the box to. Box is the Box
var target = { x: 300, y: 50 };
var box = element(by.cssContainingText('h3', 'Application Experience'));
var infoSpot = element(by.cssContainingText('h3', 'Application Experience'));
//scope is going to hold the scope variables that tell us where the box is located
//get the standardItems Scope
box.evaluate('dashboards').then(function(scope) {
//make sure the box we are using is initially set in column 0 and Row 0
expect(scope['1'].widgets[0].col).toEqual(0);
expect(scope['1'].widgets[0].row).toEqual(0);
});
//drag and drop the box somewhere else.
browser.actions().dragAndDrop(box, target).perform();
browser.waitForAngular();
browser.driver.sleep(5000);
//get the updated scope
box.evaluate('dashboards').then(function(scope) {
//test to see that the box was actually moved to column 1 and row 0
expect(scope['1'].widgets[0].col).toEqual(1);
expect(scope['1'].widgets[0].row).toEqual(0);
});
});
});
var Context = function () {
this.ignoreSynchronization = true;
//load the website
this.get = function () {
browser.get('http://127.0.0.1:57828/index.html#/dashboard');
};
};
I think the better practice is to do this in your config.
onPrepare: function() {
browser.manage().window().setSize(1600, 1000);
}
or
onPrepare: function() {
browser.manage().window().maximize();
}
You already have this line in your beforeAll function, which will maximize your browser before any test is run:
browser.driver.manage().window().maximize();
However, on some operating systems, Chrome browser won't maximize the window horizontally, only vertically. You have two options:
a) Set the width explicitly to some predefined value, e.g:
browser.driver.manage().window().setSize(1200, 768);
b) Get the screen resolution with a Javascript function and set the window size accordingly.
var width = GET_WIDTH;
var height = GET_HEIGHT;
browser.driver.manage().window().setSize(width, height);
When I'm getting the list of results in my cordova app, i want to roll it to the specified element with this function:
$scope.roll = function () {
//var rankScroll = $ionicScrollDelegate.$getByHandle('rank');
var meElement = document.getElementById('scroll');
if (!meElement) {
$ionicScrollDelegate.scrollTo(0, 0);
return;
}
var top = meElement.getBoundingClientRect().top - 50;
$ionicScrollDelegate.scrollTo(0, top);
console.log(top);
console.log($ionicScrollDelegate.getScrollView());
}
It works nicely, but I can't scroll to any other place in the list. I want to unlock scrolling in this solution or find better one. It should scroll on page loaded, not on click.
All the best
That function lock your scroll because of the $scope's binding.
If you only want that function to be invoked when the view is loaded, you should call it once at the the time the view is loaded.
You can do that by this way in your controller:
var roll = function () {
//var rankScroll = $ionicScrollDelegate.$getByHandle('rank');
var meElement = document.getElementById('scroll');
if (!meElement) {
$ionicScrollDelegate.scrollTo(0, 0);
return;
}
var top = meElement.getBoundingClientRect().top - 50;
$ionicScrollDelegate.scrollTo(0, top);
console.log(top);
console.log($ionicScrollDelegate.getScrollView());
}
$scope.$on("$ionicView.loaded", function (scopes, states) {
roll();
});
You can check more $ionicView events in this ion-view Document
I'd like to make a full screen (or a part of screen) text area that user can scroll on iPad.
Something similar to the Notes.app: there's a screen/panel that can contain a lot of text, and it is scrollable.
If you tap on it, you can edit the text.
Is there any suitable component or workaround?
Thanks!
As far as I know, TextArea in Sencha Touch doesn't have this "scrollable" property.
In my app I used a workaround, overriding the TextArea component, here is the code:
Ext.define('Ext.overrides.TextArea', {
override: 'Ext.form.TextArea',
initialize: function() {
this.callParent();
this.element.dom.addEventListener(
Ext.feature.has.Touch ? 'touchstart' : 'mousedown',
this.handleTouchListener = Ext.bind(this.handleTouch, this),
false);
this.element.dom.addEventListener(
Ext.feature.has.Touch ? 'touchmove' : 'mousemove',
this.handleMoveListener = Ext.bind(this.handleMove, this),
false);
this.moveListenersAttached = true;
},
destroy: function() {
// cleanup event listeners to avoid memory leak
if (this.moveListenersAttached) {
this.moveListenersAttached = false;
this.element.dom.removeEventListener(
Ext.feature.has.Touch ? 'touchstart' : 'mousedown',
this.handleTouchListener,
false);
this.element.dom.removeEventListener(
Ext.feature.has.Touch ? 'touchmove' : 'mousemove',
this.handleMoveListener,
false);
this.handleTouchListener = this.handleMoveListener = null;
};
this.callParent();
},
handleTouch: function(e) {
this.lastY = e.pageY;
},
handleMove: function(e) {
var textArea = e.target;
var top = textArea.scrollTop <= 0;
var bottom = textArea.scrollTop + textArea.clientHeight >= textArea.scrollHeight;
var up = e.pageY > this.lastY;
var down = e.pageY < this.lastY;
this.lastY = e.pageY;
// default (mobile safari) action when dragging past the top or bottom of a scrollable
// textarea is to scroll the containing div, so prevent that.
if((top && up) || (bottom && down)) e.preventDefault();
// Sencha disables textarea scrolling on iOS by default,
// so stop propagating the event to delegate to iOS.
if(!(top && bottom)) e.stopPropagation();
}
});
I am trying to use Angular ng-grid to show some dynamic content. The grid will have 0 - 25 rows. I want to have a minimum size and then have the ng-grid auto adjust the height as items get added to the array. For some reason the auto size stops after adding 6-7 items (it even seems to depend on your resolution).
Can anyone help? What am I missing? I have a Plunker below that shows the issue.
http://plnkr.co/edit/frHbLTQ68GjMgzC59eSZ?p=preview
If you are still having issues I would suggestion not using the ng-grid and create your layout directly in html (Use DIVs, column width formatting, styles, etc...). Then populate your new layout using your $scope. Between variables, models and ng-repeat you should be able to do everything you need.
The code bellow is a modification to the plugin. It works by checking number of items in the grid data and calculating the height based on that. The options passed in to the plugin are height of row and header height.
ngGridCustomFlexibleHeightPlugin = function (opts) {
var self = this;
self.grid = null;
self.scope = null;
self.init = function (scope, grid, services) {
self.domUtilityService = services.DomUtilityService;
self.grid = grid;
self.scope = scope;
var recalcHeightForData = function () { setTimeout(innerRecalcForData, 1); };
var innerRecalcForData = function () {
var gridId = self.grid.gridId;
var footerPanelSel = '.' + gridId + ' .ngFooterPanel';
var extraHeight = self.grid.$topPanel.height() + $(footerPanelSel).height();
var naturalHeight = (grid.data.length - 1) * opts.rowHeight + opts.headerRowHeight;
self.grid.$viewport.css('height', (naturalHeight + 2) + 'px');
self.grid.$root.css('height', (naturalHeight + extraHeight + 2) + 'px');
// self.grid.refreshDomSizes();
if (!self.scope.$$phase) {
self.scope.$apply(function () {
self.domUtilityService.RebuildGrid(self.scope, self.grid);
});
}
else {
// $digest or $apply already in progress
self.domUtilityService.RebuildGrid(self.scope, self.grid);
}
};
scope.$watch(grid.config.data, recalcHeightForData);
};
};
I find using this piece of code on the stylesheet solved my problem. I disabled the plugin but it works either way.
.ngViewport.ng-scope{
height: auto !important;
overflow-y: hidden;
}
.ngTopPanel.ng-scope, .ngHeaderContainer{
width: auto !important;
}
.ngGrid{
background-color: transparent!important;
}
I hope it helps someone
In my mobile safari project, i need to create a message posting feature. it is requires scrolling inside a textarea when lines of texts exceed the max rows of the text area. i couldn't find 'scrollable' property in Ext.field.textarea, any idea how?
Cheers!
There is a bug in touch 2.0.x such that the framework explicitly prevents the scroll action. Supposedly a fix will be in 2.1, though I didn't see that officially, just from a guy on a forum.
Until then, there is kind of a solution for touch1 here http://www.sencha.com/forum/showthread.php?180207-TextArea-scroll-on-iOS-not-working that you can port to V2. It basically involves adding an eventlistener to the actual textarea field (not the sencha object) and then calling preventdefault if it's a valid scrollevent.
The full code is at that link, but the salient bits are here.
Grab the <textarea> field (not the Sencha Touch object) directly and use addListener to apply
'handleTouch' on touchstart and 'handleMove' on touchmove
handleTouch: function(e) {
this.lastY = e.pageY;
},
handleMove: function(e) {
var textArea = e.target;
var top = textArea.scrollTop <= 0;
var bottom = textArea.scrollTop + textArea.clientHeight >= textArea.scrollHeight;
var up = e.pageY > this.lastY;
var down = e.pageY < this.lastY;
this.lastY = e.pageY;
// default (mobile safari) action when dragging past the top or bottom of a scrollable
// textarea is to scroll the containing div, so prevent that.
if((top && up) || (bottom && down)) {
e.preventDefault();
e.stopPropagation(); // this tops scroll going to parent
}
// Sencha disables textarea scrolling on iOS by default,
// so stop propagating the event to delegate to iOS.
if(!(top && bottom)) {
e.stopPropagation(); // this tops scroll going to parent
}
}
Ext.define('Aspen.util.TextArea', {
override: 'Ext.form.TextArea',
adjustHeight: Ext.Function.createBuffered(function (textarea) {
var textAreaEl = textarea.getComponent().input;
if (textAreaEl) {
textAreaEl.dom.style.height = 'auto';
textAreaEl.dom.style.height = textAreaEl.dom.scrollHeight + "px";
}
}, 200, this),
constructor: function () {
this.callParent(arguments);
this.on({
scope: this,
keyup: function (textarea) {
textarea.adjustHeight(textarea);
},
change: function (textarea, newValue) {
textarea.adjustHeight(textarea);
}
});
}
});