Let's say I have 10 input elements on a page. I want to dynamically add ng-disabled to them based on the status of ng-model
For example:
<input type="text">
<input type="text">
I would not like to use a containing <fieldset></fieldset>.
Is this possible through something like a loop?
var allInputs = document.getElementsByTagName("input");
$scope.schoolClosed = true;
angular.forEach(allInputs, function(currentInput) {
currentInput.setAttribute("ng-disabled", "schoolClosed");
});
Which would yield results such as:
<input type="text" ng-disabled="schoolClosed">
<input type="text" ng-disabled="schoolClosed">
Can I add angular attribute elements through the dom like this?
Yes, you can. You can define a directive for the input element, which will get run for all inputs. Then you can have a service to store the disabled state, and "subscribe" to changes in your directive to set the disabled property directly without using ng-disabled.
I would imagine that you will need to make your service more complicated and maybe allow different named groups to toggle, rather than all or none. You could even specify the name of the group on a containing element or for each input.
You would also likely need to check in the directive whether it is a text input, so it doesn't get applied to every single radio button, checkbox, number input, etc in your application.
Full Plunkr example
var app = angular.module('plunker', []);
app.service('InputDisableService', function() {
this._isDisabled = false;
this._subscribers = [];
this.disabled = function(val) {
if (arguments.length < 1) {
return this._isDisabled;
}
var prev = !!this._isDisabled;
if (prev !== !!val) {
this._isDisabled = !!val;
for (var i = 0, len = this._subscribers.length; i < len; i++) {
this._subscribers[i].call(null, this._isDisabled, prev);
}
}
};
this.toggle = function() {
this.disabled(!this.disabled());
}
this.subscribe = function(callback) {
this._subscribers.push(callback);
// invoke immediately with current value, too
callback(this.disabled());
var self = this;
return {
unsubscribe: function() {
self.subscribers = self.subscribers.filter(function(sub) {
return sub !== callback;
});
}
};
};
});
app.directive('input', function(InputDisableService) {
return {
restrict: 'E',
link: function($scope, $element) {
var sub = InputDisableService.subscribe(function(disabled) {
$element.prop('disabled', disabled);
});
var off = $scope.$on('$destroy', function() {
sub.unsubscribe();
off();
});
}
}
});
app.controller('MainCtrl', function($scope, InputDisableService) {
$scope.inputs = [];
for (var i = 1; i <= 100; i++) {
$scope.inputs.push(i);
}
$scope.toggleDisabled = function() {
InputDisableService.toggle();
};
$scope.isDisabled = function() {
return InputDisableService.disabled();
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular.min.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl">
<p>Inputs disabled: {{isDisabled()}} Toggle</p>
<p ng-repeat="num in inputs">
<label>Input {{num}}
<input type="text">
</label>
</p>
</div>
Related
So I have a control (textbox) being created for each row in my grid view. (c#.net)
So for each of the text boxes, I want to find the current active element and return its id.
it kind of works, but I am having some issues.
I know item.addEventListener does not return a value.
Any ideas on how i can return the value after calling the addListener() function?
var controls = {
txt: null,
};
var selectedTextArea;
var items = document.getElementsByClassName("textboxnew");
for (var i = 0; i < items.length; i++) {
controls = addListener(items[i], controls);
alert(controls.txt); ///GET ERROR HERE
}
function addListener(item, ctrls) {
selectedTextArea = document.activeElement;
ctrls.txt = selectedTextArea.name.toString();
item.addEventListener("click", function () {
selectedTextArea = document.activeElement;
ctrls.txt = selectedTextArea.name.toString();
alert(ctrls);
});
return ctrls; //VALUE NOT RETURNED ???
}
You can use jQuery.Callbacks() to define a callback function that can be fired with parameters passed.
var callbacks = jQuery.Callbacks();
function active(id) {
alert(id)
}
callbacks.add(active);
$("input").on("click", function() {
callbacks.fire(document.activeElement.id)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<input id="1" autofocus tabindex="1">
<input id="2" tabindex="2">
<input id="3" tabindex="3">
Again i'm having trouble with checkboxes. I'm getting info from an API and showing like checkbox. The problem comes when i'm triying to add a validation. This is a part of my code:
(function() {
'use strict';
var fact = {
templateUrl: './app/components/fact.components.html',
controller: factCtrl
};
angular.module('fApp').component('odcFacturas', fact);
factCtrl.$inject = ["$scope", "couponApi"];
function factCtrl($scope, couponApi) {
var vm = this;
vm.clientOrder = null;
vm.all = false;
vm.sendData = function() {
vm.apiData = couponApi.get({
idOrder: vm.idOrder
}).$promise.then(function(data) {
for (var i = 0; i < data.Response.length; i++) {
data.Response[i].Select = vm.all;
}
vm.coupons = data.Response;
vm.combo = data.Response.length > 0;
});
}
Here i call the info, and the next part of my code check all the checkboxes:
vm.selectAll = function() {
for (var i = 0; i < vm.coupons.length; i++) {
vm.coupons[i].Select = vm.all;
}
if (vm.all == 0) {
alert("Select at least one coupon");
}
}
How can I trigger three validations with a submit button? I mean: what I want to do is validate three cases:
if the checkbox "select all checkboxes" is checked, submit
if there's no selected checkboxes, show the alert message
if there's at least one checkbox (or 'n' checkboxes) selected,
submit
On the HTML view i have this:
<div class ="container-fluid">
<div class="row">
<div class="col-md-6">
<div class="cbx input-group">
<div class="checkbox" name="imtesting" ng-show="$ctrl.coupons.length > 0">
<label><input type="checkbox"
ng-show="$ctrl.coupons.length > 0"
name="allCoupons"
ng-model="$ctrl.all"
ng-click="$ctrl.selectAll()"/>Select all coupons</label>
<ul>
<li ng-repeat="c in $ctrl.coupons">
<input type="checkbox"
name="couponBox"
ng-model="c.Select"
ng-click="$ctrl.result()"
required/>{{c.CodeCoupon}}
<br>
</li>
</ul>
<label class="label label-danger" ng-show="submitted == true && !ctrl.newTest()">Select at least one coupon</label>
</div>
</div>
</div>
</div>
Hope you can help me.
Thanx in advance.
You can use the Select property from each coupon object like
vm.canSubmit = function() {
for(var i = 0; i< vm.coupons.length; i++)
{
if (vm.coupons[i].Select) {
return true;
}
}
return false;
}
Redo the way you are handling your selectsAll function. When you are using angular there is a thing called scope.$apply that is actually running which tells the dom to update if the object or properties have changed. Sometimes if you use for loops the way you are using them it wont register a change.
Try this and it should work:
vm.selectAll = function()
{
vm.all = !vm.all;
vm.coupons.forEach(function(o){
o.Select = vm.all;
})
}
vm.submit = function(){
var checked = 0;
vm.coupons.forEach(function(o){
if(o.Select === true)
checked +=1;
})
if(vm.all || checked > 0){
//submit here
}
else if(checked === 0){
//error
}
}
This will work both ways. If checked it will check all and if unchecked it will uncheck all. That validation will work for all three scenarios.
I got a requirement to bind a value to a particular model when the value in the other model contains a string starting with "https".
For example, I have two text fields both fields having different model
<input type="text" ng-model="modelText1">
<input type="text" ng-model="modelText2">
Suppose I type a value on the first text field "https", the first input model modelText1 have to bind to the second input model modelText2 and later on i have to maintain it as like two-way binding. i.e. the second field will automatically get the value dynamically when it contains "https" at starting of a string.
Try it like in this Demo fiddle.
View
<div ng-controller="MyCtrl">
<input type="text" ng-model="modelText1">
<input type="text" ng-model="modelText2">
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.modelText1 = '';
$scope.modelText2 = '';
var regEx = new RegExp(/^https/);
$scope.$watch('modelText1', function (newValue) {
if (newValue.toLowerCase().match(regEx)) {
$scope.modelText2 = newValue;
} else {
$scope.modelText2 = '';
}
});
});
An other approach is (that avoid using of $watch) is to use AngularJS ng-change like in this
example fiddle.
View
<div ng-controller="MyCtrl">
<input type="text" ng-model="modelText1" ng-change="change()">
<input type="text" ng-model="modelText2">
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.modelText1 = '';
$scope.modelText2 = '';
var regEx = new RegExp(/^https/);
$scope.change = function () {
if ($scope.modelText1.toLowerCase().match(regEx)) {
$scope.modelText2 = $scope.modelText1;
} else {
$scope.modelText2 = '';
}
};
});
You can use the ng-change directive like this:
<input type="text" ng-model="modelText1" ng-change="onChange()">
<input type="text" ng-model="modelText2">
and your controller:
$scope.onChange = function() {
if ($scope.modelText1 === 'https') {
$scope.modelText2 = $scope.modelText1;
else
$scope.modelText2 = '';
};
use ng-change to check the text is equal to 'https'
angular.module('app',[])
.controller('ctrl',function($scope){
$scope.changeItem = function(item){
$scope.modelText2 = "";
if(item.toLowerCase() === "https"){
$scope.modelText2 = item
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<input type="text" ng-model="modelText1" ng-change="changeItem(modelText1)">
<input type="text" ng-model="modelText2">
</div>
EDiTED
to make sure it does't fail under 'HTTPS' use toLoweCase function to make all lower case
HTML :
<input type="text" ng-model="modelText1" ng-change="updateModal(modelText1)">
JS :
var modelText1 = $scope.modelText1.toLowerCase();
$scope.updateModal = function(){
$scope.modelText2 = '';
if(modelText1.indexOf('https')!=-1){
$scope.modelText2 = modelText1;
}
}
you could also possibly do this as a directive if you want to have a more reusable solution over multiple views http://jsfiddle.net/j5ga8vhk/7/
It also keeps the controller more clean, i always try to use the controller only for controlling complex business logic and business data
View
<div ng-controller="MyCtrl">
<input type="text" ng-model="modelText1" >
<input type="text" ng-model="modelText2" model-listener="modelText1" model-listener-value="https" >
</div>
Angular JS
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.modelText1 = '';
$scope.modelText2 = '';
});
myApp.directive('modelListener', [function() {
return {
restrict: 'A',
controller: ['$scope', function($scope) {
}],
link: function($scope, iElement, iAttrs, ctrl) {
$scope.$watch(iAttrs.modelListener, function() {
if($scope[iAttrs.modelListener] === iAttrs.modelListenerValue ) {
$scope[iAttrs.ngModel] = $scope[iAttrs.modelListener];
} else {
$scope[iAttrs.ngModel] = "";
}
}, true);
}
};
}]);
My page has a form to add objects to an array. The array is displayed on the page with links to edit the array item.
When I add items I attach a primary key to be able to edit that item later in case it is deleted and its array index is changed.
The add functionality is working but the edit behavior is not. When I update the ng-model to which the form controls are bound the form does not display the record to be edited. It might be a $scope issue but I declared the model in the parent $scope specifically to achieve this.
Here is a plunker:
http://plnkr.co/edit/yDlPpFunxFLHPiI0kCdj?p=preview
<form ng-controller="formCtrl" novalidate ng-submit="submit()">
<input type="text" name="name" ng-model="student.name" placeholder="name">
<input type="number" name="age" ng-model="student.age" placeholder="age">
<input type="hidden" ng-value="pk">
<input type="submit">
</form>
<h1>students</h1>
<ul>
<li ng-repeat="item in students">{{item.name}} - {{item.age}}</li>
</ul>
var myApp = angular.module('myApp',[]);
myApp.controller("myCtrl", ['$scope', function($scope ){
$scope.student = {};
$scope.pk = 1;
$scope.index = 0;
$scope.students = [];
$scope.editStudent = function (id) {
for (var i = 0; i < $scope.students.length; i++) {
console.log("comparing "+$scope.students[i].pk+ " & " + id);
if ($scope.students[i].pk == id ) {
console.log("editing pk nr.: "+ id);
$scope.student = {
name: $scope.students[i].name,
age: $scope.students[i].age
};
$scope.index = id;
}
}
};
}]);
myApp.controller("formCtrl", ['$scope', function($scope) {
$scope.submit = function () {
if ($scope.index === 0) {
$scope.students.push({
name: $scope.student.name,
age: $scope.student.age,
pk: $scope.pk
});
$scope.pk++;
$scope.student = {};
} else {
for (var i = 0; i < $scope.students.length; i++) {
if ($scope.students[i].pk == $scope.index) {
$scope.students[i] = {
name: $scope.student.name,
age: $scope.student.age,
pk: $scope.index
};
}
}
$scope.index = 0;
}
};
}]);
Thanks
I have edited your plunkr, see the changes here.
The changes i made were:
Removed the form controller, no need for 2 controllers in your app.
Used the $index property that is available when using ng-repeat to edit the item you click.
Here is your HTML:
<div ng-app="myApp" ng-controller="myCtrl as ct">
<form novalidate>
<input type="text" name="name" ng-model="newStudent.name" placeholder="name">
<input type="number" name="age" ng-model="newStudent.age" placeholder="age">
<input type="hidden" ng-value="pk">
<input type="button" value="submit" ng-click="submit()">
</form>
<h1>students</h1>
<ul>
<li ng-repeat="item in students">{{item.name}} - {{item.age}}</li>
</ul>
</div>
And your JavaScript File:
var myApp = angular.module('myApp',[]);
myApp.controller("myCtrl", ['$scope', function($scope ){
$scope.newStudent = {};
$scope.students = [];
$scope.index = -1;
$scope.editStudent = function (index) {
console.log('Editing student: ' + $scope.students[index].name);
$scope.index = index;
$scope.newStudent = angular.copy($scope.students[index]);
};
$scope.submit = function () {
if ($scope.index < 0) {
$scope.students.push($scope.newStudent);
} else {
$scope.students[$scope.index] = $scope.newStudent;
}
$scope.newStudent = {
name: '',
age: ''
}
$scope.index = -1;
};
}]);
Edit:
I just modified the code for when editing the student, to use angular.copy so that when you are editing, it loses the binding to the $scope.students array, and the changes are only applied when you click on the submit button.
Using just jQuery (not validation plugin) I have devised a way to do a "if one, then all" requirement, but it's not at all elegant.
I'm wondering if someone can come up with a more elegant solution? This one uses some loop nesting and I'm really not pleased with it.
if ($("[data-group]")) {
//Store a simple array of objects, each representing one group.
var groups = [];
$("[data-group]").each(function () {
//This function removes an '*' that is placed before the field to validate
removeCurError($(this));
var groupName = $(this).attr('data-group');
//If this group is already in the array, don't add it again
var exists = false;
groups.forEach(function (group) {
if (group.name === groupName)
exists = true;
});
if (!exists) {
var groupElements = $("[data-group='" + groupName + "']");
var group = {
name: groupName,
elements: groupElements,
trigger: false
}
group.elements.each(function () {
if (!group.trigger) {
group.trigger = $(this).val().length !== 0;
}
});
groups.push(group);
}
});
//Now apply the validation and alert the user
groups.forEach(function (group) {
if (group.trigger) {
group.elements.each(function () {
//Make sure it's not the one that's already been filled out
if ($(this).val().length === 0)
// This function adds an '*' to field and puts it into a
// a sting that can be alerted
appendError($(this));
});
}
});
You don't have to store the groups in an array, just call the validateGroups function whenever you want to validate the $elements. Here is a working example http://jsfiddle.net/BBcvk/2/.
HTML
<h2>Group 1</h2>
<div>
<input data-group="group-1" />
</div>
<div>
<input data-group="group-1" />
</div>
<h2>Group 2</h2>
<div>
<input data-group="group-2" value="not empty" />
</div>
<div>
<input data-group="group-2" />
</div>
<div>
<input data-group="group-2" />
</div>
<button>Validate</button>
Javascript
function validateGroups($elements) {
$elements.removeClass('validated');
$elements.each(function() {
// Return if the current element has already been validated.
var $element = $(this);
if ($element.hasClass('validated')) {
return;
}
// Get all elements in the same group.
var groupName = $element.attr('data-group');
var $groupElements = $('[data-group=' + groupName + ']');
var hasOne = false;
// Check to see if any of the elements in the group is not empty.
$groupElements.each(function() {
if ($(this).val().length > 0) {
hasOne = true;
return false;
}
});
// Add an error to each empty element if the group
// has a non-empty element, otherwise remove the error.
$groupElements.each(function() {
var $groupElement = $(this);
if (hasOne && $groupElement.val().length < 1) {
appendError($groupElement);
} else {
removeCurError($groupElement);
}
$groupElement.addClass('validated');
});
});
}
function appendError($element) {
if ($element.next('span.error').length > 0) {
return;
}
$element.after('<span class="error">*</span>');
}
function removeCurError($element) {
$element.next().remove();
}
$(document).ready(function() {
$('button').on('click', function() {
validateGroups($("[data-group]"));
});
});
You might get some milage out of this solution. Basically, simplify and test your solution on submit click before sending the form (which this doesn't do). In this case, I simply test value of the first checkbox for truth, and then alert or check the required boxes. These can be anything you like. Good luck.
http://jsfiddle.net/YD6nW/1/
<form>
<input type="button" onclick="return checkTest()" value="test"/>
</form>
and with jquery:
checkTest = function(){
var isChecked = $('input')[0].checked;
if(isChecked){
alert('form is ready: input 0 is: '+isChecked);
}else{
$('input')[1].checked = true;
$('input')[2].checked = true;
}
};
//create a bunch of checkboxes
$('<input/>', {
type: 'checkbox',
html: 'tick'
}).prependTo('form');
$('<input/>', {
type: 'checkbox',
html: 'tick'
}).prependTo('form');
$('<input/>', {
type: 'checkbox',
html: 'tick'
}).prependTo('form');