Angular指令-如何使用JQuery向自定义指令元素添加ngModel和ngBind?

4


[信息]
我想要实现的是创建自定义Angular指令,这些指令将封装所有必需的JS代码以使其正常工作。指令不知道要显示什么内容,也不知道从用户那里接收的任何输入值存储在哪里。这些信息将从指令的属性中获取。我的指令将使用父级$scop,而不会创建它们自己的$scope。

[问题] 由于指令不知道在$scope中映射ng-model和ng-bind的位置,所以我的方法是读取指令的属性,识别应该是 ng-model 和 ng-bing 属性,然后将它们设置为相应的元素。但是这种方法并没有生效。我认为这是由于我的知识不足,因此我在这里询问:我的方法是否正确?是否可能以这种方式设置ngModel和ngBind?我做错了什么?

[我的指令代码]

var directives = angular.module('test.directives', []);

directives.directive("labeledInput", function() {
return {
    restrict: 'E',
    scope: false,
    template: "<div>" +
                "<span class='label'></span>" +
                "<input class='input' type='text'></input>" +
              "</div>",

    link: function(scope, element) {
        var elementIdentifier = angular.element(element[0]).attr("idntfr");
        var elementClass = angular.element(element[0]).attr("element-class");
        var scopeValueName = angular.element(element[0]).attr("value-name");
        var defaultValue = angular.element(element[0]).attr("default-value");
        var elementLabel = angular.element(element[0]).attr("label");

        scope[scopeValueName] = defaultValue;
        scope[elementIdentifier] = elementLabel;

        $(angular.element(element[0]).children()[0]).attr('id', elementIdentifier);
        $(angular.element(element[0]).children()[0]).addClass(elementClass);
        $(angular.element(element[0]).children().children()[1]).attr('ng-model', scopeValueName);
        $(angular.element(element[0]).children().children()[0]).attr('ng-bind', elementIdentifier);
    }
};
});

[结果] 因此,我在HTML页面中看到ng-model和ng-bind绑定在正确的位置,在Batarang提供的作用域中有scope[scopeValueName]scope[elementIdentifier],但我在屏幕上没有看到它们作为值显示。
有人解决过类似的问题吗?
谢谢你的时间!
[编辑] 抱歉,似乎我的问题没有被理解,我会添加一些细节!
以下是我指令的示例HTML用法:
<labeled-input
    idntfr='id001'
    element-class='someClass'
    value-name='person_name'
    default-value='default'
    label='Person Name:'
>
</labeled-input> 

在Angular解析指令并执行它的任务后,我在浏览器中看到的是:
<div id="effect_dt" class="someClass">
    <span class="label" ng-bind="id001"></span>
    <input class="input" type="text" ng-model="person_name">
</div>

在我的控制器作用域中,我有 - $scope.id001 = "人名:"和$scope.person_name = 默认值。但是这些值根本没有显示在页面上。


@Yoshi,抱歉我的解释不够清晰。我添加了一些信息,希望现在更加清楚了。 - Milen Igrachev
3个回答

1

如果我理解你的意思正确,你想做类似于:

<labeledInput>model-name and/or field name</labeledInput>

并将其转换为类似以下的内容:
<div>
    <span class='label' ng-bind="mode-name.field-name"></span>
    <input class='input' type='text'></input>
</div>

您需要在指令中阅读更多有关Angular编译的内容,无论如何:

  • 为了获得对指令原始内容(例如属性或内部内容)的任何访问权限,您需要使用transclude。

  • 要访问模板内容(例如,在您的情况下,假设是'span'元素),您必须调用compile方法中的tElement参数,因为它将保存来自模板的HTML。

这里有一些很好的例子: Angularjs:transclude指令模板

所有这些操作,例如添加ng-model属性指令等,都应在指令编译之前添加,为此,您必须使用编译块(取代您的'link()'块):

.compile = function compile(tElement, tAttrs) {
    //here add some code eg append ng-model attribute etc.
    return {
    pre: function preLink(scope, iElement, iAttrs) {},
        post: function postLink(scope, iElement, iAttrs) {}
    }
}

最后一部分是控制器范围,最简单的方法就是在模板中添加控制器,这样您就不会失去任何作用域。

编辑:

阅读了您更新的问题后,我会试一试:p

app.directive('labeledInput', function($compile) {

  var directive = {};
  directive.transclude = true;
  directive.restrict =  'E';
  directive.template =  "<div>" +
                "<span class='label' ></span><br/>" +
                "<input class='input' type='text' ></input>" +
              "</div>";

  directive.compile =  function(cElem, cAttrs) {
    var scope=angular.element(cElem).scope();

    console.log(scope);

    var elementIdentifier = angular.element(cElem[0]).attr("idntfr");
    var elementClass = angular.element(cElem[0]).attr("element-class");
    var scopeValueName = angular.element(cElem[0]).attr("value-name");
    var defaultValue = angular.element(cElem[0]).attr("default-value");
    var elementLabel = angular.element(cElem[0]).attr("label");

     $(cElem[0]).find("div").attr('id', elementIdentifier).addClass(elementClass);

     $(cElem[0]).find("div span").attr('ng-bind', scopeValueName);
     $(cElem[0]).find("div input").attr('ng-model', elementIdentifier);
     return {
            pre: function preLink(scope, iElement, iAttrs) {
            scope[scopeValueName] = defaultValue;
      scope[elementIdentifier] = elementLabel;

            }
     };
    };

  return directive;
});

http://plnkr.co/edit/ImJmTHP3eQCzPaKfRTS4?p=preview

http://plnkr.co/edit/KIcfwsUGfCeg1UtioFv0?p=preview


我同意你的观点,我需要再多读一些。看起来你没有完全理解我的想法,但你给了我某种形式的答案。我将尝试使用编译函数而不是链接函数。感谢提供的信息!开始吧!(我编辑了问题,现在你可以更清楚地看到我想做什么了 :)) - Milen Igrachev
好的,就是这样!它正在工作,我相信我知道为什么了:),哈哈:)。感谢提供链接和示例! - Milen Igrachev

0

我不确定我是否理解正确,但我猜您想构建一些通用指令,其行为源自父级作用域并绑定到其模板,在这种情况下就是输入字段。

您可以在labeledInput指令上创建隔离范围,像这样:

scope: {
   doSomething:'&' 
}

它的模板将变成:

template: "<div>" +
            "<span class='label'></span>" +
            "<input class='input' type='text' ng-change="doSomething()"></input>" +
          "</div>",

而你的HTML将是:

    <labeledInput doSomething="someFunctionOnController()"></labeledInput>

因此,控制器作用域中定义的someFunctionOnController函数将在输入字段更改时被调用。


好的,不用父级作用域 : )。所有指令功能都应在其中编写。控制器作用域应为空,并且知道该指令只是将其包含在其依赖项列表中。但是,该指令将以安全的方式使用控制器作用域。 - Milen Igrachev
猜想,你应该纠正你问题中的第六行,伙计。 - viven
好的,我的上一个评论完全不准确。我确实想要使用父级作用域,但我不想让我的指令使用隔离作用域。因此,我只想要一个作用域 - 控制器作用域。然而,我不想在控制器内添加任何代码。因此,我需要通过指令的控制器/链接属性添加所有内容。 - Milen Igrachev

0

有一种更简单的方法可以做到这一点:(请参见http://jsfiddle.net/j55B8/17/上的jsfiddle)

指令的代码:

var directives = angular.module('test.directives', []);

directives.directive("labeledInput", function() 
    return {
        restrict : "E",
        replace : true,
        scope : {
            "idntfr" : "@",
            "elementClass" : "@",
            "valueName" : "=",
            "defaultValue": "@",
            "label" : "@"
        },
        template : "<div id='{{idntfr}}' class='{{elementClass}}' ng-init='valueName=defaultValue'>" +  
                       "<span class='label' ng-bind='label'></span>" +
                       "<input class='input' type='text' ng-model='valueName'></input>" +
                   "</div>"
    }
});

HTML

<labeled-input 
    idntfr='id001' 
    element-class='someClass' 
    value-name='person_name' 
    default-value='default name' 
    label='Person Name: '>
</labeled-input>

The value typed in the above text box is {{person_name}}

我同意这里的观点!这是实现我的目标的好方法,但对于我来说,必须使用无作用域指令而不是隔离作用域指令。但是对于任何阅读此内容的人-这个想法完全可行,也许这就是你的答案 :)!谢谢。 - Milen Igrachev
如果您有一个无范围指令,那么所有的labeled-input字段都将无法维护各自字段的绑定。例如,如果您在表单上有10个labeled-input字段,每个字段都有不同的value-name,则这10个字段中的所有字段都只使用在最后实例中声明的单个value-name(它将“覆盖”其他字段)。您拥有隔离作用域的原因是让每个labeled-input字段可以维护其管理的字段的自己的绑定。 - rchawdry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接