I'm using angular & trying to prevent the user from entering alphabet into text field and at the same item push the model onto array. But the logic doesn't seem to work perfectly and sometimes allows alphabets. How can I prevent special characters like $,% from being entered ?
HTML
<ul>
<li ng-repeat="item in arr track by $index">
<input type="text" number ng-change="itemChange()" ng-model="item" />
<button ng-click="add()">Add Item</button>
</li>
</ul>
JS
app.directive('number', function(){
return {
require: 'ngModel',
link: function(scope, elem, attr, ctrl){
elem.bind('keyup', function(e) {
console.log(e)
var text = this.value;
this.value = text.replace(/[a-zA-Z]/g,'');
});
}
};
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.arr = [];
//Initialize with one element
$scope.arr[0] = '';
//Push when there is a change in the input
$scope.itemChange = function() {
$scope.arr[this.$index] = this.item;
}
//Add an empty item at the end
$scope.add = function() {
$scope.arr[$scope.arr.length] = '';
}
});
Demo : http://plnkr.co/edit/t8OE5uJ578zgkiUTjHgt?p=preview
Try this:
app.directive('alphabetonly', function(){
return {
require: 'ngModel',
link: function(scope, elem, attr, ctrl){
ctrl.$parsers.push(function(inputValue) {
var transformedInput = inputValue.replace(/[^\w\s]/gi,'');
if (transformedInput != inputValue) {
ctrl.$setViewValue(transformedInput);
ctrl.$render();
}
return transformedInput;
});
}
};
})
See plunk.
Related
So I have a custom directive which works fine as it is. This directive is being used at multiple places. This is an element directive.
This element directive has certain attributes. I have added a custom attribute for only 1 instance of this directive i.e. only at 1 particular usage of this directive I have added an extra attribute for this element.
Here is the directive being used in the HTML:
<attribute-types target-model="patient" attribute="::attribute"
field-validation="::fieldValidation"
is-auto-complete="isAutoComplete"
get-auto-complete-list="getAutoCompleteList"
get-data-results="getDataResults" is-read-only="isReadOnly"
handle-update="handleUpdate" validate-autocomplete="true">
</attribute-types>
The validate-autocomplete is the extra attribute I have used at 1 place use of this directive.
Here is the template for the directive:
<div class="left" data-ng-switch-when="org.openmrs.Concept" ng-if="attribute.name == 'PATIENT_OCCUPATION'" style="position: absolute">
<input type="text"
class="ui-autocomplete-input"
id="{{::attribute.name}}"
name="{{::attribute.name}}"
ng-model="targetModel[attribute.name].value"
ng-keyup="suggest(targetModel[attribute.name])"
ng-required="{{::attribute.required}}">
<ul class="ui-front ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all" ng-if="showTag" ng-hide="hideList"
style="position:absolute; top:30px; width:192px">
<li class="ui-menu-item" role="presentation" ng-repeat="info in filterOcuppation"
ng-click="hideSuggestions(info)">
<a class="ui-corner-all" tabindex="-1">{{info.description}}</a>
</li>
</ul>
</div>
And this is the directive definition:
angular.module('bahmni.common.attributeTypes', [])
.directive('attributeTypes', [function () {
var link = function (scope, element, attrs, ngModelCtrl) {
var formElement = element[0];
if (attrs.validateAutocomplete) {
ngModelCtrl.$setValidity('selection', true);
}
};
return {
link: link,
scope: {
targetModel: '=',
attribute: '=',
fieldValidation: '=',
isAutoComplete: '&',
handleLocationChange: '&',
handleSectorChange: '&',
getAutoCompleteList: '&',
getDataResults: '&',
handleUpdate: '&',
isReadOnly: '&',
isForm: '=?'
},
templateUrl: '../common/attributeTypes/views/attributeInformation.html',
restrict: 'E',
controller: function ($scope) {
var dateUtil = Bahmni.Common.Util.DateUtil;
$scope.getAutoCompleteList = $scope.getAutoCompleteList();
$scope.getDataResults = $scope.getDataResults();
$scope.today = dateUtil.getDateWithoutTime(dateUtil.now());
// to avoid watchers in one way binding
$scope.isAutoComplete = $scope.isAutoComplete() || function () { return false; };
$scope.isReadOnly = $scope.isReadOnly() || function () { return false; };
$scope.handleUpdate = $scope.handleUpdate() || function () { return false; };
$scope.handleLocationChange = $scope.handleLocationChange() || function () { return false; };
$scope.handleSectorChange = $scope.handleSectorChange() || function () { return false; };
$scope.suggestions = $scope.attribute.answers;
$scope.showTag = false;
$scope.itisinvalid = true;
$scope.appendConceptNameToModel = function (attribute) {
var attributeValueConceptType = $scope.targetModel[attribute.name];
var concept = _.find(attribute.answers, function (answer) {
return answer.conceptId === attributeValueConceptType.conceptUuid;
});
attributeValueConceptType.value = concept && concept.fullySpecifiedName;
};
$scope.suggest = function (string) {
$scope.hideList = false;
$scope.showTag = true;
var output = [];
angular.forEach($scope.suggestions, function (suggestion) {
if (suggestion.description.toLowerCase().indexOf(string.value.toLowerCase()) >= 0) {
output.push(suggestion);
}
});
$scope.filterOcuppation = output;
};
$scope.hideSuggestions = function (object) {
$scope.targetModel[$scope.attribute.name] = object;
$scope.targetModel[$scope.attribute.name].value = object.description;
$scope.targetModel[$scope.attribute.name].conceptUuid = object.conceptId;
$scope.hideList = true;
};
}
};
}]);
When running this I get TypeError: ngModelCtrl.$setValidity is not a function
What I'm basically doing it validating whatever is entered into the input text is valid or not. For that I would also need the ng-model, how would I access that in my link function?
If I have written some wrong, feel free to correct me. I'm still in the process of learning AngularJS
You should use directive like that:
directive('attributeTypes', [function() {
return {
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
...
ngModel.$setValidity(...
I'm currently trying to pass a validation directive to a custom element directive. But I'm struggling to make it work since it should receive model as an input while I am using bind to controller.
I have to premise that I cannot upgrade to a more recent version of Angular, so 1.5 is the limitation, together with the fact I cannot edit validation directive.
I thought transclude would have helped but with directive attribute it looks not so promising.
What the following code should do is to validate vm.model on input element.
Here's the HTML:
<body ng-controller="MainCtrl">
<div class="myClass">
<my-custom-directive data-placeholder="No text"
data-id="myModel.id"
data-model="myModel.text"
not-editable-directive-attribute >
</my-custom-directive>
</div>
</body>
And here the app.js:
var myTemplate = '<div class="myContainer">' +
'<input class="myInput"' +
' ng-mousedown="$event.stopPropagation();"' +
' ng-show="vm.isFocused"' +
' ng-model="vm.model"' +
' ng-change="vm.onChange()"' +
' type="text">' +
'<span ng-show="!vm.isFocused">{{vm.model}}</span>' +
'<span ng-show="!vm.isFocused && !vm.model && vm.placeholder">{{vm.placeholder}}</span>' +
'</div>';
app.controller('MainCtrl', function($scope) {
$scope.myModel = {
id: 'test',
text: 'this is text'
};
});
app.directive('myCustomDirective', ['$timeout', function($timeout) {
return {
restrict: 'E',
replace: true,
template: myTemplate,
controllerAs: 'vm',
bindToController: {
id: '#',
model: '=',
onChange: '&',
placeholder: '#'
},
scope: {},
controller: angular.noop,
link: function(scope, element) {
var input = element.find('input')[0];
var spans = Array.from(element.find('span'));
var vm = scope.vm;
vm.isFocused = false;
vm.focus = function() {
vm.isFocused = true;
scope.$applyAsync(function() {
$timeout(function() {
input.focus();
input.select();
});
});
};
spans.forEach(span => span.addEventListener('click', vm.focus));
}
};
}]);
app.directive('notEditableDirectiveAttribute', [function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.myCustomDirectiveAttribute = function(modelValue, viewValue) {
if (viewValue) {
return viewValue.indexOf('e') < 0;
}
return false;
};
}
};
}]);
I've created a plunker to make it clearer:
http://plnkr.co/edit/auminr?p=preview
So clicking on span element i should be able to edit text and directive should validate it (in this specific case check if it contains letter "e").
Is it even possible or am I struggling against windmills?
One approach to adding directives to templates based on component attributes is to use the function form of the template property:
<my-custom-directive data-placeholder="No text"
model="vm.data"
custom="not-editable-directive-attribute" >
</my-custom-directive>
app.directive("myCustomDirective", function() {
return {
template: createTemplate,
scope: {},
//...
});
function createTemplate(tElem, tAttrs) {
var placeholder = tAttrs.placeholder;
var model = tAttrs.model;
var custom = tAttrs.custom;
return `
<input placeholder=${placeholder}
ng-model=${model}
${custom} />
`;
}
})
The createTemplate function copies attributes and used them in a template literal.
For more information, see
AngularJS Comprehensive Directive API - template
MDN JavaScript Reference - Template Literals
I am in learning phase of Angularjs and am stuck in a problem for last two days. I have seen lots of answer but don't know how to adapt those solutions in my case. What I want to do is update the input field via buttons using angularjs.
// html
<body ng-controller="Controller">
<input type="number" ng-model="data" update-view>
<br>
<label for="data">{{data}}</label>
<button name="btn1" ng-click='updateInput(1)'>1</button>
</body>
// js
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = val;
// val = param;
// $scope.data = val;
}
});
The expressions gets evaluated but the input field is not updating. I have seen other updating views with $setViewValue and $render but I don't know how to use them here.
app.directive('updateView', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('change', function () {
// console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue(scope.data);
ngModel.$render();
}
}
};
});
Any help would be appreciated. Thanks
You don't need a directive for updating.
You seem to be setting a string value to $scope.data, which throws an error, because the input type is number.
angular.module('calculator', [])
.controller('Controller', function($scope){
$scope.data = 0;
var val = '';
$scope.updateInput = function(n){
val = n;
$scope.data = val;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="calculator" ng-controller="Controller">
<input type="number" ng-model="data">
<button ng-click="updateInput(1)">1</button>
</body>
I was noting that without converting the parameter into string, the input field would update with the changed model but as soon as I would change it into String, it would not update the input field. Also there was error thrown in console. So I just, on hit and trial basis, converted it back to int by changing only one piece of line $scope.data = val; into $scope.data = parseInt(val, 10); and hurrrayyy the input field is all updating just like I wanted. And as #cither suggested, I don't need to directive for this. Following is my working code
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = parseInt(val, 10);
}
});
please watch this Plunker
So I working with angular and need to add new input field when all others are filled in (by default on page placed 5 inputs and if all of them are filled automatically add one more input if new input also using will add one more input and etc).
For generate inputs I use ng-repeat and name_list[] for it:
<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>
Each input have directive attr "add-input" with $watch() method inside. This method method track when $isEmpty parameter had changed.
Then value function pass value of this parameter to listen function.
directive('addInput', ['$compile', '$sce', '$timeout', function ($compile, $sce, $timeout) {
return {
restrict: 'A',
require: ['^collectInput', '?ngModel'],
link: function (scope, element, attrs, ctrl) {
var collectInput = ctrl[0];
var ngModel = ctrl[1];
$timeout(function(){
scope.$watch(
function(){
return ngModel.$isEmpty(ngModel.$modelValue);
},
function(isEmpty){
collectInput.reportInput(ngModel, isEmpty);
}
);
},1000)
}
}
}]);
Then this function call "reportInput()" that placed inside parent directive "collect-input". Main goal of this function is to add new input name to name_list[] for generating via ng-repeat
userApp.directive('collectInput', function() {
return {
restrict: 'A',
controller: function($scope) {
var dirtyCount = 0;
this.reportInput = function(modelValue, isEmpty) {
var count = $scope.name_list.length;
if (isEmpty == false){
dirtyCount ++;
console.log('+1:' + dirtyCount);
}
if (isEmpty == true){
if (dirtyCount <= 0){
dirtyCount = 0;
console.log('0:' + dirtyCount);
}
else if(dirtyCount > 0){
dirtyCount --;
console.log('-1:' + dirtyCount)
}
}
if (count === dirtyCount) {
$scope.name_list.push(modelValue);
//dirtyCount = dirtyCount + 1;
}
console.log('count:' + count);
console.log('dirtyCount:' + dirtyCount);
console.log(modelValue)
}
},
link: function(scope, element, attrs) {
}}});
So when I filled 5 default inputs everything is good after it appears new input but it is all in my IDE it work perfect if I add only one symbol for 5+ label (in plunker in some reason it not work) but when I add or delete something more code logic crash. It's hard to explain. I hope Plunker code more clarify this.
Not tested, and could be optimized, but here's my idea:
HTML :
<div class="form-group" ng-repeat="name in name_list">
<div class="row">
<div class="col-xs-12">
<input class="form-control" ng-model="name"/>
</div>
</div>
</div>
JS :
//watch any modification in the list of names
$scope.$watchCollection('data.name_list', function (list) {
//is there an empty name in the list?
if (!list.filter(function (name) { return !name; }).length) {
//if not, let's add one.
data.name_list.push('');
//and that will automatically add an input to the html
}
});
I don't see the point of a directive.
Using the Ionic framework, I'm trying to create a group of three buttons that act as radio buttons:
If I click on Breakfast, I would like Lunch and Dinner to return to their normal (white) state, and Breakfast to turn Blue.
With my current code, I can't get this functionality to work, although I can get the buttons to switch color, slightly randomly (perhaps I just don't understand the ng-class directive).
Here is my HTML code:
<div class="bar bar-subheader">
<div class="button-bar">
<a class="button" ng-class="{'button-positive' : !isActiveB, 'none': isActiveB}" ng-click="active('breakfast')">Breakfast</a>
<a class="button" ng-class="{'button-positive' : !isActiveL, 'none': isActiveL}" ng-click="active('lunch')">Lunch</a>
<a class="button" ng-class="{'button-positive' : !isActiveD, 'none': isActiveD}" ng-click="active('dinner')">Dinner</a>
</div>
</div>
My JS:
$scope.active = function(meal) {
switch (meal) {
case 'breakfast':
$scope.$broadcast('slideBox.setSlide', 0);
$scope.isActiveB = $scope.isActiveB;
$scope.isActiveL = !$scope.isActiveL;
$scope.isActiveD = !$scope.isActiveD;
break;
case 'lunch':
$scope.$broadcast('slideBox.setSlide', 1);
$scope.isActiveB = !$scope.isActiveB;
$scope.isActiveL = $scope.isActiveL;
$scope.isActiveD = !$scope.isActiveD;
break;
case 'dinner':
$scope.$broadcast('slideBox.setSlide', 2);
$scope.isActiveB = !$scope.isActiveB;
$scope.isActiveL = !$scope.isActiveL;
$scope.isActiveD = $scope.isActiveD;
break;
}
};
I can put the code in JSFidle if you require more information and a working solution.
Thanks for your help.
NOTE: I would like to maintain my active() function, and use the ng-class directive if possible, as I have a lot of other code dependent on this function.
Maybe this simplified example will help you a little:
angular.module('plunker', []).controller('MainCtrl', function($scope) {
$scope.active = 'breakfast';
$scope.setActive = function(type) {
$scope.active = type;
};
$scope.isActive = function(type) {
return type === $scope.active;
};
});
<link rel="stylesheet" href="http://code.ionicframework.com/0.9.26/css/ionic.min.css">
<script src="http://code.angularjs.org/1.2.13/angular.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl" class="bar bar-subheader">
<div class="button-bar">
<a class="button" ng-class="{'button-positive': isActive('breakfast')}" ng-click="setActive('breakfast')">Breakfast</a>
<a class="button" ng-class="{'button-positive': isActive('lunch')}" ng-click="setActive('lunch')">Lunch</a>
<a class="button" ng-class="{'button-positive': isActive('dinner')}" ng-click="setActive('dinner')">Dinner</a>
</div>
</div>
Demo: http://plnkr.co/edit/9HmuTStz70x5KoAvLaP4?p=preview
Here is a more flexible solution for future Googlers.
Working plunker:
http://plnkr.co/edit/U2Hvx4?p=preview
.directive('barSelect',function($parse){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '=barSelect'
},
link: function(scope, element, attrs, ngModelCtrl){
element.addClass('button');
element.on('click', function(e){
scope.$apply(function(){
ngModelCtrl.$setViewValue(scope.value);
});
});
scope.$watch('model', function(newVal){
element.removeClass('active');
if (newVal === scope.value){
element.addClass('active');
}
});
}
};
});
And a usage example:
<div class="button-bar">
<a bar-select="button.value"
ng-repeat="button in clientSideList"
ng-model="data.clientSide"
>{{button.text}}</a>
</div>
Here's another alternative approach which combines the other two here. It requires just a single <button-group> element with the following attributes:
ng-model
buttons - array of objects containing 'text' and 'value' properties
button-class - optional string containing CSS class(es) to apply to the rendered links, in addition to the default 'group-btn' and 'group-btn-active' classes
.
.directive('buttonGroup',function($parse){
return {
restrict: 'E',
require: 'ngModel',
scope: {
model: '=ngModel',
buttons: '=',
buttonClass: '='
},
template: '<a class="group-btn {{buttonClass}}" ' +
' ng-repeat="button in buttons" ' +
' ng-class="{\'group-btn-active\': isActive(button.value)}" ' +
' ng-click="buttonClicked(button.value)"> ' +
' {{button.text}} ' +
'</a>',
controller: ['$scope', function($scope) {
$scope.buttonClicked = function(value) {
$scope.value = value;
};
$scope.isActive = function(value) {
return $scope.value === value;
};
}],
link: function(scope, element, attrs, ngModel) {
element.on('click', function(e){
scope.$apply(function(){
ngModel.$setViewValue(scope.value);
});
});
scope.$watch('model', function(newVal){
scope.value = newVal;
});
}
};
})
And the example usage:
<button-group ng-model="sortOrder" buttons="sortOptions"
button-class="'md-button my-other-class'"></button-group>
Where sortOptions would be an array of the form:
$scope.sortOptions = [
{ value: 'priority', text: 'Priority' },
{ value: 'duration', text: 'Call Duration' }
];