angular directive highlight if any of inputs in div has focus - javascript

I have created an angular directive for a repeatable section with form elements
I want the whole section/div to be highlighted when any of then input fields inside the div are in focus
template.html
<div class="col-md-12 employee-section">
<label for="name_{{$index}}">Name</label>
<input type="text" id="name_{{$index}}" class="col-md-6" ng-model="model.name"/>
<label for="address_{{$index}}">Address</label>
<input type="text" id="address_{{$index}}" class="col-md-6" ng-model="model.address"/>
</div>
directive
angular.module('test').directive('employee' , function(){
return {
link: function(scope, element){
},
restrict: 'AE',
scope: {
model: "="
},
templateUrl: 'template.html'
};
}
controller
angular.module('test').controller('employeeCtrl' , function($scope){
$scope.employees = [{name:'Jackk',address:'Main st'}, {name:'Jill',address:'Main st 123'}
});
html page
<div ng-repeat="employee in employees>
<employee model="employee"></employee>
</div>

Here's the directive which I was looking for with link for Plnkr
app.directive('ngFocusModel', function () {
return function (scope, element) {
var focusListener = function () {
scope.hasFocus = true;
scope.$digest();
};
var blurListener = function () {
scope.hasFocus = false;
scope.$digest();
};
element[0].addEventListener('focus', focusListener, true);
element[0].addEventListener('blur', blurListener, true);
};
});

Related

Event when updating input from the model Angular

I have a directive that I put in the input of type class.
I add the blur, focus event to know when I entered and exit the input to make the effect of the label (Material Design), but when I wrap the value through the angular ng-model I need to know that the field was filled.
Any idea?
app.directive('formControl', function() {
return {
restrict: 'C',
link: function (scope, element, attrs) {
// Add class filled to form-control's that have a value
if(element.val()){
element.parent().addClass('filled');
}
// Any event here that can tell me that the value was changed by the angular so I can put the css class
element.bind('blur', function (e) {
input = angular.element(e.currentTarget);
if(input.val()){
input.parent().addClass('filled');
} else {
input.parent().removeClass('filled');
}
input.parent().removeClass('active');
}).bind('focus', function (e) {
input = angular.element(e.currentTarget);
input.parent().addClass('active');
});
}
};
});
Instead of adding the filled class inside the bind, you can add a watcher and add the class. Please refer the below example. Using colors in the classes to represent them getting added.
var app = angular.module('myApp', []);
app.controller('MyController', ['$scope', MyController]);
function MyController($scope) {
$scope.name = 'World';
$scope.name2 = 'hello';
}
app.directive('formControl', function() {
return {
restrict: 'C',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
// Add class filled to form-control's that have a value
if(ngModel.$viewValue){
element.parent().addClass('filled');
}
// Any event here that can tell me that the value was changed by the angular so I can put the css class
element.bind('blur', function (e) {
input = angular.element(e.currentTarget);
input.parent().removeClass('active');
}).bind('focus', function (e) {
input = angular.element(e.currentTarget);
input.parent().addClass('active');
});
scope.$watch(function(){return ngModel.$viewValue;}, function(newValue){
if('required' in attrs){
if(newValue){
element.parent().addClass('filled');
} else {
element.parent().removeClass('filled');
}
}
})
}
};
});
.filled{
background-color:lightblue;
}
.active{
border:1px solid red;
}
<div ng-controller='MyController' ng-app="myApp">
<label> The below input does not have required attribute</label>
<input type="text" class="form-control" ng-model="name">
<br>
<br>
<label> The below input has required attribute</label>
<input type="text" class="form-control" required ng-model="name2">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
Additionally if you want to show an error message like how you see in ng-messages.
var app = angular.module('myApp', []);
app.controller('MyController', ['$scope', MyController]);
function MyController($scope) {
$scope.name = 'World';
$scope.name2 = 'hello';
}
app.directive('formControl', function() {
return {
restrict: 'C',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
// Add class filled to form-control's that have a value
if(ngModel.$viewValue){
element.parent().addClass('filled');
}
// Any event here that can tell me that the value was changed by the angular so I can put the css class
element.bind('blur', function (e) {
input = angular.element(e.currentTarget);
input.parent().removeClass('active');
}).bind('focus', function (e) {
input = angular.element(e.currentTarget);
input.parent().addClass('active');
});
scope.$watch(function(){return ngModel.$viewValue;}, function(newValue){
if('required' in attrs){
if(newValue){
element.parent().addClass('filled');
} else {
element.parent().removeClass('filled');
}
}
})
}
};
});
.filled{
background-color:lightblue;
}
.active{
border:1px solid red;
}
div.filled > .error-message{
display:none;
}
div:not(.filled) > .error-message{
display:block;
}
.error-message{
text-align:center;
margin-top:5px;
}
<div ng-controller='MyController' ng-app="myApp">
<label> The below input does not have required attribute</label>
<input type="text" class="form-control" ng-model="name">
<br>
<br>
<label> The below input has required attribute</label>
<input type="text" class="form-control" required ng-model="name2">
<div class="error-message"> This Field is required</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
I hope this solves your issue :)

Reload a directive, to iterate over the elements that use it. in Angular.js

I have a directive that validates all the fields of a form that contain the "validate" directive. In all fields the word "logan" must exist to be correct. At any given time I want and need, when I click on the "Re_Evaluate all the form" button, validate if the form is correct, iterating in all the elements of the form, just like when the $timeout function is executed the first time .
app.directive('validate', function ($timeout,$compile) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel){
return;
}
$timeout(function() {
console.log(ngModel.$viewValue); //I need this, iterate in all elements with the directive 'validate', when re_evaluate() is clicked
if(ngModel.$viewValue=='logan'){
ngModel.$setValidity('validate',true);
}
else{
ngModel.$setValidity('validate',false);
}
})
scope.re_evaluate = function(){
console.log('I need re-evaluate all the form!')
}
}
};
});
http://jsfiddle.net/hmk0yg42/
The HTML is
<section ng-app="app">
<article ng-controller="appCtrl" class="container">
<form class="form-horizontal well" name="zform" invalidate>
<input type="text" name="mytext1" ng-model="my_text" validate />
<input type="text" name="mytext2" ng-model="my_text2" validate />
<input type="text" name="mytext3" ng-model="my_text3" validate />
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-disabled="zform.$invalid">
Validate</button>
</form>
</article>
</section>
And the JS is
var app = angular.module('app', []);
app.controller('appCtrl', function ($scope) {
$scope.my_text='logan';
$scope.my_text2 = 'logan';
$scope.my_text3 = 'logan';
});
app.directive('validate', function () {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.unshift(function(value) {
var valid = value == 'logan';
ngModelCtrl.$setValidity('logan',valid);
return value;
});
ngModelCtrl.$formatters.unshift(function(value) {
var valid = value == 'logan';
ngModelCtrl.$setValidity('logan', valid);
return value;
});
}
};
});
you don't need a re-evaluate button

AngularJS ng-model is updating only after click on input, why?

I got weird problem with binding data in angular, I have an input file and if file is selected then his title should display in input below. It's displaying, but only if I click on this input. It should be displayed automatically after I select file from file input form, here is some code:
.html:
<div class="md-button md-raised md-primary raised-button">
<label class="ng-binding" for="image-file">UPLOAD FILE</label>
</div>
<md-input-container>
<div class="raised">
<input id="image_file_name" type="text" ng-model="vm.filename"></input>
</div>
</md-input-container>
<input ng-model="vm.filename" style="display: none" id="image-file" type="file"
on-upload="uploadFile"/>
controller:
app.controller('imageController', function($scope, fileService) {
$('.intro-for-image').show(2000);
$scope.uploadFile = function(event){
$scope.event = event;
fileService.uploadFile($scope);
};
});
directive:
app.directive('onUpload', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var onChangeFunc = scope.$eval(attrs.onUpload);
element.bind('change', onChangeFunc);
}
};
});
service:
angular.module('app')
.service('fileService', function ($http, validationService) {
this.uploadFile = function ($scope) {
var event = $scope.event;
var file = event.target.files[0];
name = file.name;
$scope.vm = {
file: file,
filename: name,
};
};
});
I'm not sure about this directive, I found it somewhere after I googled my earlier problem.

Monitor for $valid on input element in AngularJS directive

I have this directive that prepends an <i> element before each <input> element with add-icon-element attribute. Now what i'm trying to do is to observe validation for each <input> element, so when the user types something in one of them the class of the <i> element that precedes will change to fa-check green-i. I tried to do it by using attrs.$observe to see when the class changes from ng-invalid to ng-valid but it fires only one time when the DOM is structured, it doesn't react to changes in the input element.
What am i doing wrong? is there a way to do it using the input $valid?
I saw some answers regarding one input with suggestions to add names to the form and input - but what can I do if I have multiple inputs that I need to validate and not just one?
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
link: function (scope, inputElement, attrs) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
attrs.$observe('class', function(val){
if (val.indexOf("ng-valid") >= 0) {
inputElement.prev().addClass('fa-check green-i');
}
});
}
};
});
and this is one of my 'inputs' in the html:
<form role="form" name="createProjectForm" class="form-validation">
<div class="form-group">
<label class="control-label text-center center-block" for="project.name">
Name Your Project
</label>
<div class="input-icon right">
<input type="text" placeholder="type here" name="project.name"
ng-model="project.name" required="required" class="form-control" add-icon-element/>
</div>
</div>
<form>
You don't need create a directive for such case, you could achieve this by using ng-class directive, only change your field name from name="project.name" to name="project_name"
<div class="input-icon right">
<input type="text" placeholder="type here" name="project.name"
ng-class="{'fa-check green-i': createProjectForm.project_name.$valid}"
ng-model="project.name" required="required"
class="form-control"/>
</div>
Update
To make it generic way, you need to require ngModel directive on that element, which will give you access to the ngModelController.
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
require: 'ngModel', //require to get access to `ngModelController`
link: function (scope, inputElement, attrs, ngModelCtrl) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
scope.$watch(function(){
return ngModelCtrl.$valid; //made watch on field validation.
}, function(val){
if (val.indexOf("ng-valid") >= 0) {
inputElement.prev().addClass('fa-check green-i');
}
});
}
};
});
I think this will do it for you:
https://stackoverflow.com/a/23871934/1636157
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
require: '^form',
link: function (scope, inputElement, attrs, ctrl) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
scope.$watch(ctrl.$name + '.' + inputElement.attr('name') + '.$valid', function (valid) {
if(valid) {
inputElement.prev().addClass('fa-check green-i');
} else {
inputElement.prev().removeClass('fa-check green-i');
}
});
}
};
});

How to track behavior of ngModel array item using .directive

Hi everyone I'm use angularjs not so long time ago and now I have one issue related with this framework that i can't to solve. So the problem in next: I have few input fields that generate via ng-repeat:
<div class="form-group" ng-repeat="(i, name) in name_list track by $index">
<div class="row">
<div class="col-xs-12">
<input class="form-control" type="text" ng-model="data.name_list[i]" add-input/>
</div>
</div>
Where name_list some array with data. As result I have generated input fields. Next that i wanted to do it's adding new input field if all previously fields was $dirty for this thing i wrote next angular code:
userApp.directive('addInput', ['$compile', '$sce', function ($compile, $sce) {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
scope.inputCounter = scope.name_list.length;
scope.$watch(
function(){
return ngModel.$dirty
},
function(dirty_val){
if (dirty_val){
scope.name_list.push(ngModel.$modelValue);
}
}
);
}
}}]);
but of course this code works wrong (it add new field if at last one field is $dirty) I know why it works wrong but I do not know how to track all ng-models separate, I don't know how to get access to some model like ngModel[1],so I hope somebody will help me in this, thank's
You could add a parent directive which will collect the dirty elements, and will add new element once it detects all of the other elements are dirty:
Check this plunker.
HTML:
<div collect-input>
<div class="form-group" ng-repeat="(i, name) in name_list track by $index">
<div class="row">
<div class="col-xs-12">
<input class="form-control" type="text" ng-model="data.name_list[i]" add-input/>
</div>
</div>
</div>
Once addInput detects it is dirty, call parent directive controller:
if (dirty)
collectInput.reportInput();
JS:
directive('collectInput', function() {
return {
restrict: 'A',
controller: function($scope) {
var dirtyCount = 0;
this.reportInput = function() {
var count = $scope.name_list.length;
dirtyCount++;
if (count === dirtyCount) {
$scope.name_list.push('aaa');
}
}
},
}
}).
directive('addInput', ['$compile', '$sce', function ($compile, $sce) {
return {
restrict: 'A',
require: ['^collectInput', '?ngModel'],
link: function (scope, element, attrs, ctrls) {
var collectInput = ctrls[0]
var ngModel = ctrls[1];
scope.inputCounter = scope.name_list.length;
scope.$watch(
function(){
return ngModel.$dirty
},
function(dirty_val){
if (dirty_val){
collectInput.reportInput();
}
}
);
}
}}]);

Categories