Gar*_*rom 8 html javascript angularjs angularjs-directive angularjs-scope
所以我现在已经在这个问题上工作了一个星期,我似乎无法理解整个指令的事情.我看了很多帖子......
一堆视频......
并通过StackOverflow和其他论坛(链接跟随)希望有什么东西会沉入......我认为我遇到的问题是我想理解为什么/如何这些工作,以便我不会切割/粘贴某人别的解决方案进入我的代码,但后来不得不再问一些其他东西,因为我不知道我的粘贴代码在做什么.
然而,我发现每个人都有不同的方式来抚摸这只猫,但它们似乎都没有与我对这个应该如何工作的理解相符.
我试图做的是使用Metro UI CSS库构建表单.我想我会从一个简单的文本框开始.是的...只是一个简单的文本框.Metro UI文本框有一些很好的内置功能,我想保留,所以我认为这是一个很好的起点.
我读到为了利用AngularJS的Metro UI行为,我需要将它包装在自定义指令中(AngularJS ng-repeat中的自定义数据指令).虽然这个例子不正是我一直在寻找它似乎很容易解释什么,我需要做的.只需调用在指令的LINK函数中应用行为的函数,并将指令属性添加到input元素中......
所以我创建了一个名为'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>
Run Code Online (Sandbox Code Playgroud)
在指令的LINK函数中,我简单地调用了应用我正在寻找的行为的方法.我知道这比它需要的更冗长,但我正在努力学习它,所以我尽我所能地踩过它....(完整代码见这个小提琴)
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 };
}
};
});
Run Code Online (Sandbox Code Playgroud)
所以这部分工作了.它创建了Metro外观和相关行为,但是...... ngModel没有绑定到该元素.因此,这开始了一个漫长的旅程,通过隔离范围,打破各种编译,控制器,预链接,后链接功能,至少两种不同的方式来持久化ngModel ......所有这些都无效.
经过各种阅读后,我的理解是DOM操作应该在COMPILE函数中进行,这样任何DOM转换都可用于编译,然后链接摘要过程的各个阶段.所以我将inputTransform()调用移动到COMPILE函数...(小提琴)
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
};
}
};
Run Code Online (Sandbox Code Playgroud)
没有运气......同样的事情......没有绑定到ngModel.所以我发现了"隔离范围"的概念......
基于此,我尝试了以下(小提琴)......
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
};
}
};
Run Code Online (Sandbox Code Playgroud)
没变 ...
我尝试过其他一些事情,但如果我还没有,我恐怕很快就会失去你的注意力.我得到的最接近的是ONE-WAY绑定做类似下面的事情......甚至在这里你可以看到ngModel引用的提取是完全不可接受的.(小提琴)
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
};
}
};
});
Run Code Online (Sandbox Code Playgroud)
我很精疲力竭,完全不知道剩下要尝试什么.我知道这是我的无知和对AngularJS如何/为何如此工作的理解缺乏的问题.但是我读到的每一篇文章都让我提出了许多问题,这些问题都得到了回答,或者让我陷入了一个兔子洞,在这个洞里,我比起初时更加迷失.如果我无法承担我需要回答的问题而无法负担现场研讨会上的3000美元,那么我对Angular完全没有结束.
如果有人能提供指导,指导......一个很好的资源......我会非常感激...特别是可以帮助我解决这个问题的任何事情,但任何可能有助于我停止旋转车轮的事情.在同一时间里,我将继续阅读并重新阅读我能找到的所有内容,希望有些东西能够破解.
谢谢
G
更新 - 2014年10月30日
我对这个问题非常感兴趣,但我想继续关注它.我需要并想要了解这一点.另外,我真的要感谢人们为此付出的努力,虽然他们提出了一些解决方案,最终可能是最好的方法,但他们都避开了这个问题,即我试图使用Metro UI CSS库提供的行为.如果可能的话,我宁愿不必重写它们.
到目前为止提供的两种解决方案都消除了解决方案的关键声明......这就是线路......
$(element).inputTransform()
Run Code Online (Sandbox Code Playgroud)
我不想发布包含"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;
};
Run Code Online (Sandbox Code Playgroud)
因此,我已将该指令应用为属性,因为它背后的Metro代码想要CLONE文本框(如果它是元素指令则不会这样做)然后删除原始输入元素.然后,它创建新的DOM元素,并将克隆的输入元素包装在新创建的DIV容器中.我相信这个问题是......当克隆原始元素并从DOM中删除时,绑定被破坏了.如果"ng-model"属性赋值绑定到文本框的引用,则有意义.因此,我最初的期望是,因为"ng-model"属性与元素的其余部分一起被克隆,所以在指令的编译事件/函数/阶段中,引用将被重新建立到新的创建了输入元素.显然事实并非如此.您可以在这个更新的小提琴中看到,我已经尝试将ng-model重新连接到新的DOM元素但没有成功.
也许这是不可能的......似乎只是重新构建这些东西最终可能是更容易的方法.
再次感谢Mikko Viitalia和'azium'......
指令并不是最简单的概念,文档实际上并不那么好,而且它分散在各种各样的网络中.
我挣扎着compile
,pre-compile
当我试图编写我的第一个指令时,但到目前为止我从未需要这些功能.这可能是由于我缺乏理解,但仍然......
看看你的例子,我看到有一些基本的东西需要澄清.首先,我将你的指令限制为E
lement,因为它正在替换HTML中的控件.我使用A
ttribute例如为现有控件添加功能.
有一个(强制)命名约定,您在JavaScript中使用HTML和camel大小写中的虚线命名.因此something-cool
变得somethingCool
.当您将变量"绑定"到指令的范围时,对您的操作方式有很大的不同.使用=
绑定到变量,使用@
变量evaluate(字符串)值.所以首先允许"双向绑定",但后者当然不是.您还可以使用&
绑定到父作用域的表达式/函数.
如果您使用例如plain,=
那么指令的范围在HTML中需要相同的名称.如果您希望使用不同的名称,则在之后添加变量名称=
.一个例子
ngModel : '=' // <div ng-model="data"></div>
otherVar: '@someVar' // <div some-var="data></div> or <some-var="data"></some-var>
Run Code Online (Sandbox Code Playgroud)
我把自由带你的第一小提琴的metro-input-transform
为出发点和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'
};
});
Run Code Online (Sandbox Code Playgroud)
指令期望ngModel
绑定到和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>
Run Code Online (Sandbox Code Playgroud)
在这里,我们绑定ngModel
到输入和分配placeholder
.显示[X]的按钮绑定到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>
Run Code Online (Sandbox Code Playgroud)
因此在上面的示例中,metro指令的用法如下.这将被指令的HTML模板替换.
<metro-input name="productName"
ng-model="data.productName"
watermark="product name">
</metro-input>
Run Code Online (Sandbox Code Playgroud)
另一个输入有detect-mouse-over
指令应用于它,仅限A
于显示以显示A
和之间的用法/差异E
.当鼠标移出/移出鼠标时,鼠标检测指令使输入改变背景颜色.
<input detect-mouse-over
type="text"
ng-model="data.productName">
Run Code Online (Sandbox Code Playgroud)
.
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');
});
}
};
});
Run Code Online (Sandbox Code Playgroud)
它也与ng-model
控件之间的镜像更改相同.
在您的示例中,您还有一个productService
为上面的输入控件提供了值.我把它重写为
产品服务
app.service('productService', function () {
return {
get: function () {
return { productName: 'initial value from service' };
}
};
});
Run Code Online (Sandbox Code Playgroud)
因此,get()
函数只获取硬编码值,但它仍然证明了服务的使用.控制器,命名Ctrl
是非常简单的.这里的重要部分是你记得将所有服务注入你的控制器.在这种情况下,角度$scope
和我们自己的角度productService
.
调节器
app.controller('Ctrl', function ($scope, productService) {
$scope.data = productService.get();
});
Run Code Online (Sandbox Code Playgroud)
这里是上述解决方案的屏幕截图.
更改任何输入中的值会更改两者的值.下面的输入有"鼠标悬停"所以它是灰色的,鼠标输出会再次变为白色.按[X]清除值并使占位符可见.
这是再次访问plunker的链接http://plnkr.co/edit/GGGxp0
归档时间: |
|
查看次数: |
15529 次 |
最近记录: |