GVKun编程网logo

AngularJS Transclusion Directive with ngRepeat导致混合范围

9

如果您对AngularJSTransclusionDirectivewithngRepeat导致混合范围感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于AngularJSTr

如果您对AngularJS Transclusion Directive with ngRepeat导致混合范围感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于AngularJS Transclusion Directive with ngRepeat导致混合范围的详细内容,并且为您提供关于AngualrJS中的Directive制作一个菜单_AngularJS、Angular 根据 service 的状态更新 directive_AngularJS、AngularJs Directive Pass value/object/function to Scope、angularjs directive semantic-ui pagination的有价值信息。

本文目录一览:

AngularJS Transclusion Directive with ngRepeat导致混合范围

AngularJS Transclusion Directive with ngRepeat导致混合范围

AngularJS有一个超级漂亮的ngTransclude指令,它用控制器模板中的html替换指令模板中的html.如果在指令中使用隔离范围,则可以使用ngTransclude从控制器范围访问变量.

当我最终看到一个看似随机的结果时,我试图这样做. ngRepeat中的ngTransclude从指令的作用域返回值,而不是控制器的作用域.

关闭AngularJS文档,我创建了一个Plunker:http://plnkr.co/edit/GtrYtGoy2fnvgkwLFAGN?p=preview

JS

angular.module('docsTransclusionExample',[])
  .controller('Controller',['$scope',function($scope) {
    $scope.names = ['Tobias','Funke'];
  }])
  .directive('myDialog',function() {
    return {
      restrict: 'E',transclude: true,scope: {},templateUrl: 'my-dialog.html',link: function (scope,element) {
        scope.names = ['Jeff','Bridges'];
      }
    };
  });

的index.html

<!doctype html>
<html lang="en">
<head>
  <Meta charset="UTF-8">
  <title>Example - example-example87-production</title>


  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
  <script src="script.js"></script>



</head>
<body ng-app="docsTransclusionExample">
    <div ng-controller="Controller">
    <my-dialog>Check out the contents,{{names}}!</my-dialog>
  </div>
</body>
</html>

我-dialog.html

<divng-transclude></div>
<div ng-repeat="name in names">
  <divng-transclude></div>
  {{name}}
</div>
<divng-transclude></div>

此代码返回:

Check out the contents,["Tobias","Funke"]!
Check out the contents,["Jeff","Bridges"]!
Jeff
Check out the contents,"Bridges"]!
Bridges
Check out the contents,"Funke"]!

根据我读过的文档,以及我发现的用于翻译和范围的翻译和范围文章,翻译只应该考虑到控制器的范围,意思是中间的两个“查看内容,{{names}}!”应该读与外面两个相同.

进一步的实验让我将AngularJS版本从1.2.15更改为1.3.0 rc

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.1/angular.min.js"></script>

这导致正确的(据我所知)输出:

Check out the contents,"Funke"]!
Jeff
Check out the contents,"Funke"]!
Bridges
Check out the contents,"Funke"]!

有没有针对此问题的解决方法,我可以使用1.2.15或我在使用此版本时卡住了?应该是什么样的正确行为?

解决方法

从1.3.0b11的 changelog:

ngRepeat: ensure that the correct (transcluded) scope is used
(b87e5fc0)

对于ng-if也是一样的(在你的plunker中在1.2.15和1.3.0-rc.1之间切换时有相同的奇怪的不同行为).

所以正确的是使用1.3.0-rc.1.

AngualrJS中的Directive制作一个菜单_AngularJS

AngualrJS中的Directive制作一个菜单_AngularJS

说下我经常写菜单的方式:

<ul>
<li data-ng->
<a href="#/orders">Orders</a>
</li>
</ul> 
登录后复制

菜单项是否高亮显示取决于controller中的highlight方法。

vm.highlight = funciton(path){
return $locaiton.path().substr(0, path.lenght) === path;
}
登录后复制

如果以Directive的方式会更简洁。

<ul menu-highlighter highlight-class-name="active">
<li><a href="#/customers">Customers</a></li>
<li><a href="#/orders">Customers</a></li>
<li><a href="#/about">Customers</a></li>
</ul> 
登录后复制

Directive大致是:

(function(){
var injectParams = [''$location''];
var menuHighlighter = function($location){
var link = function(scope, element){
function setActive(){
var path = $location.path();
var className = scope.highlightClassName || ''active'';
if(path){
angular.forEac(element.find(''li''), function(li){
//<a href="#/customers">Customers</a>
var anchor = li.querySelector(''a'');
//#/customers
var href=(anchor && anchor.href) &#63; anchor.href : anchor.getAttribute(''data-href'').replace(''#'','''');
//customers
var trimmedHref = href.substr(href.indexOf(''#/'')+1, href.length);
var basePath = path.substr(0, trimmedHref.length);
if(trimmedHref === basePath){
angular.element(li).addClass(className);
} else {
angular.element(li).removeClass(className);
}
});
} 
}
setActive();
scope.$on(''$locationChangeSuccess'', setActive);
};
return {
restrict: ''A'',
scope: {
highlightClassName: ''@''
},
link: link
}
};
menuHighlighter.$inject = injectParams;
angular.module(''my.directives'')
.directive(''menuHighlighter'', menuHighlighter);
}()); 
登录后复制

以上内容是针对AngualrJS中的Directive制作一个菜单的相关知识,希望对大家有所帮助。

Angular 根据 service 的状态更新 directive_AngularJS

Angular 根据 service 的状态更新 directive_AngularJS

angular js (angular.js) 是一组用来开发web页面的框架、模板以及数据绑定和丰富ui组件。它支持整个开发进程,提供web应用的架构,无需进行手工dom操作。

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了。这里AngularJS就应运而生,弥补了HTML的天然缺陷,用于构件Web应用等。

TL;DR

这篇文章讲解了三种根据 service 的状态更新 directive 的做法。分别是 $watch 表达式,事件传递,和 controller 的计算属性。

问题

我有一个 readerService ,其中包含一些状态信息(比如连接状态和电量)。现在我需要做一个 directive 去展示这些状态。因为它只需要从 readerService 中获取数据,不需要任何外部传值,所以我直接把 service 注入进去。但如何更新就成了一个问题。

service 的代码如下。

const STATUS = {
DETACH: ''DETACH'',
ATTACH: ''ATTACH'',
READY: ''READY''
}
class ReaderService {
constructor() {
this.STATUS = STATUS
// The status will be changed by some callbacks
this.status = STATUS.DETACH
}
}
angular.module(''app'').service(''readerService'', readerService)
登录后复制

directive 代码如下:

angular.module(''app'').directive(''readerIndicator'', (readerService) => {
const STATUS = readerService.STATUS
const STATUS_DISPLAY = {
[STATUS.DETACH]: ''Disconnected'',
[STATUS.ATTACH]: ''Connecting...'',
[STATUS.READY]: ''Connected'',
}
return {
restrict: ''E'',
scope: {},
template: `
<div>
{{statusDisplay}}
</div>
`,
link(scope) {
// Set and change scope.statusDisplay here
}
}
})
登录后复制

我尝试过以下几种办法,下面一一介绍。

方法一:$watch

第一个想到的方法就是在 directive 中用 $watch 去监视 readerService.status 。因为它不是 directive scope 的属性,所以我们需要用一个函数来包裹它。Angular 会在 dirty-checking 时计算和比较新旧值,只有状态真的发生了改变才会触发回调。

// In directive
link(scope) {
scope.$watch(() => readerService.status, (status) => {
scope.statusDisplay = STATUS_DISPLAY[status]
})
}
登录后复制

这个做法足够简单高效,只要涉及 readerService.status 改变的代码会触发 dirty-checking ,directive 就会自动更新。service 不需要修改任何代码。

但如果有多个 directive 的属性都受 service status 的影响,那 $watch 代码就看得比较晦涩了。尤其是 $watch 修改的值会影响其他的值的时候。比如:

// In directive
link(scope) {
scope.$watch(() => readerService.status, (status) => {
scope.statusDisplay = STATUS_DISPLAY[status]
scope.showBattery = status !== STATUS.DETACH
})
scope.$watch(''showBattery'', () => {
// some other things depend on showBattery
})
}
登录后复制

这种时候声明式的编程风格会更容易看懂,比如 Ember 或 Vue 里面的 computed property 。这个待会讨论。

方法二:$broadcast/$emit + $on

这种思路是 service 每次状态改变都发送一个事件,然后 directive 监听事件来改变状态。因为 directive 渲染的时候也许 status 已经更新了。所以我们需要在 link 中计算一个初始值。

我最开始是用 $broadcast 去做的。代码如下:

// In service
setStatus(value) {
this.status = value
// Need to inject $rootScope
this.$rootScope.$broadcast(''reader.statusChanged'', this.status)
}
// In directive
link(scope) {
scope.statusDisplay = STATUS_DISPLAY[nfcReaderService.status]
scope.$on(''reader.statusChanged'', (event, status) => {
scope.statusDisplay = STATUS_DISPLAY[status]
})
}
登录后复制

但马上发现 $broadcast 之后 UI 更新总要等 1 秒多(不过 $on 回调倒是很快)。Google 一番后知道原因是 $broadcast 是向下层所有 scope 广播,广播完成后再 dirty-checking 。一个更好的做法是使用 $emit ,它只会向上传递事件,不过不管发送事件还是监听事件都得用 $rootScope 。

修改后的代码如下:

// In service
setStatus(value) {
this.status = value
// Use $emit instead of $broadcast
this.$rootScope.$emit(''reader.statusChanged'', this.status)
}
// In directive
link(scope) {
scope.statusDisplay = STATUS_DISPLAY[nfcReaderService.status]
// Use $rootScope instead of scope
$rootScope.$on(''reader.statusChanged'', (event, status) => {
scope.statusDisplay = STATUS_DISPLAY[status]
})
}
登录后复制

如果因为某些原因不得不用 $broadcast 的话,你可以在 $on 回调最后用 $digest 或 $apply 强制触发 dirty-checking ,这也可以达到快速更新 UI 的目的。

方法三:controller + property

我个人觉得前两个方法能解决问题,但代码维护性都不太好。 $watch 在属性相互关联的情况下非常难看懂, $emit/$on 需要把一些逻辑写两次(初始化 directive 时和回调执行时)。方法一中我提到了有些时候声明式的属性比 $watch 更容易看懂。这个方法就是使用 controller 。directive 可以设置自己的 controller 作为数据来源(或者说 view model),我们可以把那些需要计算的属性作为 controller 的属性。这样 dirty-checking 时它们就会自动计算。

// In directive
class ReaderController {
constructor($scope, readerService) {
this.readerService = readerService
}
get statusDisplay() {
return STATUS_DISPLAY[this.readerService.status]
}
}
return {
// ...
controller: ReaderController,
controllerAs: ''vm'',
template: `
<div>
{{vm.statusDisplay}}
</div>
}
登录后复制

这样一来,大部分逻辑都可以挪到 controller 中。如果没有 DOM 操作我们甚至可以不写 link 方法。也没必要加入额外的 $watch 和 $on 。只是因为 dirty-checking 的特性,绑定到 template 的属性往往会多计算几次。所以属性必须非常简单。大部分情况下这不会有什么问题。

以上内容是小编给大家介绍的Angular 根据 service 的状态更新 directive,希望对大家有所帮助!

AngularJs Directive Pass value/object/function to Scope

AngularJs Directive Pass value/object/function to Scope

记录一下

@ 单向传递字符串的值。对于外层 scope 的值,可用 {{}} 形式。

= 与父 scope 中的属性进行双向绑定,通常用于对象,无需加 {{}}

& 传递来自父 scope 的函数

angularjs directive semantic-ui pagination

angularjs directive semantic-ui pagination

ui.semantic.pagination.js

(function (angular) {

    angular.module(''ui.semantic.pagination'', [])

    .controller(''PaginationController'', [''$scope'', ''$attrs'', ''$parse'', function ($scope, $attrs, $parse) {
        var self = this,
            ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
            setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;

        this.init = function (ngModelCtrl_, config) {
            ngModelCtrl = ngModelCtrl_;
            this.config = config;

            ngModelCtrl.$render = function () {
                self.render();
            };

            if ($attrs.itemsPerPage) {
                $scope.$parent.$watch($parse($attrs.itemsPerPage), function (value) {
                    self.itemsPerPage = parseInt(value, 10);
                    $scope.totalPages = self.calculateTotalPages();
                });
            } else {
                this.itemsPerPage = config.itemsPerPage;
            }
        };

        this.calculateTotalPages = function () {
            var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
            return Math.max(totalPages || 0, 1);
        };

        this.render = function () {
            $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
        };

        $scope.selectPage = function (page) {
            if ($scope.page !== page && page > 0 && page <= $scope.totalPages) {
                ngModelCtrl.$setViewValue(page);
                ngModelCtrl.$render();
            }
        };

        $scope.getText = function (key) {
            return $scope[key + ''Text''] || self.config[key + ''Text''];
        };
        $scope.noPrevious = function () {
            return $scope.page === 1;
        };
        $scope.noNext = function () {
            return $scope.page === $scope.totalPages;
        };

        $scope.$watch(''totalItems'', function () {
            $scope.totalPages = self.calculateTotalPages();
        });

        $scope.$watch(''totalPages'', function (value) {
            setNumPages($scope.$parent, value); // Readonly variable

            if ($scope.page > value) {
                $scope.selectPage(value);
            } else {
                ngModelCtrl.$render();
            }
        });
    }])

    .constant(''paginationConfig'', {
        itemsPerPage: 10,
        boundaryLinks: false,
        directionLinks: true,
        firstText: ''First'',
        previousText: ''Previous'',
        nextText: ''Next'',
        lastText: ''Last'',
        numDisplayEntries: 6, //连续分页主体部分分页条目数
        numEdgeEntries: 2,   //两侧首尾分页条目数
        rotate: true
    })

    .directive(''pagination'', [''$parse'', ''paginationConfig'', function ($parse, paginationConfig) {
        return {
            restrict: ''EA'',
            scope: {
                totalItems: ''='',
                firstText: ''@'',
                previousText: ''@'',
                nextText: ''@'',
                lastText: ''@''
            },
            require: [''pagination'', ''?ngModel''],
            controller: ''PaginationController'',
            templateUrl: ''/UserControls/pagination.html'',
            replace: true,
            link: function (scope, element, attrs, ctrls) {
                var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];

                if (!ngModelCtrl) {
                    return; // do nothing if no ng-model
                }

                // Setup configuration parameters
                var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,
                    rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;
                scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
                scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;

                var num_display_entries = angular.isDefined(attrs.numDisplayEntries) ? scope.$parent.$eval(attrs.numDisplayEntries) : paginationConfig.numDisplayEntries,
                    num_edge_entries = angular.isDefined(attrs.numEdgeEntries) ? scope.$parent.$eval(attrs.numEdgeEntries) : paginationConfig.numEdgeEntries;

                paginationCtrl.init(ngModelCtrl, paginationConfig);

                if (attrs.maxSize) {
                    scope.$parent.$watch($parse(attrs.maxSize), function (value) {
                        maxSize = parseInt(value, 10);
                        paginationCtrl.render();
                    });
                }

                // Create page object used in template
                function makePage(number, text, isActive) {
                    return {
                        number: number,
                        text: text,
                        active: isActive,
                        disabled: text == ''...'',
                    };
                }

                function getPages2(currentPage, totalPages) {
                    var ret = [];
                    var num_edge_entries = 2;
                    var np = totalPages;
                    var interval = getInterval(currentPage - 1, totalPages);

                    // Generate starting points
                    if (interval[0] > 0 && num_edge_entries > 0) {
                        var end = Math.min(num_edge_entries, interval[0]);
                        for (var i = 0; i < end; i++) {
                            var page = makePage(i + 1, i + 1, (i + 1) === currentPage);
                            ret.push(page);
                        }
                        if (num_edge_entries < interval[0]) {
                            var page = makePage(-1, ''...'', false);
                            ret.push(page);
                        }
                    }
                    // Generate interval links
                    for (var i = interval[0]; i < interval[1]; i++) {
                        var page = makePage(i + 1, i + 1, (i + 1) === currentPage);
                        ret.push(page);
                    }
                    // Generate ending points
                    if (interval[1] < np && num_edge_entries > 0) {
                        if (np - num_edge_entries > interval[1]) {
                            var page = makePage(-1, ''...'', false);
                            ret.push(page);
                        }
                        var begin = Math.max(np - num_edge_entries, interval[1]);
                        for (var i = begin; i < np; i++) {
                            var page = makePage(i + 1, i + 1, (i + 1) === currentPage);
                            ret.push(page);
                        }
                    }

                    return ret;
                }

                function getPages(currentPage, totalPages) {
                    var pages = [];

                    // Default page limits
                    var startPage = 1, endPage = totalPages;
                    var isMaxSized = (angular.isDefined(maxSize) && maxSize < totalPages);

                    // recompute if maxSize
                    if (isMaxSized) {
                        if (rotate) {
                            // Current page is displayed in the middle of the visible ones
                            startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
                            endPage = startPage + maxSize - 1;

                            // Adjust if limit is exceeded
                            if (endPage > totalPages) {
                                endPage = totalPages;
                                startPage = endPage - maxSize + 1;
                            }
                        } else {
                            // Visible pages are paginated with maxSize
                            startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;

                            // Adjust last page if limit is exceeded
                            endPage = Math.min(startPage + maxSize - 1, totalPages);
                        }
                    }

                    // Add page number links
                    for (var number = startPage; number <= endPage; number++) {
                        var page = makePage(number, number, number === currentPage);
                        pages.push(page);
                    }

                    // Add links to move between page sets
                    if (isMaxSized && !rotate) {
                        if (startPage > 1) {
                            var previousPageSet = makePage(startPage - 1, ''...'', false);
                            pages.unshift(previousPageSet);
                        }

                        if (endPage < totalPages) {
                            var nextPageSet = makePage(endPage + 1, ''...'', false);
                            pages.push(nextPageSet);
                        }
                    }

                    return pages;
                }

                /**
                * Calculate start and end point of pagination links depending on
                * currentPage and num_display_entries.
                * @return {Array}
                */
                function getInterval(currentPage, pageCount) {
                    //var num_display_entries = 6;
                    //var num_edge_entries = 2;

                    var ne_half = Math.ceil(num_display_entries / 2);
                    var np = pageCount;
                    var upper_limit = np - num_display_entries;
                    var start = currentPage > ne_half ? Math.max(Math.min(currentPage - ne_half, upper_limit), 0) : 0;
                    var end = currentPage > ne_half ? Math.min(currentPage + ne_half, np) : Math.min(num_display_entries, np);
                    return [start, end];
                }

                var originalRender = paginationCtrl.render;
                paginationCtrl.render = function () {
                    originalRender();
                    if (scope.page > 0 && scope.page <= scope.totalPages) {
                        scope.pages = getPages2(scope.page, scope.totalPages);
                    }
                };
            }
        };
    }]);

})(angular);

pagination.html

<div>
    <ang-data-ng-click="selectPage(page - 1)">
        <i></i>
    </a>
    <adata-ng-repeat="page in pages track by $index"
       data-ng-data-ng-click="selectPage(page.number)">
        {{page.text}}
    </a>
    <ang-data-ng-click="selectPage(page + 1)">
        <i></i>
    </a>
</div>

今天关于AngularJS Transclusion Directive with ngRepeat导致混合范围的讲解已经结束,谢谢您的阅读,如果想了解更多关于AngualrJS中的Directive制作一个菜单_AngularJS、Angular 根据 service 的状态更新 directive_AngularJS、AngularJs Directive Pass value/object/function to Scope、angularjs directive semantic-ui pagination的相关知识,请在本站搜索。

本文标签: