如何在AngularJS中继承另一个指令的行为?

时间:2023-01-05 20:13:44

This is really a rather generic question, but as people on IRC appeared to have issues understanding what I was trying to do, I've provided some rather specific information in this post. The answer would likely be applicable to a far wider range of issues than just this specific one.

这确实是一个相当普遍的问题,但是由于IRC上的人似乎有理解我想要做的事情,我在这篇文章中提供了一些相当具体的信息。答案可能适用于更广泛的问题,而不仅仅是这个问题。

I'm looking to have one directive inherit behaviour of another directive, while also providing behaviour of its own. Below is an example case.

我希望有一个指令继承另一个指令的行为,同时也提供自己的行为。以下是一个示例案例。

Say I have the following document:

说我有以下文件:

<body ng-app="AppCore" dock-container>
    <pane dock="left" class="one">
        left
    </pane>
    <pane dock="fill" class="two" title="middle">
        <pane dock="top" class="four" height="100">
            middle top
        </pane>
        <pane dock="fill" class="five">
            middle fill
        </pane>
    </pane>
    <pane dock="right" class="three" title="whatever">
        right
    </pane>
</body>

Here, I have three different directives:

在这里,我有三个不同的指令:

  • pane: An element directive. This represents a UI element (a pane or 'panel', as the name implies).
  • pane:元素指令。这表示UI元素(窗格或“面板”,顾名思义)。
  • dock-container: An attribute directive. This acts as a modifier; it adds some bookkeeping data to the scope, and marks it as being a 'container'.
  • dock-container:属性指令。这充当修饰语;它将一些簿记数据添加到范围,并将其标记为“容器”。
  • dock: An attribute directive. This also acts as a modifier; it indicates that the element should be positioned relative to the nearest dock-container parent - 'docking' here refers to making the element 'stick' to a side of the nearest container (or fill up the remaining space).
  • dock:属性指令。这也可以作为修饰语;它表示元素应相对于最近的dock-container parent定位 - 这里的'docking'是指使元素“粘贴”到最近容器的一侧(或填满剩余空间)。

Now, the problem is that a pane should also implicitly be a dock-container - that is, it should behave exactly the same as an element with the dock-container directive would, in addition to pane-specific behaviour.

现在,问题是窗格也应该隐式地是一个dock-container - 也就是说,除了特定于窗格的行为之外,它应该与具有dock-container指令的元素完全相同。

However, in the interest of clean markup, I don't want to have to explicitly specify dock-container on every pane - it should implicitly inherit from dock-container without having to actually specify the dock-container directive.

但是,为了清理标记,我不希望必须在每个窗格上显式指定dock-container - 它应该隐式地从dock-container继承,而不必实际指定dock-container指令。

In other words, I'm looking to make my pane directive behave as if it were like this:

换句话说,我希望我的窗格指令的行为就像它是这样的:

<pane dock-container>

... while it actually says this in the document:

......虽然它在文件中实际上是这样说的:

<pane>

I don't particularly care how the pane directive inherits its behaviour from the dock-container directive, even if it means having the pane directive add the dock-container directive itself before Angular finishes processing the element. Ideally, the link function of the dock-container directive, not just the controller, would also be applied to the pane.

我并不特别关心窗格指令如何从dock-container指令继承其行为,即使它意味着在Angular完成处理元素之前让窗格指令添加dock-container指令本身。理想情况下,dock-container指令的链接功能(而不仅仅是控制器)也将应用于窗格。

I am currently using element.parent().controller('dockContainer') to find the nearest dock-container from a dock directive - for this to work, the controller that I've defined for the dock-controller directive would have to be present on the pane element, regardless of the method of inheritance.

我目前正在使用element.parent()。controller('dockContainer')从dock指令中找到最近的dock-container - 为此工作,我为dock-controller指令定义的控制器必须是无论继承方法如何,都显示在窗格元素上。

If your suggested inheritance implementation requires a different method of finding the nearest dock-container (or pane), that's fine too - the problem here is just the inheritance, the rest can be changed to accommodate the solution to this problem.

如果您建议的继承实现需要一种不同的方法来查找最近的dock-container(或窗格),那也没关系 - 这里的问题只是继承,其余的可以更改以适应此问题的解决方案。

The following are not suitable options:

以下不适合选择:

  • Copy-pasting the code for dock-container to the pane directive. I want to keep code duplication to a minimum, and it's quite possible that other directives in the future should also inherit from dock-container.
  • 将dock-container的代码复制粘贴到窗格指令。我希望将代码重复保持在最低限度,并且将来其他指令很可能也应该从dock-container继承。
  • Manually adding dock-container to every pane element - this is exactly what I'm trying to avoid. The code I'm working on is meant to be used as a reusable set of directives, and I aim to keep the syntax as clean as possible. Every pane should inherit the dock-container behaviour, so there's no added value in requiring it to be specified explicitly.
  • 手动将dock-container添加到每个窗格元素 - 这正是我想要避免的。我正在处理的代码旨在用作可重用的指令集,我的目标是尽可能保持语法清晰。每个窗格都应该继承dock-container行为,因此要求明确指定它没有任何附加值。
  • Anything that overrides the behaviour for the pane directive. The dock-container behaviour should be an addition, not a replacement. The pane directive will also have some specific behaviour of its own, that the dock-container directive itself does not have.
  • 任何覆盖窗格指令行为的内容。 dock-container行为应该是一个补充,而不是替代。窗格指令还将具有自己的一些特定行为,即dock-container指令本身没有。
  • Anything that makes it impossible for an element to have both a dock and dock-container directive - the search for the 'nearest dock-container' starts at the parent of the element, so an element will never be its own dock-container.
  • 任何使元素不可能同时具有dock和dock-container指令的东西 - 搜索'nearest dock-container'从元素的父元素开始,因此元素永远不会是它自己的dock-container。
  • Anything that changes the type of element. The pane element should stay a pane element.
  • 任何改变元素类型的东西。窗格元素应保持窗格元素。

3 个解决方案

#1


2  

As a simpler alternative to my other answer, if you don't need there to be a pane element in the final rendered DOM, and no other directive has a template on the element or uses transclusion, then you can just use standard transclusion to dynamically add the dock-container directive:

作为我的其他答案的一个更简单的替代方案,如果您不需要在最终呈现的DOM中有窗格元素,并且没有其他指令在元素上有模板或使用转换,那么您可以只使用标准转换来动态添加dock-container指令:

app.directive('pane', function($compile) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '<div dock-container ng-transclude></div>',
    link: function() {
      // Any special behaviour for a pane element
    }
  };
});

As seen in this Plunker

正如这个Plunker所见

#2


0  

even if it means having the pane directive add the dock-container directive itself before Angular finishes processing the element

即使这意味着让窗格指令在Angular完成处理元素之前添加dock-container指令

You can do this by

你可以这样做

  • Setting a very high priority to the pane directive
  • 为pane指令设置一个非常高的优先级
  • Set it to terminal: true to stop other directives being compiled after this one
  • 将其设置为terminal:true以停止在此之后编译的其他指令
  • In the linking function add dock-container attribute, and then recompile the element, passing the priority of the pane directive as the third parameter. This will allow all other directives with a lower priority, such as dock-container, and any other directive on the element, to run.
  • 在链接函数中添加dock-container属性,然后重新编译该元素,将pane指令的优先级作为第三个参数传递。这将允许运行具有较低优先级的所有其他指令,例如dock-container和元素上的任何其他指令。

Example code of this is as follows

其示例代码如下

app.directive('pane', function($compile) {
  return {
    restrict: 'E',
    priority: 9999,
    terminal: true,
    link: function(scope, element, attrs, controllers, transclude) {
      element.attr('dock-container', '');
      $compile(element, null, 9999)(scope);
    }
  };
});

An example of this can be seen here

这里可以看到一个例子

I've seen a few other suggestions about how to dynamically adding directives to elements, but this way minimises DOM manipulation, and as long as this really is the top priority directive on the element, it ensures that other directives on the same element only get compiled once, after the dynamic directive has been added.

我已经看到了一些关于如何动态地向元素添加指令的其他建议,但是这样可以最大限度地减少DOM操作,只要这确实是元素的最高优先级指令,它就可以确保同一元素上的其他指令只获得在添加动态指令之后编译一次。


As as sidenote/recommendation, unless I had a good reason too, in order to KISS I would just have both pane and dock-container explicitly on the same element. Yes, the template is a bit longer, but it explicitly shows what directives are in play on the element, and the interaction between the 2 directives is much more standard.

作为旁注/推荐,除非我有充分的理由,为了KISS,我会在同一元素上明确地同时使用pane和dock-container。是的,模板有点长,但它明确地显示了元素上正在使用的指令,并且2指令之间的交互更加标准。

#3


0  

You can try using a template with '<dock-container/>' within your pane directive defintion...

您可以尝试在窗格指令定义中使用带有“ ”的模板...

{
    template: '<dock-container/>',
    replace: true,
}

And if dockContainer exposes a controller, you can reference it in your dock directive something like...

如果dockContainer公开了一个控制器,你可以在你的dock指令中引用它...

{
    template: '<dock-container/>',
    replace: true,
    require: '^dockContainer',
    link: function ($scope, $element, $attrs, dockContainerController) {
    }
}

But please consult the docs for the appropriate require syntax, ie the ^.

但请查阅文档以获取相应的require语法,即^。

#1


2  

As a simpler alternative to my other answer, if you don't need there to be a pane element in the final rendered DOM, and no other directive has a template on the element or uses transclusion, then you can just use standard transclusion to dynamically add the dock-container directive:

作为我的其他答案的一个更简单的替代方案,如果您不需要在最终呈现的DOM中有窗格元素,并且没有其他指令在元素上有模板或使用转换,那么您可以只使用标准转换来动态添加dock-container指令:

app.directive('pane', function($compile) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '<div dock-container ng-transclude></div>',
    link: function() {
      // Any special behaviour for a pane element
    }
  };
});

As seen in this Plunker

正如这个Plunker所见

#2


0  

even if it means having the pane directive add the dock-container directive itself before Angular finishes processing the element

即使这意味着让窗格指令在Angular完成处理元素之前添加dock-container指令

You can do this by

你可以这样做

  • Setting a very high priority to the pane directive
  • 为pane指令设置一个非常高的优先级
  • Set it to terminal: true to stop other directives being compiled after this one
  • 将其设置为terminal:true以停止在此之后编译的其他指令
  • In the linking function add dock-container attribute, and then recompile the element, passing the priority of the pane directive as the third parameter. This will allow all other directives with a lower priority, such as dock-container, and any other directive on the element, to run.
  • 在链接函数中添加dock-container属性,然后重新编译该元素,将pane指令的优先级作为第三个参数传递。这将允许运行具有较低优先级的所有其他指令,例如dock-container和元素上的任何其他指令。

Example code of this is as follows

其示例代码如下

app.directive('pane', function($compile) {
  return {
    restrict: 'E',
    priority: 9999,
    terminal: true,
    link: function(scope, element, attrs, controllers, transclude) {
      element.attr('dock-container', '');
      $compile(element, null, 9999)(scope);
    }
  };
});

An example of this can be seen here

这里可以看到一个例子

I've seen a few other suggestions about how to dynamically adding directives to elements, but this way minimises DOM manipulation, and as long as this really is the top priority directive on the element, it ensures that other directives on the same element only get compiled once, after the dynamic directive has been added.

我已经看到了一些关于如何动态地向元素添加指令的其他建议,但是这样可以最大限度地减少DOM操作,只要这确实是元素的最高优先级指令,它就可以确保同一元素上的其他指令只获得在添加动态指令之后编译一次。


As as sidenote/recommendation, unless I had a good reason too, in order to KISS I would just have both pane and dock-container explicitly on the same element. Yes, the template is a bit longer, but it explicitly shows what directives are in play on the element, and the interaction between the 2 directives is much more standard.

作为旁注/推荐,除非我有充分的理由,为了KISS,我会在同一元素上明确地同时使用pane和dock-container。是的,模板有点长,但它明确地显示了元素上正在使用的指令,并且2指令之间的交互更加标准。

#3


0  

You can try using a template with '<dock-container/>' within your pane directive defintion...

您可以尝试在窗格指令定义中使用带有“ ”的模板...

{
    template: '<dock-container/>',
    replace: true,
}

And if dockContainer exposes a controller, you can reference it in your dock directive something like...

如果dockContainer公开了一个控制器,你可以在你的dock指令中引用它...

{
    template: '<dock-container/>',
    replace: true,
    require: '^dockContainer',
    link: function ($scope, $element, $attrs, dockContainerController) {
    }
}

But please consult the docs for the appropriate require syntax, ie the ^.

但请查阅文档以获取相应的require语法,即^。