AngularJS ng-click not firing - javascript

I have a custom AngularJS directive for a textdown control. In it is an ng-repeat that is printing a list of div's for the emulated dropdown, and each item has an ng-click function. That function will not fire when the div is clicked. Can you please help me figure out why?
Plunkr: https://plnkr.co/edit/vOwtjqltq2WfCM9A0dFJ?p=preview
I don't remember where I first heard that concept, but it's very similar to StackOverflow's question Tags input, except you can only select 1 item. If you haven't seen that example, it is a text input that has a dropdown list when you start typing into it with related items that has fields that partially match what you've typed so far. Then the user can click on an item in the dropdown and it fills in the text input.
Here is the main page's HTML:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="app.js"></script>
<script src="textdown.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello and welcome to the Textdown example!</p>
<label>City:
<textdown input-placeholder-text="Select a City..." is-editable="true" items="cities" ng-model="selectedCity" title="Name" width="150px"></textdown>
</label>
</body>
</html>
Here is the directive's HTML:
var HYG_TEXTBOX_DROPDOWN_TEMPLATE = '\
<div class="hyg-textdown-container activate-textdown" \
ng-class="{ \'hyg-focused\': isFocused() }"> \
<input type="search" class="activate-textdown" placeholder="{{ inputPlaceholderText }}" style="width: 100%;" \
ng-class="{ \'invalid\': !isValid() }" \
ng-change="onInputChanged()" \
ng-focus="onInputFocus($event)" \
ng-model="input" \
ng-blur="onInputBlur()" \
ng-show="isEditable"> \
</input> \
<div class="hyg-textdown-list activate-textdown" ng-show="selectActive" ng-style="{ top: ytop, left: xleft }" style="z-index:5; width: {{ width }}"> \
<div class="hyg-textdown-listed activate-textdown" \
ng-repeat="item in items | property: title: (ngModel != null ? \'\' : input) | orderBy: title | limitTo:5" \
ng-class="{ \'hyg-textdown-listed-active\': isSelected(item) }" \
ng-click="selectItem(item, $event);"> \
<span class="activate-textdown">{{ item[title] }}</span> \
</div> \
</div> \
</div>';
Here is the module, directive, controller, and associated filter code:
angular.module("hyg.Textdown", [])
.directive("textdown", ["$compile", "$document", "$filter", "$log", "$timeout", function ($compile, $document, $filter, $log, $timeout) {
return {
restrict: "E",
replace: false,
controller: "hygTextdownCtrl",
template: function (element, attrs) {
return HYG_TEXTBOX_DROPDOWN_TEMPLATE;
},
require: "?ngModel",
scope: {
inputPlaceholderText: "#",
isEditable: "=",
items: "=",
ngModel: "=",
title: "#",
width: "#"
},
link: function (scope, element, attrs) {
scope.orderBy = $filter("orderBy");
if (scope.isEditable == null)
scope.isEditable = true;
$document.bind("click", function (e) {
var shouldHideSelectList = !Enumerable.From(e.target.classList).Any(function (x) { return x == "activate-textdown"; });
if (shouldHideSelectList) {
$timeout(function () { scope.selectActive = false; }, 0);
}
});
scope.destroy = function () {
if (scope.handler != null)
scope.handler();
};
scope.isFocused = function () {
return scope.focus;
};
scope.isSelectActive = function () {
return scope.selectActive;
};
scope.isValid = function () {
return scope.input == null || scope.input.length == 0 || scope.ngModel != null;
};
scope.onInputChanged = function () {
var input = scope.input == null ? null : scope.input.toLowerCase();
var item = Enumerable.From(scope.items).Where(function (x) { return x[scope.title].toLowerCase() == input; }).ToArray()[0];
scope.selectItem(item);
};
scope.onInputFocus = function ($event) {
scope.focus = true;
scope.selectActive = true;
};
scope.onInputBlur = function () {
scope.focus = false;
scope.selectActive = false;
};
scope.selectItem = function (item, $event) {
if (scope.isEditable) {
scope.ngModel = item;
if (item != null)
scope.selectActive = false;
}
};
scope.isSelected = function (item) {
return scope.ngModel == item;
};
scope.handler = scope.$watch("ngModel", function () {
if(scope.ngModel != null)
scope.input = scope.ngModel[scope.title];
});
}
}
}])
.controller("hygTextdownCtrl", ["$scope", function ($scope) {
$scope.focus = false;
$scope.handler = null;
$scope.selectActive = false;
}])
.filter("property", ["$filter", function ($filter) {
return function (array, propertyString, target) {
if (target == null)
return array;
var matched = [];
var toMatch = target.toLowerCase();
angular.forEach(array, function (item) {
if (item[propertyString].includes != undefined) {
if (item[propertyString].toLowerCase().includes(toMatch)) {
matched.push(item);
}
}
else
{
if (item[propertyString].toLowerCase().indexOf(toMatch) > -1) {
matched.push(item);
}
}
});
return matched;
}
}]);
Thanks,
Jibba

The reason why the ng-click is not firing is because before the option is clicked, blur event is fired on the input, which hides the options and your option never gets clicked.
You can try selecting option using ng-mousedown instead of ng-click.

Related

angularjs directive for accept input numbers only in the given range

Hi I am developing web application in angularjs. I have requirement to validate textbox. It should accept only numbers with max 10 digits. I have directive but current directive should not restrict number of digits typed.
myapp.directive('validNumber', function () {
return {
require: '?ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$parsers.push(function (val) {
if (angular.isUndefined(val)) {
var val = '';
}
var clean = val.replace(/[^0-9]+/g, '');
if (val !== clean) {
ngModelCtrl.$setViewValue(clean);
ngModelCtrl.$render();
}
return clean;
});
element.bind('keypress', function (event) {
if (event.keyCode === 32) {
event.preventDefault();
}
});
}
};
});
<div class="inputblock" ng-class="{ 'has-error' : ((form1.$submitted && form1.idnumber.$invalid )|| (form1.idnumber.$invalid && form1.idnumber.$dirty))}">
<label class="inputblock-label" ng-show="idnumber">{{ 'ID Number' | translate }}</label>
<span class="ang-error" style="color:#fff" ng-show="form1.idnumber.$dirty && form1.idnumber.$invalid ">
<span ng-show="form1.idnumber.$invalid && form1.idnumber.$dirty">*{{'Max allowed digits 10' | translate}}</span>
</span>
<input class="with-icon" type="text" name="idnumber" placeholder="{{ 'ID Number' | translate }}" ng-model="idnumber" required ng-pattern="/^[0-9]{1,7}$/" > <!--valid-number-->
</div>
May i know what should be changed in the above directive so that it can accept maximum only 10 digits! Any help would be appreciated. Thank you.
Use the following code and try. my original purpose of this code was to limit the number to integer. But I have modified it a little so you can use this
(function() {
'use strict';
angular.module('app').directive('intValidate', intValidate);
function intValidate($locale) {
var decimalSep = $locale.NUMBER_FORMATS.DECIMAL_SEP;
var toNumberRegex = new RegExp('[^0-9\\' + decimalSep + ']', 'g');
function toNumber(currencyStr) {
return parseFloat(currencyStr.toString().replace(toNumberRegex, ''), 10);
}
return {
restrict : 'A',
require : 'ngModel',
link : function validate(scope, elem, attrs, modelCtrl) {
modelCtrl.$parsers.push(function(newViewValue) {
var modelValue = toNumber(newViewValue);
var valid = modelValue <= 9999999999;
modelCtrl.$setValidity('limitcheck', valid);
return valid ? newViewValue : undefined;
});
}
};
}
})();
and use,
<input type="text" id="value" name="value" int-validate>
and if you want an error message
<p class="help-block" ng-if="cacc.form.value.$error.limitcheck">Max 10 digits allowed</p>

Multiple directives sharing same scope

I have developed a directive, but it is always applying functionality on the last element in the page, here is my code:
HTML:
<div ng-controller="MyCtrl">
<input type="text" email-commy comma-separated-data="model1" class="form-control"/>
<input type="text" email-commy comma-separated-data="model2" class="form-control"/>
</div>
JS:
var app=angular.module('myApp', []);
function MyCtrl($scope) {
$scope.model1 = "abc#gmail.com";
$scope.model2 = "abc2#gmail.com";
}
app.directive("emailCommy", function(){
return {
scope: {
commaSeparatedData: '='
},
restrict: "A",
multiple: true,
link: function (scope, elem, attrs, ngModel) {
function handler(dataString){
function buildEmailList() {
var lis = data.map(function (em, index) {
return "<li>" + em + "<span class='remove-element' data-index='" + index + "'>×</span>" + "</li>";
}).join("");
var input = $("<input/>", {"class": "form-control shadow-element", "placeholder": "Enter..."});
lis += "<li class='input-field'>" + input.wrap("<p/>").parent().html() + "</li>";
return "<ul class='list-inline'>" + lis + "</ul>";
}
function renderer() {
$wrapper.empty().append(buildEmailList());
elem.addClass("hide").before($wrapper);
listener();
}
function listener() {
angular.element(".shadow-element").off("keypress").on("keypress", function (evt) {
var $this = $(this),
charCode = evt.which || evt.keyCode;
//add input in case of (comma, enter, and semi-colon) pressed
if ($this.val() && (charCode === 44 || charCode === 13 || charCode === 59)) {
data.push($this.val().toLowerCase());
renderer();
updateModel();
$this.val("");
}
});
angular.element(".remove-element").off("click").on("click", function () {
var $this = $(this),
index = $this.data("index");
data.splice(index, 1);
updateModel();
renderer();
});
}
function updateModel(){
var updatedVal = data.join(",");
$(elem).val(updatedVal);
}
if(dataString) {
var data = dataString.split(",").map(function (em) {
return em.toLowerCase().trim();
});
var $wrapper = $("<div/>", {"class": "email-container"});
renderer();
}
}
handler(scope.commaSeparatedData);
//scope.$watch(attrs.commaSeparatedData, handler);
}
};
});
Here is fiddle:
http://jsfiddle.net/RmDuw/928/
Directive only working on last element. If i add data in first directive it adds to below one.
Please help, and suggest me how to fix.
Thanks
Just change your code in order to select the input of current directive like this:
function listener() {
$wrapper.find('input').on(...
and for remove element:
$wrapper.find('span')..
See the fiddle

ng-mouseleave event is not working ul li?

Create a diretive for content rating. I was used ng-mouseenter and ng-mouseleave.
There are two types content rating user can click and one is readonly.
when I passed readonly attribute ng-mouseleave is working. but When I didn't pass it is not working
basically I am using ng-mouseenter and ng-mouseleave for providing hovering effect on it and remove hovering effect.
This is my jsfiddle link
https://jsfiddle.net/amitme/3Ldad4v6/1/
var app = angular.module("glyph", []);
app.controller("contentRatingController", ['$scope', function($scope) {
$scope.rating1 = 5;
$scope.rating2 = 2.5;
$scope.isReadonly = true;
}]);
app.directive('contentRating', function contentRating() {
var defaults = JSON.parse('{}');
return {
restrict: 'E',
transclude: false,
require: '',
scope: {},
bindToController: {
ratingValue: "=ngModel",
max: "#",
onRatingSelect: "&?",
readonly: "=?",
name: "#"
},
controller: function() {
var g = this;
g.gRatingTemp = g.ratingValue;
if (g.max == undefined) {
g.max = 5;
}
g.updateStars = function(ratingValue) {
g.stars = [];
for (var i = 0; i < g.max; i++) {
g.stars.push({
filled: (i < ratingValue),
halffilled: (ratingValue)
});
}
console.log(g.stars);
};
g.updateStars(g.ratingValue);
g.toggle = function(index) {
if (g.readonly == undefined || g.readonly == false) {
g.ratingValue = index + 1;
console.log("----------toggle--------" + g.ratingValue);
if (g.gRatingTemp != g.ratingValue) {
g.gRatingTemp = g.ratingValue;
g.updateStars(g.ratingValue);
}
}
}
g.mouseEnter = function(index) {
console.log("----------mouseEnter--------" + index);
if (g.readonly == undefined || g.readonly == false) {
g.updateStars(index + 1);
}
};
g.mouseLeave = function(index) {
alert("----------mouseLeave--------" + index);
if (g.readonly == undefined || g.readonly == false) {
g.updateStars(index + 1);
}
};
},
compile: function() {
return {
pre: function(scope, el, attrs) {
for (var key in defaults) {
if (!attrs[key]) {
attrs[key] = defaults[key];
}
}
},
post: function(scope, el, attrs) {
}
};
},
template: '<ul class="rate" ng-class="{readonly: Ctrl.readonly}"> <li ng-repeat="star in Ctrl.stars" class="star" ng-mouseover="$event.stopPropagation(); Ctrl.mouseEnter($index)" ng-mouseleave="$event.stopPropagation(); Ctrl.mouseLeave($index)"> <input type="radio" id="{{Ctrl.name}}{{$index}}" name="{{Ctrl.name}}" value="{{$index+1}}" ng-model="Ctrl.ratingValue" ng-click="Ctrl.toggle($index)"/><label for="{{Ctrl.name}}{{$index}}" title="{{$index+1}}" ng-class="{filled:( star.filled && star.halffilled >= $index+1)}"></label> <input type="radio" id="{{Ctrl.name}}{{$index+1/2}}" name="{{Ctrl.name}}" value="{{$index+1/2}}" ng-model="Ctrl.ratingValue" ng-click="Ctrl.toggle($index-1/2)"/><label class="half" for="{{Ctrl.name}}{{$index+1/2}}" title="{{$index+1/2}}" ng-class="{filled: star.filled}" ></label> </li></ul><!-- <ul class="star-rating" ng-class="{readonly: Ctrl.readonly}"> <li ng-repeat="star in Ctrl.stars" class="star {{yellowStar}}" ng-class="{filled: star.filled}" ng-click="Ctrl.toggle($index)" ng-mouseenter="Ctrl.mouseEnter($index)" ng-mouseleave="Ctrl.mouseLeave($index)"> <span> <i class="fa fa-star"></i> </span> </li></ul> -->',
controllerAs: 'Ctrl',
};
});

md-date-picker with md-menu

I have a md-date-picker inside md-menu, but when I click on datepicker, md-menu closes. Here is codepen for this. It is related to this bug of ng-material. What can be workaround for this?
HTML:
<div class="md-menu-demo menudemoBasicUsage" ng-controller="BasicDemoCtrl as ctrl" ng-app="MyApp">
<div class="menu-demo-container" layout-align="center center" layout="column">
<h2 class="md-title">Month Select</h2>
<p>Select a month by clicking the input</p>
<md-menu>
<input md-menu-origin="" aria-label="Open phone interactions menu" ng-focus="ctrl.openMenu($mdOpenMenu, $event)" ng-model="ctrl.selectedMonth">
<md-menu-content width="4" ng-click="$event.stopPropagation()">
<md-datepicker ng-model="dateFilter" md-placeholder="Till date" md-min-date="dateFilter.fromDate"></md-datepicker>
</md-menu-content>
</md-menu>
</div>
</div>
JS:
angular
.module('MyApp')
.controller('BasicDemoCtrl', function DemoCtrl($mdDialog) {
var originatorEv;
this.openMenu = function($mdOpenMenu, ev) {
originatorEv = ev;
$mdOpenMenu(ev);
};
this.setMonth = function(val) {
this.month = val;
this.setSelectedMonth();
};
this.notificationsEnabled = true;
this.toggleNotifications = function() {
this.notificationsEnabled = !this.notificationsEnabled;
};
this.nextYear = function() {
this.year++;
this.setSelectedMonth();
};
this.preYear = function() {
this.year = this.year - 1;
this.setSelectedMonth();
};
}).directive('stopEvent', function() {
return {
restrict: 'A',
link: function(scope, element, attr) {
if (attr && attr.stopEvent)
element.bind(attr.stopEvent, function(e) {
e.stopPropagation();
});
}
};
});
I found a solution that works but is not the best answer.
HTML:
<md-datepicker id="myDatePicker"
ng-model="dateFilter"
md-placeholder="Till date"
md-min-date="dateFilter.fromDate">
</md-datepicker>
JS:
function setupDateButton()
{
var dateButtonFix = document.getElementById("myDatePicker").children;
for (var i = 0; i < dateButtonFix.length; i++)
{
if (dateButtonFix[i].tagName == 'BUTTON' || dateButtonFix[i].tagName == 'DIV')
{
if (dateButtonFix[i].tagName == 'DIV')
{
var child2 = dateButtonFix[i].children;
for (var j = 0; j < child2.length; j++)
{
if (child2[j].tagName == 'BUTTON')
{
child2[1].setAttribute("md-prevent-menu-close", "md-prevent-menu-close");
}
}
}
else
dateButtonFix[0].setAttribute("md-prevent-menu-close", "md-prevent-menu-close");
}
}
}
setupDateButton();
I'm sure there is a better way to do this but as of right now, it works.
Today I got to the same problem, and I created a class-restricted directive to solve this problem.
This is my directive's code:
const TRUE = 'true';
const PREVENT_CLOSE = 'md-prevent-menu-close';
class CalendarBtnFixDirective {
constructor() {
this.restrict = 'C';
this.require = '^^mdDatepicker'
}
link(scope, element, attrs, datePickerCtrl) {
const nativeElement = element[0];
const preventMenuClose = datePickerCtrl.$attrs.mdPreventMenuClose;
if ([TRUE, PREVENT_CLOSE].indexOf(preventMenuClose) !== -1) {
nativeElement.setAttribute(PREVENT_CLOSE, PREVENT_CLOSE);
}
}
}
export const MdCalendarFixModule = angular
.module('md.calendar.fix.module', [])
.directive('mdDatepickerTriangleButton', () => new CalendarBtnFixDirective())
.name;
Now in your md-datepicker you can use the md-prevent-menu-close attribute
Add md-prevent-menu-close="true" to the button or to the icon. This will prevent the menu from closing.
https://material.angularjs.org/1.0.3/api/directive/mdMenu

Password Validation with password bar & regExp special characters in angular using directive

This app is working for me BUT if someone find any error/mistake then please correct it.
I was create a small app for password validation using angular js directive. Where user can validate password that's required One Special & Capital character, and one num value with minimum length 8. i also created password strength bar with it.
Thanks
Here Plunkr Link my
Here is My HTML file :
<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap#*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script data-require="jquery#*" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="bootstrap#*" data-semver="3.3.1" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script data-require="angular.js#*" data-semver="1.4.0-beta.3" src="https://code.angularjs.org/1.4.0-beta.3/angular.js"></script>
<script src="passwordModule.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div ng-app="passwordModule" ng-controller="pwdCtrl" class="container">
<h2>Password Validation:</h2>
<form name="form">
<div class="form-group">
<label>Password</label>
<input type="text" name="password" id="password" ng-model="user.password" ng-model-options="{allowInvalid: true}"
pattern-validator="((?=.*\d)(?=.*[A-Z])(?=.*\W).{8,8})" class="form-control"/>
</div>
<span class="alert alert-error" ng-show="form.password.$error.passwordPattern">
Password required 1 special & capital letter, 1 numeric letter <br> Required minimum 8 letter.</span>
<div class="form-group">
<label>Password Strength</label>
<password-strength ng-model="user.password"></password-strength>
<label>Confirm Password</label>
<input class="form-control" type = "text" name = "Confpassword" ng-model="user.cnfPwd" data-equal-to="password" >
<div data-ng-show = "showmsg"> Password matched </div>
<div data-ng-show = "hidemsg"> Password not matched </div>
</div>
<button class="btn btn-primary" type="button" ng-disabled = "disabledButton"> save </button>
</form>
</div>
<script type="text/javascript">
</script>
</body>
</html>
Here is My Controller File :
var pwdModule = angular.module('passwordModule', []);
//Controller
pwdModule.controller('pwdCtrl', ['$scope',
function($scope) {
// Initialise the password as hello
$scope.user = {};
$scope.showmsg = false;
$scope.disabledButton = true;
if($scope.user.password === undefined) {
$scope.showmsg = false;
}
$scope.$watch('user.cnfPwd', function(newVal, oldVal) {
if(newVal !== undefined){
$scope.hidemsg = true;
}
if(newVal === $scope.user.password && $scope.user.password !== undefined) {
$scope.showmsg = true;
$scope.disabledButton = false;
$scope.hidemsg = false;
} else {
$scope.showmsg = false;
$scope.disabledButton = true;
}
})
}
]);
// Directive: Validate a regex pattern
pwdModule.directive('patternValidator', [
function() {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
var patt = new RegExp(attrs.patternValidator);
var isValid = patt.test(viewValue);
ctrl.$setValidity('passwordPattern', isValid);
return viewValue;
});
}
};
}
]);
// Dircetive: Display strength bar
pwdModule.directive('passwordStrength', [
function() {
return {
require: 'ngModel',
restrict: 'E',
scope: {
password: '=ngModel'
},
link: function(scope, elem, attrs, ctrl) {
scope.$watch('password', function(newVal) {
var strength = isSatisfied(newVal && newVal.length >= 8) +
isSatisfied(newVal && /[A-z]/.test(newVal)) +
isSatisfied(newVal && /(?=.*\W)/.test(newVal)) +
isSatisfied(newVal && /\d/.test(newVal));
var style = '',
percent= 0;
switch (strength) {
case 1:
style = 'danger';
percent = 25;
break;
case 2:
style = 'warning';
percent = 50;
break;
case 3:
style = 'warning';
percent = 75;
break;
case 4:
style = 'success';
percent = 100;
break;
}
scope.style = style;
scope.percent = percent;
function isSatisfied(criteria) {
return criteria ? 1 : 0;
}
}, true);
},
template: '<div class="progress">' +
'<div class="progress-bar progress-bar-{{style}}" style="width: {{percent}}%"></div>' +
'</div>'
}
}
])
Please check this and if any modification needed then and on it. Thanks
Speaking about errors:
isSatisfied(newVal && /[A-z]/.test(newVal)) +
Here, [A-z] matches more than English letters, it also matches [, \, ], ^, _, and `, see this SO post.
In
isSatisfied(newVal && /(?=.*\W)/.test(newVal)) +
you should anchor the look-ahead to increase performance:
isSatisfied(newVal && /^(?=.*\W)/.test(newVal)) +
^
Note that {8,8} is equivalent to {8} - exactly 8 occurrences of the preceding subpattern. Use
pattern-validator="(?=.*\d)(?=.*[A-Z])(?=.*\W).{8}"
Or (if it is not anchored by default, can't find it anywhere):
pattern-validator="^(?=.*\d)(?=.*[A-Z])(?=.*\W).{8}$"

Categories