Changing single cell color with ngClass - angularjs

I'm having a hard time here with ngClass as I'm new to angular. I've got a table set up that generates rows dynamically from a database using ng-repeat, with span and input elements that hide/show based on editing/not editing the cell (imagine microsoft excel).
<tr>
<th class="row_head">Row Title</th>
<td ng-click="edit = true" ng-repeat="item1 in JSONobject | orderBy: '_id'" >
<span ng-hide="edit">{{ item1.key.value1 }}</span>
<input ng-show="edit" class="editing-cell" ng-model="item1.key.value1" ng-blur="edit = false; " ng-enter="saveData(item1._id, item1.key); edit = false" type="text" />
</td>
</tr>
<tr>
<th class="row_head">Row Title</th>
<td ng-click="edit = true" ng-repeat="item2 in JSONobject | orderBy: '_id'" >
<span ng-hide="edit">{{ item2.key.value2 }}</span>
<input ng-show="edit" class="editing-cell" ng-model="item2.key.value2" ng-blur="edit = false; " ng-enter="saveData(item2._id, item2.key); edit = false" type="text" />
</td>
</tr>
and so on.
I want to set it so on blur (data not saved) the cell turns red, and alternatively on save - turns green.
The issue i'm running into with ngClass is if i just go with:
ng-class="{'saved': saved, 'notSaved': notSaved}"
It changes the background color of EVERY cell, rather than just the cell edited.
I do not have this issue with ng-hide and ng-show, even though they appear to be a similar scenario with changing the boolean value of 'edit'. They still stick to their specific cell.
Any help would be appreciated.

You should look into how form styling works.
The following classes are added to, or removed from, input fields:
ng-untouched The field has not been touched yet
ng-touched The field has been touched
ng-pristine The field has not been modified yet
ng-dirty The field has been modified
ng-valid The field content is valid
ng-invalid The field content is not valid
ng-valid-key One key for each validation. Example: ng-valid-required, useful when there are more than one thing that must be validated
ng-invalid-key Example: ng-invalid-required
The following classes are added to, or removed from, forms:
ng-pristine No fields has not been modified yet
ng-dirty One or more fields has been modified
ng-valid The form content is valid
ng-invalid The form content is not valid
ng-valid-key One key for each validation. Example: ng-valid-required, useful when there are more than one thing that must be validated
ng-invalid-key Example: ng-invalid-required
The classes are removed if the value they represent is false.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<style>
form.ng-pristine {
background-color:lightblue;
}
form.ng-dirty {
background-color:pink;
}
</style>
<body ng-app="">
<form name="myForm">
<p>Try writing in the input field:</p>
<input name="myName" ng-model="myName" required>
<p>The form gets a "ng-dirty" class when the form has been modified, and will therefore turn pink.</p>
</form>
</body>
</html>

My issue was not knowing enough about custom directives. I've got it figured out now with
.directive('isSaved', function(){
return {
link: function(scope, elem, attr) {
scope.notSaved = function(){
attr.$set('class', 'notSaved');
};
scope.saved = function(){
attr.$set('class', 'saved');
};
}
};
});

Related

Values are duplicated into all input field which is created using the ng-repeat and ng-model

I am using the ng-repeat to create the input fields dynamically, everything seems to be working fine and the input fields are created but due to some reason when I enter some value into that created input field then due to two-way data binding the value is entered simultaneously in all the text fields that were created.
As per my knowledge, I am assigning the different ng-model to each of the created fields so I am not sure why the values are being copied. When I try to print the values which are assigned to the input field just above the field then I see the id is different wanted to know why all the fields are taking the same value and also how can I prevent it.
HTML Code:
<tr>
<td ng-repeat="extension in ExtensionList">
<!-- Here the value displayed are different -->
{{ formdata.extension.ExtensionVlaues }}
<input type="text" class="form-control" id="extension.ExtensionVlaues" ng-model="formdata.extension.ExtensionVlaues">
</td>
</tr>
Here is the screenshot from the front-end:
Here is the example of my ExtensionList: {"Field1":"","Field2":"","Field3":1,"ExtensionVlaues":2}
As per my knowledge, the given ng-model is different but still not working. Can anyone please help me with this? I tried few things like ng-model="formdata.extension[ExtensionVlaues]" but still no luck.
I tried to assign ng-model like this but it is not working:
ng-model="formdata.extension[ExtensionVlaues]"
Based on the below answer I tried 2 different things it but it's not working and getting the same issue. Please let me know how can I resolve this:
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata.extension.key">
</td>
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata.extension.ExtensionVlaues">
</td>
You are trying to use ExtensionList as an array. You need to use it as an object with ng-repeat:
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata[extension][key]">
</td>
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata[extension][ExtensionVlaues]">
</td>
Just incase anyone else also struck at this issue:
<span ng-repeat="(key, extension) in ExtensionList" class="form-inline">
<br/>
<span>
{{ extension.NameSpace + ":" + extension.LocalName }}
</span> 
<input type="text" class="form-control" id="extension.ExtensionVlaues" ng-model="ExtensionList.extension[key].FreeText" ng-blur="ExtensionText(ExtensionList.extension[key].FreeText, extension.ExtensionVlaues)">
</span>

Custom validation via child control

So I'm working inside a directive that contains it's own form element and buttons, however all the controls must be transcluded through. The model for this particular view contains a property for total capacity, and a property that is a collection of compartments (separate entity). Each compartment has it's own capacity. I already have a function that will show an error on the view if/when the Total Capacity is not equal to the combined capacity of all compartments. The problem here is, since all my controls are transcluded through (and I'm not supposed to modify the parent directive) I have no clue if/how I can use that same function to mark the form as invalid to disable the save button. I was wondering if there is a solution (hopefully one that doesn't involve custom directives or services) that would allow me to set the parent form invalid if an expression returns true.
** UPDATE **
Sorry guys, I think I explained it backwards the first time. So this would be a good representation of what is going on in the html. (Also I haven't used stackoverflow much before this so bear with me)
edit-page-directive:
<div>
<form name="editForm">
<ng-transclude>
</ng-transclude>
<a class="btn btn-success">Save</a>
<a class="btn btn-danger">Cancel</a>
</form>
</div>
View for this particular edit:
<edit-page>
<uib-tabset>
<uib-tab>
<!--Total Capacity input-->
<input type="text" numeric="{min:1, format:'#,###.#'}" ng-model-options="{updateOn: 'blur'}" class="form-control" id="tcCapacity" name="tcCapacity" data-ng-required="true" ng-model="vm.dataContext.entity.TotalCapacity" />
<!--End Total Capacity-->
</uib-tab>
<uib-tab>
<table>
<tr><thead><th>...</th><th>Capacity</th><th>(Buttons for compartment add/remove)</th></thead></tr>
<tr ng-repeat="compartment in vm.dataContext.entity.TrailerConfigCompartments">
<td width="200">{{compartment.Sequence}}</td>
<!--Important input under this-->
<td><input type="text" numeric="{min:0, format:'#,###.#'}" class="form-control" ng-model="compartment.Capacity" data-ng-required="true" /></td>
<!--Important input above-->
<td align="right" style="padding-right:30px;">
<a class="btn" style="padding: .7em; color: black;" ng-click="vm.addCompartment(compartment.Sequence + 1)">
<span uib-tooltip="New compartment at sequence {{compartment.Sequence + 1}}" class="btn-edit" style='margin-left:5px'><span class="glyphicon glyphicon-plus" style="margin-top:3px"></span></span>
</a>
<a class="btn" style="padding: .7em; color: black;" ng-click="vm.removeCompartment(compartment)">
<span uib-tooltip="Remove compartment" class="btn-edit" style='margin-left:5px'><span class="glyphicon glyphicon-minus" style="margin-top:3px"></span></span>
</a>
</td>
</tr>
</table>
</uib-tab>
</uib-tabset>
</edit-page>
If I understand you correctly you have something like
HTML
<div data-ng-controller="FormController as vm">
<form class="foo form">
<input type="text"> // some inputs
<input type="text"> // some inputs
<transcluded-directive>
<button class="foo button-to-disable">Do something</button> // button that should be disabled
</transcluded-directive>
</form>
</div>
JS
.controller("FormController", function($scope) {
var vm = this;
vm.validateTotalCapacity = function () {
// validation stuff
}
});
So I think you can do something like:
HTML
<div data-ng-controller="FormController as vm">
<form class="foo form {{vm.validateTotalCapacity() ? '' : 'form-has-errors'}}" >
<input type="text"> // some inputs
<input type="text"> // some inputs
<transcluded-directive>
<button class="foo button-to-disable">Do something</button> // button that should be disabled
</transcluded-directive>
</form>
</div>
Look I put your form validator in <form class="foo form"> and make condition for error class
CSS
.form-has-errors .button-to-disable {
pointer-events: none;
cursor: default;
opacity: 0.5
// or your custom disabled styles
}
UPDATE
I see, but I believe you could try this:
HTML
<div>
<form name="editForm" class="{{editForm.$valid ? '' : 'form-has-errors '}}">
<ng-transclude>
</ng-transclude>
<a class="btn btn-success">Save</a>
<a class="btn btn-danger">Cancel</a>
</form>
</div>
So I realized I had misinterpreted the customValidation piece of angularjs. I thought any directive I'd have to create for validation would have to be added to the form element itself. Just as well I thought it would be alot harder to set up than it actually is.
For future reference:
1.) Create a directive and restrict it to an attribute
2.) Require ngModel for this directive
3.) Set up your link function:
link: function(scope, elem, attrs, ngModel) {....}
4.) Add a function to the $validators object of the control you want to validate. Do this INSIDE of your link function. Ex:
link: function(scope, elem, attrs, ngModel) {
ngModel.$validators.validationFn = function(value) {
//Where value is the current value of the control
//In my case, where I want to compare value to the combined value of other
//compartments I would send in whatever data I wanted via the scope property of
//this directive and compare the two in this function
}
}
5.) Return true if control is valid and vice versa
And that's it.
If you want to access this validator to display an error message just:
ng-show="vm.arbitraryInput.$error.validationFn"
Keep in mind that now if it returns true, then the input is invalid.

Configuring validation tooltips with angular-ui

I am doing a simple form using angularjs with angular-ui. Somehow angular is inserting boostrap tooltips into my validation. But it is getting inserted automatically and I do not know how to control/customize or disable the tooltips. Here is my html:
<form name="frm" ng-submit="contact.contactUsSubmit(frm)">
<table id="tblContactUs">
<tr>
<td id="tblContactUsTitleTd">
Send us an email
</td>
</tr>
<tr>
<td>
<span class="LabelStyle1">Name:</span><br/>
<input type="text" tooltip-trigger="0" name="name" style="width: 75%" ng-model="contact.formInfo.name" required/><br/>
<span ng-show="frm.name.$dirty && frm.name.$error.required" class="errorMsg">Required!<br /></span>
<span class="LabelStyle1">Email:</span> <br/>
<input type="email" name="email" style="width: 75%" ng-model="contact.formInfo.email" required/><br/>
<span ng-show="frm.email.$dirty && frm.email.$error.required" class="errorMsg">Required!<br /></span>
<span ng-show="frm.email.$dirty && frm.email.$error.email" class="errorMsg">Not a valid email!<br /></span>
<span class="LabelStyle1">Message:</span> <br/>
<textarea name="message" rows="5" style="width: 100%" ng-model="contact.formInfo.message" required></textarea><br/>
<span ng-show="frm.email.$dirty && frm.name.$error.required" class="errorMsg">Required!</span>
</td>
</tr>
<tr>
<td style="text-align: right">
<button>Submit</button>
</td>
</tr>
</table>
</form>
:
But I am getting this:
Yes the tooltip is nice. But I didn't say I wanted it there. IF I want to use the tooltip for validation, I need to be able to customize the message (for different languages). I have googled the bejesus out the topic but I can't find answers as to how to configure it or to turn it off. I don't even know why its there in the first place. Any help would be much appreciated.
Those aren't bootstrap or even css related in any way.
They are html5 validation tooltips that are part of the browser itself.
If you don't want to use built in browser validation add the novalidate attribute to <form> tag.
<form name="frm" ng-submit="contact.contactUsSubmit(frm)" novalidate>
Reference MDN <form> docs
Thank you to charlietfl for putting me on the right track with "novalidate." The problem with novalidate, however, is that when you hit ng-submit none of the validation error messages showed up.
The complete solution was to add the following code to the form tag ("contact" an alias for "contactController":
<form name="frm" ng-submit="contact.formIsValid(frm) && contact.contactUsSubmit(frm)" novalidate>
In the controller the two functions went as follows:
formIsValid(frm) {
angular.forEach(frm.$error.required, function (field) {
field.$setDirty();
});
return frm.$valid;
}
contactUsSubmit(frm) {
var scope = this;
if (frm.$valid) {
//do $http post data etc here
}
return frm.$valid;
}
Now, if when someone hits submit, the field are made "dirty" and this causes the angular validation rules to fire. If the form is valid then the submit goes thru, if not then we are left with the validation errors.

Angular radio button validation with an ng- repeat

I'm running into an interesting Angular validation issue. I need to make sure that radio buttons are selected (they might not have a default value when loaded). The values for the radio buttons are iterated over from an array using ng-repeat. This whole thing also happens inside another ng-repeat for a list of users.
The issue is that I have to set a unique/dynamic name attribute on each group of related radio buttons, otherwise selecting one will unselect others in a different row. The validation in based on the name attribute and I cannot seem to find a way to use this unique/dynamic name in the needed ng-class and ng-show expressions.
This is waht is not working:
ng-show="userFormRow.service-{{$index}}.$invalid"
Here's a sample of the code in context:
<table class='table table-striped table-bordered'>
<tbody>
<tr ng-repeat="u in users"
ng-form="userFormRow">
<td>
<!-- THIS is having an issue determining the name of this form item since I need to generate a dynamic one here-->
<div class="form-group"
ng-class="{'has-error': userFormRow.service-{{$index}}.$invalid }">
<label class="control-label center-block">Service</label>
<div class="radio-inline" ng-repeat="o in serviceOptions">
<label>
<!-- HERE is where I define the unique name attribute based on the index of the table repeater -->
<input type="radio"
name="service-{{$parent.$index}}"
value="{{::o}}"
ng-model="u.Service"
ng-required="!u.Service"> {{::o}}
</label>
</div>
<!-- THIS is having an issue determining the name of this form item since I need to generate a dynamic one here-->
<p class="help-block" ng-show="userFormRow.service-{{$index}}.$invalid">A service must be selected!</p>
</div>
</td>
</tr>
</tbody>
</table>
And here is a full code example.http://codepen.io/chrismbarr/pen/eNoGLJ?editors=101 Check the console for errors
You should use bracket notation to access variable object property:
ng-show="userFormRow['service-' + $index].$invalid"
and same with ngClass:
ng-class="{'has-error': userFormRow['service-' + $index].$invalid }"
Demo: http://codepen.io/anon/pen/rVbYpG?editors=100

ng-submit will not allow duplicate values

Why does Angular not allow duplicate data to be submitted? I am using ng-list to write all points scored using a simple controller.
For example - If a player inputs '10' into my app and then another player also does the same the ng-submit will stop functioning completely. I haven't specified anything else to allow this to happen. How can I change this default behaviour so users are allowed to post duplicate values?
Controller:
bowlingApp.controller('bowlPoints', function ($scope){
$scope.bowlPoints = [];
$scope.addBowlPoints = function() {
$scope.bowlPoints.push($scope.enteredPoints);
};
});
HTML
<form ng-submit="addBowlPoints()" role="form">
<div class="form-group">
<label>Player Score</label>
<input type="text" class="form-control" ng-model="enteredPoints">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<tr>
<ul>
<td>Player </td>
<td ng-repeat="points in bowlPoints ">{{points}}</td>
</ul>
</tr>
ng-submit works fine, in your example it adds value to array bowlPoints, but you just can not to see it.
To see an array and that ng-submit works fine you can type {{bowlPoints}} in your HTML file.
But if you want to see all data in table and if you want your table will to update and angular dynamically add your columns - you need to add track by in your ng-repeat
<td ng-repeat="points in bowlPoints track by $index ">{{points}}</td>
Demo: http://plnkr.co/edit/oFMm9Jqk034IRvS1qOCO?p=preview
I believe submit only does something if the form is dirty. Since the first submit will have cleared the dirty state, it won't do anything. You could try manually setting the dirty state of the form. Failing that, you could use ng-click instead and do the form submission yourself.

Resources