Reputation: 403
I am trying to generate a directive for click-to-edit input fields. Since I have a variety of different types of input fields that it needs to work with, I wanted to make it an attribute type directive that just transcludes the input field itself.
However, the problem is that when using a scope parameter to the directive description (for ipDisable), things stop working as they should (try commenting in line 44 in jsFiddle js part). Presumably, it is therefore a scope error, but I really have no idea where to begin debugging it, and any help would be appreciated.
jsFiddle: http://jsfiddle.net/HbYwX/3/
HTML:
<input inplace type="string" ip-disable=false name="username" ng-model="uname">
JS:
myApp.directive('inplace', function($compile) {
var compile = function(tElem,tAttrib,transclude) {
var whole = $('<div ng-scope></div>');
var editable = $('<div class="editable-transclude" ng-hide="static">'+
'<a ng-click="changeStatic()" ng-show="!static && !disable()">'+
'<save></a></div>');
whole.append(editable).append('<span class="disabledText" ng-show="static">{{ngModel.$viewValue}}</span>' +
'<a ng-click="changeStatic()" ng-show="static && !disable()">'+
'<edit></a>');
tElem.replaceWith(whole);
transclude(tElem,function(clone) {
clone.removeAttr('inplace');
editable.prepend(clone);
});
return function(scope, element, attrs) {
var input_element = $($(element).find('input')[0]);
scope.name = input_element.name;
scope.ngModel = element.controller('ngModel');
scope.static = true;
scope.changeStatic = function() {
if (scope.static) {
scope.static = false;
} else if (!scope.ngModel.$error[scope.name]){
scope.static = true;
}
};
};
};
return {
transclude: 'element',
scope: { disable: '&ipDisable' },
restrict: 'A',
compile: compile
};
});
Upvotes: 0
Views: 215
Reputation: 403
Ed's proposed solution solved a part of the problem. However, there was another one that probably threw me off in the first place:
The template was compiled into the parent scope as opposed to being attached to the new directive scope. To fix that, I needed to manually compile the created template in the linker function, where I could bind it to the proper scope.
The working solution is: http://jsfiddle.net/HbYwX/5/
myApp.directive('inplace', function($compile) {
var compile = function(tElem,tAttrib,transclude) {
var whole = $('<div ng-scope></div>');
var editable = $('<div class="editable-transclude" ng-hide="static">'+
'<a ng-click="changeStatic()" ng-show="!static && ipDisable()">'+
'<save></a></div>');
transclude(tElem,function(clone) {
clone.removeAttr('inplace');
clone.attr('ng-model','model');
editable.prepend(clone);
});
whole.append(editable).append('<span class="disabledText" ng-show="static">{{model}}</span>' +
'<a ng-click="changeStatic()" ng-show="static && !ipDisable()">'+
'<edit></a>');
return function(scope, element, attrs) {
element.replaceWith($compile(whole)(scope));
scope.name = attrs.name;
scope.static = true;
scope.changeStatic = function() {
if (scope.static) {
scope.static = false;
} else {
scope.static = true;
if (scope.name) scope.$emit('inplace-edit',scope.name);
}
};
};
};
return {
transclude: 'element',
scope: { ipDisable: '&', model: '=' },
restrict: 'A',
compile: compile
};
});
(which might be useful to anyone looking for something similar and is under MIT licence i.e. do what you wish with it).
Upvotes: 1
Reputation: 19098
This is because you're moving the input
element inside an element that has an isolate scope, so it can no-longer interact with the scope outside of it. As such, the uname
you have bound to will not be on the same scope as the one you're feeding into the ng-model
of the input.
You've got a couple of options - the first is not to create the isolate scope at all - you can still access ipDisable
through attrs
in your link function.
The other (better) solution, is to add ngModel
to the isolate scope as well (scope: { disable: '&ipDisable', ngModel:'='}
,), and update the value of the input yourself using the ngModelController, when the input is changed.
Upvotes: 1