I am running into some problems using the keypress as follows-
<div ng-controller="GBNController">
...
<input id="search-field" type="text" placeholder="JSON Query" ng-model="queryText" ui-keypress="{enter: foo()}"/>
...
</div>
and my javascript has -
var AngApp = angular.module('gbn', ['ui']);
var GBNController = function($scope) {
$scope.queryText = '';
$scope.foo = function() {
alert('test');
}
};
Now this function foo is called only when the document is loaded and then after that, the return keypress event in the text field is never handled.
I am using the current head of the master branch.
Am I doing something wrong here, or is this broken??
You need to put foo() in quotes.
<input ui-keypress="{enter: 'foo()'}">
You could easily just roll your own keypress directive
Here's a plunker to demo
And the code:
app.directive('zKeypress', function(){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('keypress', function(){
scope.$apply(function(s) {
s.$eval(attr.zKeypress);
});
});
}
};
});
HTML
<input type="text" z-keypress="foo()"/>
Related
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
This code shows how to use a directive to submit a form by hitting 'enter' while in a textarea. However, I would like to be able to shift+enter and go to the next line and submit the result as it is. Whenever the submission is made, it shows up in the same line. How do I submit and show the submitted text in the next line as the user intends.
<div ng-app="testApp" ng-controller="MyController">
<textarea ng-model="foo" enter-submit="submit()"></textarea><br/>
Last submitted text: {{ lastSubmitted }}<br/>
</div>
The AngularJS code:
var app = angular.module('testApp', []);
function MyController($scope) {
$scope.foo = ""
$scope.lastSubmitted = ""
$scope.submit = function() {
$scope.lastSubmitted = $scope.foo;
}
}
app.directive('enterSubmit', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('keydown', function(event) {
var code = event.keyCode || event.which;
if (code === 13) {
if (!event.shiftKey) {
event.preventDefault();
scope.$apply(attrs.enterSubmit);
}
}
});
}
}
});
What should I do?
It looks like you need to convert \n\r into <br/> then use ng-bind-html to sanitize the string.
Here is a code example
You will also have to include the angularjs sanitize js file:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.js"></script>
I'm trying to unite the AngularJS validation model with the Bootstrap form validation display.
If a user loads an empty form, I don't want the form to display error message right away. I want to wait until the user interacts with the form.
If a user submit the form with required fields not filled out, I also want to display an error message.
If a user starts typing in the field, I want error messages to show up right away.
So I have to check myForm.$submitted, myForm.<fieldName>.$dirty as well as myForm.<fieldName>.$touched.
However, it makes a lot of duplicated code with very few variation.
I've tried to make a directive to fix this issue but I can't seem to find the right way to wrap this complexity away.
HTML:
<div class="form-group required" ng-class="{ 'has-error': myForm.firstname.$invalid && (myForm.firstname.$dirty || myForm.$submitted || myForm.firstname.$touched) }">
<label for="firstname" class="control-label" translate>Profile.FirstName</label>
<input type="text" class="form-control" id="firstname" name="firstname" required ng-model="vm.profile.firstName"/>
<p class="help-block" ng-if="myForm.firstname.$error.required" translate>Forms.Default.Required</p>
</div>
I want to take the whole ng-class attribute and replace it by something more succinct. The directive seemed like the way to go so tried this:
(function(){
'use strict';
angular.module('app')
.directive('hasError', [function(){
return {
restrict: 'A',
scope: {
form: '=bsForm',
control: '=bsControl'
},
link: function(scope, element){
scope.$watch('form', function(){
var isInvalid = scope.control.$invalid && scope.control.$dirty;
element.toggleClass('has-error', isInvalid);
});
}
};
}]);
})();
Usage:
<div class="form-group required" has-error bs-form="myForm" bs-control="myForm.firstname">
...
</div>
This however was not refreshing when properties of form changed.
What am I missing?
So... I managed to make a directive work properly for exactly my usage.
If there is a better way, please prove me wrong.
(function(){
'use strict';
angular.module('app')
.directive('hasError', [function(){
return {
restrict: 'A',
scope: {
form: '=bsForm',
control: '=bsControl'
},
link: function(scope, element){
scope.$watchGroup(['control.$invalid', 'control.$dirty', 'control.$touched', 'form.$submitted'], function(){
var isInvalid = scope.control.$invalid && (scope.control.$dirty || scope.form.$submitted || scope.control.$touched);
element.toggleClass('has-error', isInvalid);
});
}
};
}]);
})();
I did something like this one. My solution took a slightly different approach, but it may be helpful here (you can view the gist on Github).
Essentially, what I do is wrap all my form data inside a single object and I assign that object to a <form> attribute. I then watch that object and any time it changes, I select all elements with the ng-dirty and ng-invalid classes (this selector could be changed to whatever you like). I then loop through each of these elements and update messages for each of them.
Here's the code:
(function() {
"use strict"
angular.module('app')
.directive('formValidator', function() {
return {
require: '^form',
scope: {
formData: '=',
validateAll: '='
},
link: function(scope, element, attrs, ctrls) {
window.frm = ctrls;
var selector = '.ng-dirty.ng-invalid';
function validate() {
$(".formValidator-input-validation-error-message").remove();
element.find(selector).each(function(index, el) {
$el = $(el);
var messages = [];
var classes = $el.attr('class').match(/[\d\w-_]+/g);
for (var i in classes) {
var lastIndex = classes[i].lastIndexOf('-invalid-');
if (lastIndex != -1) {
var validationMessageAttr = "data-" + classes[i].substr(lastIndex + 9) + "-validation-message";
var msg = $el.attr(validationMessageAttr);
if (!msg) {
msg = element.attr(validationMessageAttr);
if (!msg) {
msg = "Invalid!";
}
}
messages.push("<div class='validator'>" + msg + "</div>");
}
}
$(el).after("<div style='position:absolute;' class='formValidator-input-validation-error-message'>" + messages.join() + "</div>");
});
}
scope.$watch(function() {
return scope.formData;
}, function() {
validate();
}, true);
scope.$watch('validateAll', function(newValue, oldValue) {
selector = !!newValue ? '.ng-invalid' : '.ng-dirty.ng-invalid';
validate();
});
}
};
})
})();
I was following this SO Post on creating an Enter keypress directive.
Here is my JS, in which I simulate the same functionality:
JavaScript
var app = angular.module('myApp', []);
app.controller('MainCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
app.directive('myEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.myEnter);
});
event.preventDefault();
}
});
};
});
function callServerWithSong(){
alert("calling!");
}
HTML
<div id="search" ng-app="myApp" ng-controller="MainCtrl">
<input type="text" id="search" my-enter="calServerWithSong()">
</div>
The problem is that when I select the input box, type a string and press enter, it doesn't report an error or execute the alert() which tells me that my directive must not be properly set up to fire when I keypress that element.
You had a typo of method name while passing through a directive attribute. Also the method should be there in $scope of controller.
HTML
<div id="search" ng-app="myApp" ng-controller="MainCtrl">
<input type="text" id="search" my-enter="callServerWithSong()">
</div>
Code
function callServerWithSong(){
alert("calling!");
}
$scope.callServerWithSong = callServerWithSong;
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>