gpt4 book ai didi

javascript - 将 ngModel 绑定(bind)到自定义指令

转载 作者:太空狗 更新时间:2023-10-29 13:48:16 24 4
gpt4 key购买 nike

所以我已经在这个问题上工作了一个星期,我似乎无法理解整个指令的事情。看了很多帖子。。。

  • Demystifying Directives
  • Directives
  • Compile, Pre and Post Linking

  • 一堆视频...
  • Creating Reusable Directives in AngularJS
  • Writing Directives

  • 并浏览了 StackOverflow 和其他论坛(要遵循的链接),希望有什么东西会沉没......我认为我遇到的问题是我想了解这些为什么/如何工作,这样我就不会剪切/粘贴某人else 的解决方案到我的代码中,但是当其他东西突然出现时不得不再次询问,因为我不知道我粘贴的代码在做什么。

    然而,我发现每个人都有不同的方法来剥这只猫的皮,而且似乎没有一个与我对它应该如何工作的理解相匹配。

    我试图做的是使用 Metro UI CSS 构建一个表单图书馆。我以为我会从一个简单的文本框开始。是的......只是一个简单的文本框。 Metro UI 文本框有一些很好的内置功能,我想保留这些功能,所以我认为这是一个很好的起点。

    我读到为了在 AngularJS 中利用 Metro UI 行为,我需要将它包装在自定义指令中( Custom data-directives inside an AngularJS ng-repeat )。虽然这个例子并不是我想要的,但它似乎很容易解释我需要做什么。只需调用在指令的 LINK 函数中应用行为的函数,并将指令属性添加到输入元素...

    因此,我创建了一个名为“metroInputTransform”的指令,并将其作为属性添加到输入元素中。
    <div data-ng-controller="pageOneFormCtrl as page">
    <input type="text" id="txProductName"
    data-ng-model="page.data.productName"
    data-metro-input-transform=""
    placeholder="product name" />
    </div>

    在指令的 LINK 函数中,我只是调用了应用我正在寻找的行为的方法。我知道这比它需要的要冗长一些,但我正在努力学习它,所以我正在尽我所能地逐步完成它。 ...(完整代码见 this fiddle)
    var metroDirectives = angular.module('metroDirectives', []);
    metroDirectives.directive('metroInputTransform', function ($compile) {

    function postLink($scope, element, attrs, controller) {

    $(element).inputTransform();
    };

    return {
    priority: 100,
    compile: function (element, attrs) {

    return { postLink };
    }
    };
    });

    所以这部分有效。它创建了 Metro 外观和相关行为,但是 ... ngModel 未绑定(bind)到元素。所以这开始了一个漫长的旅程,比如隔离作用域,打破各种编译、 Controller 、预链接、后链接功能,至少两种不同的持久化 ngModel 方式......所有这些都不起作用。

    经过各种阅读后,我的理解是 DOM 操作应该发生在 COMPILE 函数中,以便任何 DOM 转换都可用于编译和摘要过程的链接阶段。所以我将 inputTransform() 调用移动到 COMPILE 函数......( fiddle )
        return {
    priority: 100,
    terminal: true, // if I didn't put this everything would execute twice
    compile: function (element, attrs) {

    $(element).inputTransform();

    return {
    pre: preLink,
    post: postLink
    };
    }
    };

    没有运气......同样的事情......不绑定(bind)到ngModel。所以我发现了“隔离范围”的概念......
  • Understanding Isolate Scope - 视频
  • Using Isolate Scopes in Directives - 视频
  • Using ngModel with Isolate Scope

  • 基于此,我尝试了以下操作( fiddle )...
        return {
    priority: 100,
    scope: {
    ngModel : '='
    },
    terminal: true, // if I didn't put this everything would execute twice
    compile: function (element, attrs) {

    $(element).inputTransform();

    return {
    pre: preLink,
    post: postLink
    };
    }
    };

    没变 ...

    我尝试了很多其他的东西,但如果我还没有的话,恐怕我很快就会失去你的注意力。我得到的最接近的是 ONE-WAY binding 做如下所示的事情......即使在这里你也可以看到 ngModel 引用的提取是完全 Not Acceptable 。 ( fiddle )
    var metroDirectives = angular.module('metroDirectives', []);
    metroDirectives.directive('metroInputTransform', function () {

    function postLink($scope, element, attrs, controller) {
    //
    // Successfully perfomes ONE-WAY binding (I need two-way) but is clearly VERY
    // hard-coded. I suppose I could write a pasrsing function that would do this
    // for whatever they assign to the ngModel ... but ther emust be a btter way
    $(element).on("change", '[data-metro-input-transform]', function(e) {
    $scope.$apply(function(){
    $scope['page']['data']['productName'] = e.currentTarget.value;
    });
    });
    };

    return {
    priority: 100,
    terminal: true, // if I didn't put this here the compile would execute twice
    compile: function (element, attrs) {

    $(element).inputTransform();

    return {
    pre: function ($scope, element, attrs, controller, transcludeFn) { },
    post: postLink
    };
    }
    };
    });

    我筋疲力尽,完全不知道还有什么可以尝试的。我知道这是我对 AngularJS 如何/为什么以这种方式工作的无知和缺乏理解的问题。但是我读过的每一篇文章都让我提出的问题和得到的回答一样多,或者让我掉进了一个兔子洞,在那里我比开始时迷失了更多。除了在现场面对面研讨会上投入 3000 美元之外,我无法在那里提出我需要回答的问题,我对 Angular 已经完全陷入了死胡同。

    如果有人能提供指导、方向……一个很好的资源……任何可以帮助我特别了解这个问题的东西,但任何可以帮助我停止旋转的东西,我将不胜感激。与此同时,我将继续阅读并重新阅读我能找到的所有内容,希望有些东西会破裂。

    谢谢

    G

    更新 - 2014 年 10 月 30 日

    我对这个问题很感兴趣,但想坚持到底。我需要并想学习这个。此外,我真的想对人们为此付出的努力表示感谢,虽然他们提出了一些解决方案,这最终可能是最好的方法,但他们都避开了这个问题,即我正在尝试使用Metro UI CSS 库提供的行为。如果可能的话,我宁愿不必重写它们。

    到目前为止提供的两种解决方案都消除了解决方案中的关键语句......这是行......
    $(element).inputTransform()

    我不想发布包含“inputTransform”定义的整个 jQuery 小部件,但我把它的主要部分删掉并包含在这里......
        function createInputVal(element, name, buttonName) {

    var wrapper = $("<div/>").addClass("input-control").addClass(name);
    var button = $("<button/>").addClass(buttonName);
    var clone = element.clone(true); // clone the original element
    var parent = element.parent();

    $(clone).appendTo(wrapper);
    $(button).appendTo(wrapper);
    $(wrapper).insertBefore(element);
    $(element).remove(); // delete the original element

    return wrapper;
    };

    因此,我已将该指令作为属性应用,因为它背后的 Metro 代码想要克隆文本框(如果它是元素指令,则不会这样做),然后删除原始输入元素。然后它创建新的 DOM 元素并将克隆的输入元素包装在新创建的 DIV 容器中。我认为问题在于……当原始元素被克隆并从 DOM 中删除时,绑定(bind)被破坏了。如果“ng-model”属性赋值绑定(bind)到 是有道理的引用 的文本框。所以我最初的期望是,由于“ng-model”属性与元素的其余部分一起被克隆,在指令的编译事件/函数/阶段中,引用将(重新)建立到新的创建的输入元素。情况显然并非如此。您可以在此更新 fiddle 中看到我已经尝试将 ng-model 重新连接到新的 DOM 元素,但没有成功。

    也许这是不可能的……看起来,重新构建这些东西最终可能是更简单的方法。

    再次感谢 Mikko Viitalia 和 'azium' ...

    最佳答案

    指令并不是最简单的概念,文档也不是那么好,而且它分散在互联网上。

    我挣扎于 compile , pre-compile因此,当我尝试编写我的第一个指令时,但迄今为止我从未需要这些函数。这可能是由于我缺乏了解,但仍然......

    看看你的例子,我发现有一些基本的事情需要澄清。首先,我会将您的指令限制为 E元素,因为它正在替换 HTML 中的控件。我会用 A属性例如向现有控件添加功能。

    有一个(强制性的)命名约定,您可以在 HTML 中使用虚线命名,在 JavaScript 中使用驼峰命名。所以something-cool变成 somethingCool .当您将变量“绑定(bind)”到指令的作用域时,您的操作方式存在重大差异。使用 =你绑定(bind)到变量,使用 @到变量评估(字符串)值。所以首先允许“双向绑定(bind)”,但后者当然不允许。您也可以使用 &绑定(bind)到父作用域的表达式/函数。

    如果您使用例如普通=那么指令的范围需要在您的 HTML 中使用相同的名称。如果您希望使用不同的名称,则在 = 后添加变量名称。 .一个例子

    ngModel : '='        // <div ng-model="data"></div>
    otherVar: '@someVar' // <div some-var="data></div> or <some-var="data"></some-var>

    我冒昧拿 your first Fiddlemetro-input-transform作为起点 and rewrite it in Plunker .我试图在这里解释它(希望我理解你的意思)。

    Metro 输入指令
    directives.directive('metroInput', function () {
    return {
    restrict: 'E',
    scope: {
    ngModel: '=',
    placeholder: '@watermark'
    },
    link: function (scope) {
    scope.clear = function () {
    scope.ngModel = null;
    };
    },
    templateUrl: 'metro-template.html'
    };
    });

    指令预期 ngModel绑定(bind)到和 watermark显示 ngModel 何时没有值(文本输入为空)。内 link我介绍过 clear()在指令中用于重置 ngModel 的函数.重置值时, watermark是表演。我已将 HTML 部分分成一个单独的文件,metro-template.html。

    Metro 输入 HTML 模板
    <input type="text" ng-model="ngModel" placeholder="{{ placeholder }}">
    <button type="button" class="btn-clear" ng-click="clear()">x</button>

    我们在这里绑定(bind) ngModel输入和分配 placeholder .显示 [X] 的按钮绑定(bind)到 clear()方法。

    现在,当我们设置好指令后,这是使用它的 HTML 页面。

    HTML 页面
    <body>
    <div ng-controller="Ctrl">
    <section>
    The 'Product name' textbox in the 'Directive'
    fieldset and the textbox in the 'Controls'<br>
    fieldset should all be in sync.
    </section>

    <br>

    <fieldset>
    <legend>Directive</legend>
    <label for="productName">Product name</label>
    <br>
    <metro-input name="productName"
    ng-model="data.productName"
    watermark="product name">
    </metro-input>
    </fieldset>

    <br>

    <fieldset>
    <legend>Control</legend>
    <input detect-mouse-over
    type="text"
    ng-model="data.productName">
    </fieldset>
    </div>
    </body>

    所以在上面的例子中,metro 指令的用法如下。这将替换为指令的 HTML 模板。
    <metro-input name="productName" 
    ng-model="data.productName"
    watermark="product name">
    </metro-input>

    另一个输入有 detect-mouse-over应用于它的指令,仅限于 A ttribute 只是为了显示 A 之间的用法/差异和 E .鼠标检测指令使输入在鼠标移过/移出时更改背景颜色。
    <input detect-mouse-over
    type="text"
    ng-model="data.productName">

    .
    directives.directive('detectMouseOver', function () { 
    return {
    link: function (scope, element, attrs) {
    element.bind('mouseenter', function () {
    element.css('background-color', '#eeeeee');
    });
    element.bind('mouseleave', function () {
    element.css('background-color', 'white');
    });
    }
    };
    });

    它也有相同的 ng-model来反射(reflect)控件之间的变化。

    在您的示例中,您还有一个 productService为上述输入控件提供值。我把它改写为

    产品服务
    app.service('productService', function () {
    return {
    get: function () {
    return { productName: 'initial value from service' };
    }
    };
    });

    所以 get() function 只是获取硬编码值,但它仍然演示了服务的使用。 Controller ,名为 Ctrl真的很简单。这里的重要部分是您记得将所有服务等注入(inject)您的 Controller 。在这种情况下, Angular 的 $scope和我们自己的 productService .

    Controller
    app.controller('Ctrl', function ($scope, productService) {
    $scope.data = productService.get();
    });

    这是上述解决方案的屏幕截图。

    imgur

    更改任何输入中的值都会更改两者的值。下面的输入有“mouseover”所以它是灰色的,mouseout 会再次变成白色。按 [X] 清除值并使占位符可见。

    这是 plunker 的链接再次 http://plnkr.co/edit/GGGxp0

    关于javascript - 将 ngModel 绑定(bind)到自定义指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26537960/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com