Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Different uiScrollViewport schemes when using as a component #239

Open
ornic opened this issue Apr 13, 2020 · 4 comments
Open

Different uiScrollViewport schemes when using as a component #239

ornic opened this issue Apr 13, 2020 · 4 comments

Comments

@ornic
Copy link

ornic commented Apr 13, 2020

I am trying to use great ui-scroll module inside one of the components. This is a (surprise!) list, but i need to show it both inside window (no viewport) or inside popup (with viewport).

The only way I know to tell ui-scroll about viewport is ui-scroll-viewport attribute. And the only way I can tell Angular about optional attribute is ng-attr- prefix. I want it to be optional attribute, since I hate to copy-paste the same code in ng-switches.

And this is a place when things get interesting. According to angular/angular.js#16441 ui-scroll gets information about ui-scroll-viewport attribute before evaluation of ng-attr-ui-scroll-viewport value.

May be someone will point me to more "right" solution. But for now I had to patch ui-scroll.js file.

  1. I send the max-height style inside some param to the component
    <ul class="list-group" style="overflow-x: hidden" ng-attr-ui-scroll-viewport="$ctrl.checkRestrictHeight()" ng-style="{'min-height': '1px', 'max-height': $ctrl.checkRestrictHeight()}">

        <a class="list-group-item" ui-scroll="survey in $ctrl.itemsSource" start-index="$ctrl.startPosition" buffer-size="30" adapter="$ctrl.scroll"
...
  1. I check the result of $eval for ng-attr-ui-scroll-viewport value
function Viewport(elementRoutines, buffer, element, viewportController, $rootScope, padding) {
  var topPadding = null;
  var bottomPadding = null;

    //ADD

    let ngAttrEval = true;
    if (viewportController && viewportController.viewport && viewportController.viewport[0].attributes && viewportController.viewport[0].attributes.hasOwnProperty('ng-attr-ui-scroll-viewport') && !viewportController.viewport[0].attributes.hasOwnProperty('ui-scroll-viewport')) {
        ngAttrEval = false;
        if (viewportController.scope) {
            ngAttrEval = viewportController.scope.$eval(viewportController.viewport[0].attributes['ng-attr-ui-scroll-viewport'].value);
        }
    }

    //CHANGE

    var viewport = viewportController && viewportController.viewport && ngAttrEval ? viewportController.viewport : angular.element(window);
    var container = viewportController && viewportController.container && ngAttrEval ? viewportController.container : undefined;
    var scope = viewportController && viewportController.scope && ngAttrEval ? viewportController.scope : $rootScope;

...
@dhilt
Copy link
Member

dhilt commented Apr 15, 2020

@ornic If you are going to augment Viewport module, an easier approach might be as follows.

Template:

<ul class="list-group" ui-scroll-viewport="$ctrl.checkRestrictHeight()">

Directive:

.directive('uiScrollViewport', function () {
  return {
    restrict: 'A',
    scope: { window: '=uiScrollViewport' },   // new line, assign directive attr value to scope.window
    controller: [
    // ...

Viewport:

const ctrl = viewportController;
const isViewport = ctrl && ctrl.scope && ctrl.scope.window !== false;
const scope = isViewport ? ctrl.scope : $rootScope;
const viewport = isViewport && ctrl.viewport ? ctrl.viewport : angular.element(window);
const container = isViewport && ctrl.container ? ctrl.container : undefined;

Note, the explicit check is used: !== false -- it is necessary for the case when a user doesn't pass any value to the "ui-scroll-viewport" attribute but expects the Viewport to be present.

@ornic
Copy link
Author

ornic commented Apr 17, 2020

This way code looks more readable than in my crude variant. But I found that after defining scope in .directive it is unable to make an adaptor in viewportController.scope, but constantly setting viewport.scope to $rootScope fixed the problem (for me, with using ui-scroll via component). But I assume that in future (more usecases inside one app) this approach may backfire.

UPD: I looked further and it seems that this approach should be Ok, since the only result AFAIK is always using scope of uiScroll directive.

@dhilt
Copy link
Member

dhilt commented Apr 18, 2020

I didn't meet any issues with the Adapter when using the suggested approach.

@ornic
Copy link
Author

ornic commented Apr 23, 2020

I also found the very handy method of using full-window scroll: with some block of text before the list. But there is a problem on the way.

This is the easy correction of your example. This div goes before the list

<div style="
    height: 300px;
    text-align: center;
    border: 1px solid lightgreen;
    vertical-align: middle;
    padding: 100px;
">Hello! I'm 300px of height.</div>

and we limit the list with index -100.

And then we lose ability to correctly identify first and last element:
image

I'm trying to find the way to overcome this. :)

....

That was... interesting :))) As a result I adjusted *Pos functions with an idea to use container top offset and scrollHeight to compensate:

bottomDataPos: function bottomDataPos() {
    var scrollHeight = viewport[0].scrollHeight;
    scrollHeight = scrollHeight != null ? scrollHeight : viewport[0].document.documentElement.scrollHeight;
    let btmPadding = bottomPadding.height();
    if (container && container[0] != viewport[0]) { // the only possible case: viewport = window && container != window
        if (btmPadding > 0) { // consider space under the container
            return container[0].scrollHeight - btmPadding;
        }
        scrollHeight -= container.offset().top;
    }
    return scrollHeight - btmPadding;
},
topDataPos: function topDataPos() {
    let topDataPos = topPadding.height();
    return topDataPos;
},
bottomVisiblePos: function bottomVisiblePos() {
    let viewPortHeight = viewport.outerHeight();
    if (container && container[0] != viewport[0]) {
        let offset = container.offset().top - viewport.scrollTop();
        if (offset > 0) viewPortHeight -= offset;
    }
    return viewport.topVisiblePos() + viewPortHeight;
},
topVisiblePos: function topVisiblePos() {
    let topVisiblePos = viewport.scrollTop();
    if (container && container[0] != viewport[0]) {
        topVisiblePos -= container.offset().top;
        if (topVisiblePos < 0) topVisiblePos = 0; // no negative positions :)
    }
    return topVisiblePos;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants