2012-10-08 44 views
33

Tôi có một biến phạm vi $ scope.first_unread_id được xác định trong bộ điều khiển của tôi. Trong mẫu của tôi, tôi có:Chỉ thị AngularJS để cuộn đến một mục nhất định

<div id="items" > 
    <ul class="standard-list"> 
    <li ng-repeat="item in items" scroll-to-id="first_unread_id"> 
    <span class="content">{{ item.content }}</span> 
    </li> 
    </ul> 
</div> 

và chỉ thị của tôi trông giống như:

angular.module('ScrollToId', []). 
directive('scrollToId', function() { 
    return function (scope, element, attributes) { 
    var id = scope.$parent[attributes["scrollToId"]]; 
    if (id === scope.item.id) { 
     setTimeout(function() { 
     window.scrollTo(0, element[0].offsetTop - 100) 
     }, 20); 
    } 
    } 

}); 

nó hoạt động, tuy nhiên, hai câu hỏi:

  1. Có cách nào tốt hơn về nhận được "first_unread_id" ra khỏi phạm vi điều khiển vào trực tiếp hơn so với phạm vi thẩm vấn. $ parent? Điều này có vẻ hơi 'icky'. Tôi đã hy vọng tôi có thể vượt qua điều đó thông qua quan điểm trực tiếp như là một tham số w/o phải lặp lại rằng trên yếu tố bao giờ li.

  2. Có cách nào tốt hơn để tránh sự cần thiết của cuộc gọi setTimeout() không? Nếu không có nó, nó hoạt động đôi khi - Tôi tưởng tượng do sự khác biệt về thời gian bố trí. Tôi hiểu cú pháp tôi đã sử dụng là định nghĩa một hàm liên kết - nhưng nó không rõ ràng đối với tôi nếu đó là tiền tố hoặc liên kết sau theo mặc định - và nếu điều đó thậm chí còn quan trọng đối với vấn đề của tôi.

+0

https://docs.angularjs.org/api/ng/service/$anchorScroll – Blazemonger

+0

có bạn kiểm tra ra khỏi AnchorScroll của Angular? [AnchorScroll Service] (https://docs.angularjs.org/api/ng/service/$anchorScroll) –

Trả lời

39
  1. Bạn không nên cần $ cha mẹ phạm vi -. Vì nó sẽ kế thừa các giá trị từ phạm vi phụ huynh, và khi nó thay đổi trong phạm vi cha mẹ nó sẽ được chuyển xuống.
  2. Giá trị mặc định là chức năng sau liên kết. Bạn có một số hình ảnh hoặc tải gì đó có thể làm thay đổi bố cục trang ngay sau khi tải ban đầu không? Bạn đã thử một setTimeout không có thời gian trên nó, ví dụ setTimeout (function() {})? Điều này sẽ đảm bảo điều này sẽ đi 'một sau' tất cả mọi thứ khác được thực hiện.
  3. Tôi cũng sẽ thay đổi logic của chỉ thị của bạn một chút để làm cho nó tổng quát hơn. Tôi sẽ làm cho nó cuộn đến phần tử nếu điều kiện đã cho là đúng.

Dưới đây là những 3 thay đổi:

html:

<div id="items" > 
    <ul class="standard-list"> 
    <li ng-repeat="item in items" scroll-if="item.id == first_unread_id"> 
     <span class="content">{{ item.content }}</span> 
    </li> 
    </ul> 
</div> 

JS:

app.directive('scrollIf', function() { 
    return function (scope, element, attributes) { 
    setTimeout(function() { 
     if (scope.$eval(attributes.scrollIf)) { 
     window.scrollTo(0, element[0].offsetTop - 100) 
     } 
    }); 
    } 
}); 
+4

Tại sao không sử dụng $ timeout? Ngoài ra, tại sao $ eval thay vì $ parse? Chẳng phải phân tích $ sẽ là góc-y hơn sao? – alalonde

+0

Chúng tôi đang làm một điều một lần ở đây, vì vậy $ eval dường như hoạt động. $ parse thường được sử dụng để phân tích cú pháp một cái gì đó sau đó lưu nó để sử dụng nhiều lần sau đó. Và $ timeout thường được sử dụng để gọi một digest khi nó cháy, mà chúng tôi không thực sự cần ở đây. –

+6

$ timeout (fn, delay, false) - và không kiểm tra bẩn! –

11

Giả sử rằng các yếu tố phụ huynh là một trong những nơi mà chúng tôi di chuyển, công trình này cho tôi :

app.directive('scrollIf', function() { 
    return function(scope, element, attrs) { 
    scope.$watch(attrs.scrollIf, function(value) { 
     if (value) { 
     // Scroll to ad. 
     var pos = $(element).position().top + $(element).parent().scrollTop(); 
     $(element).parent().animate({ 
      scrollTop : pos 
     }, 1000); 
     } 
    }); 
    } 
}); 
+0

Cảm ơn '$ watch()', trong trường hợp của tôi, điều kiện được đặt động. –

6

Tôi đã kết thúc với mã sau (không phụ thuộc vào jQ) cũng hoạt động nếu phần tử cuộn không phải là cửa sổ.

app.directive('scrollIf', function() { 
    var getScrollingParent = function(element) { 
     element = element.parentElement; 
     while (element) { 
      if (element.scrollHeight !== element.clientHeight) { 
       return element; 
      } 
      element = element.parentElement; 
     } 
     return null; 
    }; 
    return function (scope, element, attrs) { 
     scope.$watch(attrs.scrollIf, function(value) { 
      if (value) { 
       var sp = getScrollingParent(element[0]); 
       var topMargin = parseInt(attrs.scrollMarginTop) || 0; 
       var bottomMargin = parseInt(attrs.scrollMarginBottom) || 0; 
       var elemOffset = element[0].offsetTop; 
       var elemHeight = element[0].clientHeight; 

       if (elemOffset - topMargin < sp.scrollTop) { 
        sp.scrollTop = elemOffset - topMargin; 
       } else if (elemOffset + elemHeight + bottomMargin > sp.scrollTop + sp.clientHeight) { 
        sp.scrollTop = elemOffset + elemHeight + bottomMargin - sp.clientHeight; 
       } 
      } 
     }); 
    } 
}); 
1

Kết hợp với giao diện người dùng Router của $uiViewScroll tôi đã kết thúc với chỉ thị sau:

app.directive('scrollIf', function ($uiViewScroll) { 
    return function (scope, element, attrs) { 
     scope.$watch(attrs.scrollIf, function(value) { 
      if (value) { 
       $uiViewScroll(element); 
      } 
     }); 
    } 
}); 
0

Trong kết hợp với @uri, công trình này cho nội dung động của tôi với ui-router và stateChangeSuccess trong.chạy:

$rootScope.$on('$stateChangeSuccess',function(newRoute, oldRoute){ 

     setTimeout(function() { 
      var postScroll = $state.params.postTitle; 
      var element = $('#'+postScroll); 
      var pos = $(element).position().top - 100 + $(element).parent().scrollTop(); 
      $('body').animate({ 
       scrollTop : pos 
      }, 1000); 
     }, 1000); 

    }); 
3

Tương tự như câu trả lời được chấp nhận, nhưng sử dụng javascript tích hợp trong phương pháp "scrollIntoView":

angular.module('main').directive('scrollIf', function() { 
    return function(scope, element, attrs) { 
     scope.$watch(attrs.scrollIf, function(value) { 
      if (value) { 
       element[0].scrollIntoView({block: "end", behavior: "smooth"}); 
      } 
     }); 
    } 
}); 
0

Đối với một câu trả lời chụp tốt nhất của các câu trả lời ở đây, trong ES6:

file: scroll.directive.js

export default function ScrollDirective() { 
    return { 
     restrict: 'A', 
     scope: { 
      uiScroll: '=' 
     }, 
     link: link 
    }; 

    function link($scope, $element) { 
     setTimeout(() => { 
      if ($scope.uiScroll) { 
       $element[0].scrollIntoView({block: "end", behavior: "smooth"}); 
      } 
     }); 
    } 
} 

tập tin scroll.module.js

import ScrollDirective from './scroll.directive'; 

export default angular.module('app.components.scroll', []) 
    .directive('uiScroll', ScrollDirective); 

Sau khi nhập nó trong dự án của bạn, bạn có thể sử dụng nó trong html của bạn:

<div id="items" > 
    <ul class="standard-list"> 
    <li ng-repeat="item in items" ui-scroll="true"> 
    <span class="content">{{ item.content }}</span> 
    </li> 
    </ul> 
</div> 
Các vấn đề liên quan