Get rid of pesky borders in ngMessage - angularjs

In this plunk I have a form with two fields, each validated with ngMessage, where the error message appears when the user tabs out of the field, or the form is submitted.
The issue is that if the message is shown and then hidden (because the problem was fixed) the borders are still shown.
Try tabbing out of a field, then entering a value, you will see only borders in the error message.
How to get rid of these borders?
HTML
<body ng-app="ngMessagesExample" ng-controller="ctl">
<form name="myForm" novalidate ng-submit="submitForm()">
<label>
Enter Aaa:
<input type="text"
name="aaa"
ng-model="aaa"
required ng-blur="aaaBlur()" />
</label>
<div ng-show="showAaa || formSubmitted"
ng-messages="myForm.aaa.$error"
style="color:red;background-color:yellow;border:1px solid brown">
<div ng-message="required">You did not enter a field</div>
</div>
<br/>
<label>
Enter Bbb:
<input type="text"
name="bbb"
ng-model="bbb"
ng-minlength="2"
ng-maxlength="5"
required ng-blur="bbbBlur()" />
</label>
<br/><br/>
<div ng-show="showBbb || formSubmitted" ng-messages="myForm.bbb.$error"
style="color:red;background-color:yellow;border:1px solid brown">
<div ng-message="required">You did not enter a field</div>
</div>
<br/>
<button style="float:left" type="submit">Submit</button>
</form>
</body>
Javascript
var app = angular.module('ngMessagesExample', ['ngMessages']);
app.controller('ctl', function ($scope) {
$scope.formSubmitted = false;
$scope.showAaa = false;
$scope.showBbb = false;
$scope.submitForm = function() {
$scope.formSubmitted = true;
};
$scope.aaaBlur = function() {
$scope.showAaa = true;
};
$scope.bbbBlur = function() {
$scope.showBbb = true;
};
});

It's because you show the div (showAaa=true) but there's no content. Solution? Don't show the div. :)
<div ng-show="!myForm.aaa.$valid && (showAaa || formSubmitted)"

In which field do you have the problem ? Both or only the 2nd one ?
I see that in the 2nd field you fixed a min and max length. If they're not right, you don't have any ng-message to handle them, but your field will be in error, so you'll end up with the red border and no message.
By the way : you can use [formName].[fieldName].$touched instead of using your native onblur.

Related

AngularJS v1.5.5 Reset form not working for invalid input fields

I have the code below (or see the fiddle) and I try to reset form and clear all input fields too for each form.
With AngularJS version 1.2.1 is working fine! but in my app I using version 1.5.5 because I have other libraries for separating the nested forms in <md-tab> tags using material framework who need this version.
The problem is when any field it's invalid then reset does not work as expected and these fields remain unchanged instead to be clear.
There is another way to clearing all fields (of nested form) when I click reset button ?
angular.module("main", [])
.controller("register", function($scope) {
$scope.data = {
A: {},
B: {}
};
$scope.reset = function(form) {
form.$setPristine();
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<body ng-app="main">
<div ng-controller="register" class="form">
<form name="form" novalidate role="form">
<div class="form">
<h1>TAB1 - Form A:</h1>
<ng-form name="A">
A1:
<input type="text" ng-model="data.A.A1" ng-minlength="4" ng-maxlength="15" />A2:
<input type="text" ng-model="data.A.A2" ng-minlength="4" ng-maxlength="15" />
<button type="button" ng-disabled="A.$pristine" ng-click="reset(A); data.A=null;">Reset</button>
<br/>
<strong>A.$pristine =</strong> {{A.$pristine}}
<strong>A.$valid =</strong> {{A.$valid}}
</ng-form>
</div>
<br/>
<br/>
<div class="form">
<h1>TAB2 - Form B:</h1>
<ng-form name="B">
B1:
<input type="text" ng-model="data.B.B1" ng-minlength="4" ng-maxlength="15" />B2
<input type="text" ng-model="data.B.B2" ng-minlength="4" ng-maxlength="15" />
<button type="button" ng-disabled="B.$pristine" ng-click="reset(B); data.B=null;">Reset</button>
<br/>
<strong>B.$pristine =</strong> {{B.$pristine}}
<strong>B.$valid =</strong> {{B.$valid}}
</ng-form>
</div>
</form>
<h1>data:</h1>
<pre>{{data | json}}</pre>
</div>
</body>
It's because ng-model binds to the A and B objects by reference in $scope.data. If you remove the $scope.data.A = null from your ng-click and reset the object without creating a new one, it works:
https://jsfiddle.net/utwf604r/15/
$scope.reset = function (form)
{
// don't change the reference to A
// $scope.data.A = {} wont work!!
angular.extend($scope.data, {A:{A1:'',A2:''}, B:{B1:'',B2:''}});
form.$setPristine();
};

ng-model not change after save editing

i try to make simple CRUD with angularjs and php api
the problem is in angularjs
i have code like this :
$scope.editData = function(pid) {
$scope.hideform = false;
$scope.edit = false;
$scope.pid = $scope.projects[pid].pid;
$scope.title = $scope.projects[pid].title;
$scope.pcode = $scope.projects[pid].pcode;
$scope.type = $scope.projects[pid].type;
$scope.proj_type = $scope.projects[pid].proj_type;
};
$scope.saveData = function(pid, title, pcode, type2, proj_type){
$http.post("api/getProject.php",
{type: "edit", id:pid, title:title, pcode:pcode, type2:type2, proj_type:proj_type})
.success(function(data) {
notification(data.success, data.message);
$scope.hideform = true;
});
};
and have form for editing data (edit button clicked , then show form edit with ng-model loaded on it
like this
<form class="form-horizontal" ng-hide="hideform" ng-submit="saveData(pid,title, pcode, type, proj_type)">
<h3 ng-hide="edit">Edit Project:</h3>
<div class="form-group">
<label class="col-sm-2 control-label">PID:</label>
<div class="col-sm-10">
<input type="text" ng-model="pid" ng-disabled="!edit" placeholder="PID">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Title:</label>
<div class="col-sm-10">
<input type="text" ng-model="title" placeholder="Title">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Pcode:</label>
<div class="col-sm-10">
<input type="text" ng-model="pcode" placeholder="Pcode">
</div>
</div>
<button class="btn btn-success" ng-disabled="error || incomplete">
<span class="glyphicon glyphicon-save"></span> Save Changes
</button>
</form>
the script are working , but everytime i click save editing then click edit button (on each row on table) , the ng-model on form input still same like the last data what i save but other input are changed , ex : i edited title , when click edit button on different row on the table , just pcode changed to different row data , the Title still same like the last data i edit
sorry if my explanation make all of you confuse
Any help will be appreciated
here is the image if my explanation not clear
This problem resolved using
$scope.selectedProject = $scope.projects[pid];
so editData function look like this
$scope.editData = function(pid) {
$scope.hideform = false;
$scope.edit = false;
$scope.pid = $scope.projects[pid].pid;
$scope.title = $scope.projects[pid].title;
$scope.pcode = $scope.projects[pid].pcode;
$scope.type = $scope.projects[pid].type;
$scope.proj_type = $scope.projects[pid].proj_type;
$scope.selectedProject = $scope.projects[pid];
};
and ng-model (on every input)
need to change like this
<input type="text" ng-model="selectedProject.pid" ng-disabled="!edit" placeholder="PID">
instead of this
<input type="text" ng-model="pid" ng-disabled="!edit" placeholder="PID">
Now every ng-model changed every edit button has been click after save Changes button clicked :)
Thank you!

Using Angular ng-show/ng-hide with radio buttons

I'm using $scope.checked to show/hide the "Top" text box, name="numRows" in my html.
However, even when I click on the 'top' radio button, $scope.checked always retains the value of `all' when I debug my controller code.
This simply plunker seems to work fine, http://plnkr.co/edit/6GoUZw7zkf8oqUmKg9hf?p=preview, but it won't work in my application.
In the plunker, there's a simple button whose click value will hit the controller event $scope.showval to show the value of either "top" or "all".
My HTML:
<div class="form-group">
<div class="row-fluid">
<label class="col-md-2 col-lg-2 control-label" for="numRows">Returned Rows</label>
<!-- ALL ROWS RADIO -->
<div class="col-md-1 col-lg-1">
<label class="radio-inline" for="radio-all">
<input name="radios" id="radio-all" value="all" type="radio" ng-model="checked" ng-click="setTopRows('all')">All
</label>
</div>
<!-- TOP NUM OF ROWS RADIO -->
<div class="col-md-1 col-lg-1">
<label class="radio-inline" for="radio-top">
<input name="radios" id="radio-top" value="top" type="radio" ng-model="checked" ng-click="setTopRows('top')">Top
</label>
</div>
<!-- NUM OF ROWS TEXT BOX -->
<div class="col-md-2">
<input ng-show="checked == 'top'" ng-hide="checked == 'all'" type="text" class="form-control" name="numRows" ng-model="settings.numRowsReturned" placeholder="" >
</div>
<div class="col-md-3">
Click to show<input type="button" ng-click="showval(e)">
</div>
</div>
</div>
A snippet from my angular controller code :
The $scope.checked value is always 'all', even when I click on 'top' from the html.
(function () {
'use strict';
angular.module('rage')
.controller('GadgetSettingsCtrl_NEW', ['$rootScope', '$scope', '$modalInstance', gridSettings]);
function gridSettings($rootScope, $scope, $modalInstance,) {
var settings = this;
$scope.checked = 'all';
$scope.showval = function (e) {
var test = $scope.checked;
}
$scope.setTopRows = function (numRows) {
if (numRows === 'top') {
$scope.checked = 'top';
}
else {
$scope.checked = 'all';
}
}
function checkOptions(){
var top = '';
if ($scope.checked === 'top') {
top = (settings.numRowsReturned != undefined ? settings.numRowsReturned : '');
}
}
}; // end of gridSettings()
})();
Can someone help me clear up why $scope.checked is NOT changing when I click on the radio buttons ?
thank you,
Bob
****** UPDATE *********
I've added a simple ng-click="setTopRows('all')" to force my scope variable to change. This works, but it seems like too much code to accomplish this.

Angular directive for horizontal Bootstrap form

I'm trying to build a directive for my Angular to help with the integration of form fields. I've implemented Scott Allens solution from his Angular playbook, and it works fine for a normal stacked form.
I need however to adapt it to a horizontal form instead. Here's my code:
Markup
<div form-group>
<label for="name">Name</label>
<input type="text" id="name" ng-model="vm.name">
</div>
formGroup directive
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
var label = element.querySelector("label");
label.classList.add("control-label");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
}
element.classList.add("form-group");
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
The output becomes:
<div form-group="" class="form-group">
<label for="name" class="control-label">Name</label>
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
And that's fine for stacked form. Since I need a horizontal form, my output needs to look like this:
<div form-group="" class="form-group">
<label for="name" class="control-label col-sm-3">Name</label>
<div class="col-sm-9">
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
</div>
I've tried many solutions and I can get it work with single elements like an input, textarea or a select. It becomes much more tricky when I have something like two radio buttons inside my markup like this:
<div form-group>
<label>Active</label>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
The desired output of the above mentioned code should be:
<div form-group class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
</div>
Please notice that the input(s) in the form-group is not fixed. It can be either a single input, textarea, select, a group of radio buttons or checkboxes. I'm lost for how I can make that happen. Any help is appreciated. Thanks!
UPDATE
I made some small changes to Mark Veenstra's code to make it (sort of) working:
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label", "col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
angular.element(input).wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelector("div[class='radio']");
angular.element(div_radio).wrap(angular.element('<div class="col-sm-9"></div>'));
}
This does not work completely as intended with multiple radio inputs since it only wraps the <div> on the first radio input element.
The output from radio button example in my original post using Marks code is:
<div form-group="" class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active" value="true"> Yes
</label>
</div>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active" value="false"> No
</label>
</div>
</div>
SOLUTION
Check out the Plunker with the final result: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview
In the angular.element documentation you can find the method wrap() to be able to wrap HTML around a selected element. Or see this direct link.
So what you could do in your directive is change the setupDom() function to match your requirements per type of form element.
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
input.wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelectorAll("div[class='radio']");
div_radio.wrap(angular.element('<div class="col-sm-9"></div>'));
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
NOTE: This code is not tested, maybe there are some minor mistakes, but I guess you'll get the point now.
Mark's suggestion came close, but it didn't solve my problem completely. I ended up using the following code in my formGroup directive:
(function (module) {
"use strict";
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var children = angular.element(element).children();
var labels = children.splice(0, 1);
// Set label classes
labels[0].classList.add("control-label", "col-sm-3");
// Wrap children in div
angular.element(children).wrapAll(angular.element("<div class='col-sm-9'></div>"));
// Handle inputs
var inputs = element.querySelectorAll("input, textarea, select");
for (var i = 0, len = inputs.length; i < len; i++) {
var input = inputs[i],
type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox") {
input.classList.add("form-control");
}
}
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
module.directive("formGroup", formGroup);
}(angular.module("app.core")));
Check out this Plunker to see it in action: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview

Cannot read property $valid of undefined

I have a form like this -
<form name="myForm" novalidate>
There are some fields in the form which I am validating and then submitting the form like this -
<input type="button" ng-click="Save(data)" value="Save">
In the controller, I want to check if the form is not valid then Save() should show some error on the page. For that, I am setting up a watch like this -
$scope.$watch('myForm.$valid', function(validity) {
if(validity == false)
// show errors
});
But I am always getting this error on running it -
Cannot read property '$valid' of undefined
Can someone explain why?
Thanks
You just misspelled "myForm" in your controller code. In order to remove the error, Write "myform" instead of "myForm".
However I expect what you want is like this.
$scope.Save = function(data){
alert($scope.myform.$valid);
}
I setup jsfiddle.
In my case I was wrapping the form in a modal created in the controller and therefore got the same error. I fixed it with:
HTML
<form name="form.editAddress" ng-submit="save()">
<div class="form-group">
<label for="street">Street</label>
<input name="street" type="text" class="form-control" id="street" placeholder="Street..." ng-model="Address.Street" required ng-minlength="2" />
<div class="error" ng-show="form.editAddress.street.$invalid">
<!-- errors... -->
</div>
</div>
<button type="submit" class="btn btn-primary" >Save address</button>
</form>
JS
angular.module("app").controller("addressController", function ($scope, $uibModal, service) {
$scope.Address = {};
$scope.form = {};
$scope.save = function() {
if (modalInstance !== null) {
if (isValidForm()) {
modalInstance.close($scope.Address);
}
}
};
var isValidForm = function () {
return $scope.form.editAddress.$valid;
}
});

Resources