I'm a newbie in Angular, so my apologies in advance for any silly mistakes. I have defined a textField input through a directive in Angular:
.directive('textField', function () {
return {
restrict: 'E',
scope: true,
require: 'ngModel',
template: '{{start}}<span class="text-field-cursor">{{end}}</span>',
controller: 'TextFieldController',
link: function (scope, elem, attrs, ngModel) {
scope.keyboardOptions.limitTo = parseInt(attrs.limitTo);
scope.keyboardOptions.caps = _.has(attrs, 'caps');
scope.model = ngModel;
elem.bind('click', function () {
scope.openKeyboard();
});
elem.on('$destroy', function clickDestroyElement() {
elem.unbind();
});
}
};
})
Then, on my HTML, I wrote
<text-field class="text-field" data-t-username focusable request-focus="true" ng-class="{ 'is-focused': (focused || active), 'is-active': active }" ng-model="data.username" placeholder='Enter your username here' limit-to="32"></text-field>
However, the placeholder is not being visualized on the web page. What am I doing wrong?
In your directive, the template isn't an input. So the placeholder can't work.
You can change span to input or other directive who work with placeholder.
Related
I have a form that contain date inputs and select elements,
I tried to use ngChange but I found it not very useful when handling dates errors.
So I'm trying to set a watcher in a directive for every input.$error in order to display error message to the user.
my directive:
module.directive('validator', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.input = ctrl;
scope.$watch('input.$error', function() {
console.log(scope.input);
}, true);
}
}
}]);
The problem with the directive is that the scope.$watch fires only when an error object of dates is changing, and the scope.input of every input becomes similar to the ctrl of the changed date input.
JSFiddle
Update your directive to watch if the input is invalid
module.directive('validator', [function() {
return {
restrict: 'A',
require: '^form',
link: function(scope, elem, attr, form) {
var inputName = attr.name;
console.log(inputName, form[inputName].$error)
function watcherForRequired(){
return form[inputName].$error.required;
}
function watcherForMin(){
return form[inputName].$error.min;
}
scope.$watch(watcherForRequired, function(required) {
console.log("required", required);
})
scope.$watch(watcherForMin, function(min) {
console.log("min error", min);
})
}
}
also update ng-min to min="{{loan.loaned}}"
here is JSFiddle
After deep checking of the directive behavior. I find out that the scope.input of the last element infect all the others scopes.
So I set the $watch to listen to ctrl.$error, and it solved the problem.
The directive:
module.directive('validator', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.$watch(function() {return ctrl.$error}, function() {
if (ctrl.$invalid) {
console.log(attr.name, 'invalid');
}
else {
console.log(attr.name, 'valid');
}
}, true);
}
}
}]);
Updated JSFiddle
You're doing this to display error messages? Why not simply do this in the template?
<form name="myform">
<input ng-model="myform.model.user-name" name="user-name" id="my-form-user-name" />
<div ng-messages="myform.user-name.$error">
<p ng-if="myform.user-name.$invalid">My Error Message Here</p>
</div>
</div>
I'm trying to create the directive, in which I need to catch the validity input inside directive and then transfer to the parent element.
Simple directive
<my-input type="text" placeholder="Simple placeholder" name="Name of input"></my-input>
Template of simple directive
<div class="my-input-wrap" ng-class="getInputClass(inputModel)">
<input type="{{type}}" name="{{name}}" placeholder="{{placeholder}}" ng-model="inputModel" />
</div><!-- ./my-input-wrap -->
Simple directive code
angular.module('angular-bootstrap')
.directive('myInput', MyInputDirective);
function MyInputDirective() {
return {
require: 'ngModel',
restrict: 'E',
scope: {
label: '#',
name: '#',
type: '#',
placeholder: '#'
},
templateUrl: 'my-input.html',
controller: MyInputController,
controllerAs: 'myInput',
link: function(scope, element, attr, NgModelController) {
// NgModelController get validation only directive not input element inside directive
scope.getInputClass = function() {
console.log('INV' + NgModelController.$invalid);
console.log('DIRT' + NgModelController.$dirty);
return {
required: NgModelController.$invalid && NgModelController.$dirty
};
};
}
};
}
function MyInputController($scope) {
//...
}
I need in directive find input and his validation ($invalid, $dirty, ...) and set class to parent element, I do not want to use the form inside.
How can I be notified when the validity of a input changes?
There is the following custom directive code:
angular.module('app.directives', ['ng']).directive('liveSearch', [
'$compile', '$timeout', function($compile, $timeout) {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
scope: {
ngModel: '=',
liveSearchCallback: '=',
liveSearchSelect: '=?',
liveSearchItemTemplate: '#',
liveSearchWaitTimeout: '=?',
liveSearchMaxResultSize: '=?',
liveSearchResultField: '=?'
},
template: "<input type='text' />",
link: function(scope, element, attrs, controller) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
scope.ngModel = '10';
element.val(item[attrs.liveSearchResultField]
}
});
}
}}]);
It's not a complete code of my directive, but it's enough to understand the problem. Also view code:
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title"ng-model="skill" type="text"></live-search>
Value: {{ skill }}
<button class="btn btn-success" type="submit" ng-click="createEmployee">Сохранить</button>
My controller code:
$scope.createEmployee = function() {
console.log($scope.skill);
};
As you can see my custom directive has 'ng-model' attribute with name of variable 'skill'. How can I change 'skill' value in my custom directive? My way doesn't work. Thanks in advance!
In your link: try using
link:function(scope, element, attrs, ngModel){
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModel.$setViewValue(10);
ngModel.$render() // will update the input value as well
element.val(item[attrs.liveSearchResultField];
}
});
}
also it seems you have missed space between to separate ng-model attribute in your HTML
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title" ng-model="skill" type="text"></live-search>
See the documentation here
When you use require: 'ngModel', the 4th parameter passed to your link function is ngModelController. Use method $setViewValue to update the value of ngModel passed to your directive, then call $render() if your view needs to be updated as well.
link: function(scope, element, attrs, ngModelController) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModelController.$setViewValue(10);
ngModelController.$render();
element.val(item[attrs.liveSearchResultField]
}
});
}
I'm trying to access a transcluded element in an element directive from a different inner directive applied within the first directive's template.
My (trivial example) setup:
Element directive
.directive('elementDirective', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'template.html',
link: function (scope, elem, attr, ctrl, transclude) {
elem.find('transclusion').replaceWith(transclude());
}
};
})
Template
<div inner-directive>
<div id="transclusion"></div>
</div>
Inner Directive
.directive('innerDirective', function() {
return {
restrict: 'A',
link: function (scope, elem, attr, ctrl) {
//Do something here with child elements (which will be transcluded).
var some = elem[0].querySelector('[name]');
//...
}
};
})
Html
<element-directive>
<input name="input_name" />
</element-directive>
My Error:
I get an error of the kind.
element-directive element has no child input elements with a 'name' attribute
I'm assuming this means that the inner directive is compiled, linked, whatnot, before transclusion happens. So, how can I achieve what I'm trying to do?
I'm using bootstrap-colorpicker in combination with an angular directive.
Inside my form is colorpicker which I want to watch for changes.
As the value of the colorpicker is updated by this colorpicker jQuery plugin this does not work. I understand I have to implement this in my directive but can't figure out how.
Inside my I have the following markup:
<div data-colorpicker class="input-group colorpicker-element">
<input id="background" type="text" class="form-control" data-ng-model="myModel.background" />
<span class="input-group-addon"><i></i></span>
</div>
This markup is needed by bootstrap-colorpicker. Note I added data-colorpicker directive which initializes the colorpicker with:
myDirectives.directive('colorpicker', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.colorpicker();
}
};
});
So far so good. Note that the colorpicker is hooked up on wrapping div, which I thin kis causing my problem.
I extended the code to something like:
myDirectives.directive('colorpicker', function() {
return {
restrict: 'A',
require : '?ngModel',
link: function(scope, element, attrs, ngModel) {
element.colorpicker().on('changeColor', function(ev) {
scope.$apply(function() {
ngModel.$setViewValue(ev.color.toHex());
});
});
}
};
});
but the problem is the ngModel is defined on the input and not on the wrapping div.
Can I access the child scope/ngModel?
The easiest way is to wrap all the html in a directive.
The template:
<div class="input-group colorpicker-element">
<input id="background" type="text" class="form-control" data-ng-model="ngModel" />
<span class="input-group-addon"><i></i></span>
</div>
Directive code:
myDirectives.directive('colorpicker', function() {
return {
restrict: 'E',
require : '?ngModel',
scope: {
'ngModel': '='
},
link: function(scope, element, attrs, ngModel) {
element.colorpicker().on('changeColor', function(ev) {
scope.$apply(function() {
ngModel.$setViewValue(ev.color.toHex());
});
});
},
templateUrl: '/partials/colorpicker.html'
};
});
Then you can use it in controller:
<colorpicker ng-model="myModel.background" />