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

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.

Related

angular directive highlight if any of inputs in div has focus

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);
};
});

Angularjs directive $compile causing ng-click to fire twice

I created an angular validator module for form validation without the need to include ngMessages in the form and everything is working as expected.
However I discovered a bug which I have been trying to fix. It has something to do with $compile.
So I have a directive that adds attributes to form elements and this is achieved by using $compile service however, it seems the $compile service causes an unwanted behaviour to ng-click so when ng-clicklocated inside the form is called it fires twice;
Here is the directive and what I am doing:
angular.module('app',[])
.directive('validateForm',['$compile',
function($compile)
{
var addErrors = function(rules, form, ctrls, scope, config){
//code
};
return {
restrict: 'A',
require: ['^form'],
link: {
post: function(scope, element, attrs, ctrls){
var form = ctrls[0];
var config = scope.validator;
if(typeof config != 'object') return;
var rules = config['rules'];
var errors = [];
//-----
},
pre: function(scope, element, attrs, ctrls){
var elm = element.find('select, input, textarea').attr('validate-field','');
element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-validate-form");
$compile(element.contents())(scope);
}
}
};
}
])
.controller('UserController',['$scope', function($scope){
$scope.title = 'Form Validator';
$scope.clickThings = function(value){
alert(value); //pops up twice means ng-click fires twice
}
}]);
Form markup:
<div ng-controller="UserController">
<form novalidate="" validate-form name="form" role="form">
<div class="form-group">
<input type="text" class="form-control" ng-model="first_name" name="first_name" />
</div>
<div class="form-group">
<input type="text" class="form-control" ng-model="last_name" name="last_name" />
</div>
<div class="form-group">
<input type="text" class="form-control" ng-model="email" name="email" />
</div>
<div class="form-group">
<input type="password" class="form-control" ng-model="password" name="password" />
</div>
<div class="form-group">
<input type="text" class="form-control" ng-model="country" name="country" />
</div>
<a type="button" class="btn btn-success" ng-click="clickThings('Submit Clicked')">Submit</a>
</form>
</div>
I have created a plunker:
http://embed.plnkr.co/uIid4gczKxKI4rPOHqx7
After trying different things I realized ng-click which is already compile is compiled a second time when $compile(element.contents())(scope) is called.
To resolve this issue, only need to compile the altered/affected elements i.e
elem = element.find('select, input, textarea').attr('validate-field','');
by replacing $compile(element.contents())(scope) with $compile(elem)(scope);
So I ended up with this:
angular.module('app',[])
.directive('validateForm',['$compile',
function($compile)
{
var addErrors = function(rules, form, ctrls, scope, config){
//code
};
return {
restrict: 'A',
require: ['^form'],
link: {
post: function(scope, element, attrs, ctrls){
var form = ctrls[0];
var config = scope.validator;
if(typeof config != 'object') return;
var rules = config['rules'];
var errors = [];
//-----
},
pre: function(scope, element, attrs, ctrls){
var elem = element.find('select, input, textarea').attr('validate-field','');
element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-validate-form");
$compile(elem)(scope);
}
}
};
}
])
.controller('UserController',['$scope', function($scope){
$scope.title = 'Form Validator';
$scope.clickThings = function(value){
alert(value); //pops up twice means ng-click fires twice
}
}]);
Working plunker here:
What is the purpose for using the $compile in a pre-link function? if u just want to do template transformation before the directive element get linked, u should put ur code in directive's compile and take out the $compile. Or if you would like to $compile your element after child elements are linked, u should put ur code in post-link. Putting $compile in pre-link will cause the child nodes of your child elements get linked twice.
Either choose:
angular.module('app',[])
.directive('validateForm',['$compile',
function($compile)
{
var addErrors = function(rules, form, ctrls, scope, config){
//code
};
return {
restrict: 'A',
require: ['^form'],
compile:function(element, attrs, ctrls){
// the code will be executed before get complied
var elm = element.find('select, input, textarea').attr('validate-field','');
return function(scope, element, attrs, ctrls){
var form = ctrls[0];
var config = scope.validator;
if(typeof config != 'object') return;
var rules = config['rules'];
var errors = [];
//-----
}
}
};
}
])
Or
angular.module('app',[])
.directive('validateForm',['$compile',
function($compile)
{
var addErrors = function(rules, form, ctrls, scope, config){
//code
};
return {
restrict: 'A',
require: ['^form'],
link: {
post: function(scope, element, attrs, ctrls){
var form = ctrls[0];
var config = scope.validator;
if(typeof config != 'object') return;
var rules = config['rules'];
var errors = [];
//-----
var elm = element.find('select, input, textarea').attr('validate-field','');
element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-validate-form");
$compile(element.contents())(scope);
},
pre: function(scope, element, attrs, ctrls){
}
}
};
}
])
EDIT
Just eliminate the $compile clause will also do the trick. But these three ways have some difference.
For more information u should refer to the official document

$dirty set value on focus

I am creating a form in AngularJS and I want validate the fields, the problem is that the required message appear only if write something in the input and after I delete it, but I want that the message appear after to focus the input
my code is the following
<input type="text" name="textInput" data-ng-model="field.data" class="form-control" required/>
<span ng-show="form.textInput.$dirty && form.textInput.$error.required">Required!</span>
Try the following
<span ng-show="form.textInput.$touched && form.textInput.$error.required">Required!</span>
This will show the message after you touched and left(lost focus) with the field invalid. Documentation
Yes, it's doable. First add a directive like this:
myApp.directive('trackFocus', ['$timeout', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
scope: true,
link: function ($scope, element, attr, ctrl) {
element.on("focus", function() {
$timeout(function() {
ctrl.hasFocus = true;
});
});
element.on("blur", function() {
$timeout(function() {
ctrl.hasFocus = false;
});
});
}
}
}]);
Then use the directive and modify your code:
<input type="text" name="textInput" ng-model="field.data" class="form-control" required track-focus />
<span ng-show="form.textInput.hasFocus && form.textInput.$dirty && form.textInput.$error.required">Required!</span>

AngularJS input watch $valid or $error

I'm trying to $watch the $error or the $valid value of a control. This is the control:
<form id="myForm" name="myForm">
<input name="myInput" ng-model="myInputMdl" business-validation/>
</form>
business-validation is a custom directive that alters the validity of the control. I've attempted the following approaches (using either $valid or $error) based on what I've read at AngularJS directive watch validity:
This does not work since $valid does not exist on myForm.myInput.
$scope.$watch('myForm.myInput.$valid', function (isValid) {
$scope.usefulVariable = step3.CreditCardNumber.$valid;
},true);
This does not work since validity.valid apparently cannot be watched.
$scope.$watch('myForm.myInput.validity.valid', function (isValid) {
$scope.usefulVariable = step3.CreditCardNumber.$valid;
},true);
This does not work since $scope.myInputMdl is not watched on every change.
$scope.$watch('$scope.myInputMdl', function (isValid) {
$scope.usefulVariable = step3.CreditCardNumber.$valid;
},true);
Can't the validity be watched from a controller?
EDIT
I'm not trying to write or edit business-validation directive. What I'm trying is to $watch $valid or $error from form's controller.
EDIT 2
Controller's code is:
app.controller('WizardBusinessActionCtrl',
function ($scope, $http, $parse, $filter, wizardBusinessActionService, $timeout) {
//controller code
}
);
I don't see any reason why your first variant shouldn't work.
HTML:
<div ng-app="myApp">
<div data-ng-controller="MainController">
<form name="myForm">
<input name="myInput" ng-model="myInputMdl" business-validation />
</form>
</div>
</div>
Controller and directive:
var myApp = angular.module('myApp', []);
myApp.controller('MainController', function ($scope) {
$scope.$watch('myForm.myInput.$valid', function (validity) {
console.log('valid', validity);
});
});
myApp.directive('businessValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attr, ctrl) {
function validate (input) {
return input === 'hello';
}
ctrl.$parsers.unshift(function (value) {
var valid = validate(value);
ctrl.$setValidity('businessValidation', valid);
return valid ? value : undefined;
});
ctrl.$formatters.unshift(function (value) {
ctrl.$setValidity('businessValidation', validate(value));
return value;
});
}
};
});
$valid property value will change when myInput validity changes, and $watch callback will be fired.
See example: http://jsfiddle.net/Lzgts/287/
Try the code below. Working Plunker
HTML
<div ng-controller="ExampleController">
<form name="userForm" novalidate >
Name:
<input type="text" name="userName" ng-model="firstName" required />
</form>
<div>{{firstName}}</div>
<input type = 'button' value = 'Submit' ng-click ='validateInput()'/>
</div>
JavaScript
angular.module('getterSetterExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.validateInput = function(){
$scope.$watch($scope.userForm.userName.$valid, function() {
if($scope.userForm.userName.$valid){
alert('Value is True');
}
else{
alert('Value is False');
}
});
}
}]);

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