Angular tabs form validation - javascript

I have the following structure
<div id="contractTab" class="ui top attached large tabular menu">
<a id="main-tab" class="item active" data-tab="first" ng-show="ctrl.currentTabsGroup == 'main'">some data</a>
<a id="supplier-tab" class="item" data-tab="third" ng-show="ctrl.currentTabsGroup == 'supplier'">some data2</a>
<a id="position-tab" class="item" data-tab="fourth" ng-show="ctrl.currentTabsGroup == 'position'">some data3</a>
<a class="item" data-tab="second" ng-show="ctrl.currentTabsGroup == 'main'">some data4</a>
</div>
And each tab looks like
<div class="ui bottom attached tab segment" data-tab="third">
<div class="ui basic segment">
<ng-form name="supplierForm">
...
</ng-form>
</div>
</div>
I have many fields required in each form. The problem is that when I try to validate form when this form is active(on screen), I get that it is invalid. For, example supplierForm.$valid == false
However, if I move to different form everything apparently erases and I get
supplierForm.$valid == true
Is there a way to connect all of this forms in order not to lose data?
This function is called when I change tabs
resetFormObject(formName);
$s[formName].$setPristine();
$timeout(function() {
document.getElementById(tabId).click();
}, 20);
Where resetFormObject just initializes active object to send it when the form is submitted

Related

Semantic ui dropdown, prevent auto select with input element

I added an option to the dropdown that allows user to add item if it doesn't exist.
For that matter, I added an input field to the dropdown but when the user enters something, the dropdown tries to match the entered text with items that are already in the list.
I find it quite annoying in that specific case. I have noticed in the docs that input elements are bound to the search function. Nevertheless, I couldn't find how to disable this behaviour.
Here's the HTML:
<div class="ui fluid selection dropdown playlist">
<input name="playlist" type="hidden">
<i class="dropdown icon"></i>
<div class="default text">playlist</div>
<div class="menu">
<div class="item create" data-value="0">
<span class="create-placeholder">+ new playlist</span>
<div class="ui action input add-playlist">
<input placeholder="new playlist">
<button class="ui button">Add</button>
</div>
</div>
<div class="item" data-value="1">foo</div>
<div class="item" data-value="2">bar</div>
<div class="item" data-value="3">baz</div>
</div>
</div>
The .add-playlist div and its content are not shown but I'm willing to spare you with the CSS here.
And the js:
$dropdown = $('.ui.dropdown');
$dropdown.dropdown({
action: (text, val) => {
if (val == 0) { // eslint-disable-line
$('.create-placeholder').hide(100);
$('.add-playlist').css('display', 'inline-flex');
$('.add-playlist input, .add-playlist button').show(200);
}
else $dropdown.dropdown('set selected', val).dropdown('hide');
},
onHide: () => {
// do that after dropdown has been hidden
setTimeout(() => {
$('.add-playlist, .add-playlist input, .add-playlist button').hide();
$('.create-placeholder').show();
}, 400);
}
});
I've set up a fiddle to have a clear exemple. Just type "foo" and you'll see what I mean in case it's not crystal clear.
To allow user to add new items just add allowAdditions: True To dropdown options, for more informtions see semantic-ui dropdown settings
Example:
$('dropdownSelector').dropdown({
allowAdditions: True
}).dropdown();

How to enable only active tab and disable all other tabs by default in Angularjs?

I have four tabs as "Tab1, Tab2, Tab3, Tab4".
By default all tabs should be disabled and active tab should be enabled.
If I click on submit button in active tab then I should automatically navigate to next tab by enabling the next tab and setting it as active and disabling the previous tab.
<li class="myli" ng-repeat="tab in tabs track by $index" ng-class="{active:isSelected($index)}"><a href ng-click="displaySelectedtab(tab, $index)">{{tab}}</a></li>
<div class="panel-body newPanelBody" ng-if="displaytab1 && !displaytab2 && !displaytab3 && !displaytab4">
<form name="actForm" role="form" data-ng-init="resp()" ng-submit="save()" novalidate>
<h4>Tab1</h4>
<br>
<button class="btn save sbmt" type="submit" id="submit">SAVE & CONTINUE</button>
</form>
</div>
<div class="panel-body newPanelBody" ng-if="displaytab2 && !displaytab1 && !displaytab3 && !displaytab4">
<form name="actForm" role="form" data-ng-init="resp()" ng-submit="save()" novalidate>
<h4>Tab2</h4>
<br>
<button class="btn save sbmt" type="submit" id="submit">SAVE & CONTINUE</button>
</form>
</div>
<div class="panel-body newPanelBody" ng-if="displaytab3 && !displaytab1 && !displaytab2 && !displaytab4">
<form name="actForm" role="form" data-ng-init="resp()" ng-submit="save()" novalidate>
<h4>Tab3</h4>
<br>
<button class="btn save sbmt" type="submit" id="submit">SAVE & CONTINUE</button>
</form>
</div>
<div class="panel-body newPanelBody" ng-if="displaytab4 && !displaytab1 && !displaytab2 && !displaytab3">
<form name="actForm" role="form" data-ng-init="resp()" ng-submit="save()" novalidate>
<h4>Tab4</h4>
<br>
<button class="btn save sbmt" type="submit" id="submit">SAVE & CONTINUE</button>
</form>
</div>
First off, your use of .panel-body and .btn has me assuming you use bootstrap, so have a look here: https://angular-ui.github.io/bootstrap/
There's a tabs component on that page made for use with angular and bootstrap.
Secondly, instead of using booleans to control which tab should show, it is much easier to use an integer to control the currently selected tab. That will also allow you to work with a variable amount of tabs.
<li class="myli" ng-repeat="tab in tabs track by $index" ng-class="{active: selectedIndex == $index}"><a href ng-click="displaySelectedtab(tab, $index)">{{tab}}</a></li>
<div class="panel-body newPanelBody" ng-repeat="tab in tabs track by $index" ng-if="selectedIndex == $index">
<h4>Tab {{$index + 1}}</h4>
<!-- If you need different content for each tab you can include an angular template as well -->
<ng-include src="'path/to/template.tpl.html'"></ng-include>
</div>
It will require you to think about how to store your tab content a little. The easiest way is probably to use templates. In that case you could devise a strategy where your tabs array contains objects that contain both the tab title as well as the content template url, like so:
$scope.tabs = [
{
"title": "Tab 1",
"templateUrl": "path/to/template.tpl.html"
}
];
Your ng-include would then look like like:
<ng-include src="tab.templateUrl"></ng-include>
Making your form's submit action go to a different tab then becomes a simple matter of changing the $scope.selectedIndex variable to the index of the tab you want opened.
Change ng-if="displaytab1 && !displaytab2 && !displaytab3 && !displaytab4" to ng-if="$index==selected"
In your button submit function add index like this ng-submit="save($index)"
In controller method:
`$scope.selected=1; $scope.save= function(index){selected=index+1;}`

Angular Two Apps on the same page

What I am trying to do is render two Calendars on one page, each with a different data set. Currently the first Calendar loads correctly but the second calendar will not load. It doesn't appear to even try to load.
I am quite new to Angular, so I am not sure if what I am doing is allowed in the concept of a one page application, or if I should be doing it another way.
Open to all suggestions!
Calendar App Using: https://github.com/mattlewis92/angular-bootstrap-calendar
Front end (Trimmed down)
<div class="col-lg-9 panel panel-default" id="Calandars">
<button class="btn dropdown" data-toggle="collapse" data-target="#userCal" data-parent="#Calandars"><i class="icon-chevron-right"></i> User Calandar </button>
<button class="btn dropdown" data-toggle="collapse" data-target="#GlobalCal" data-parent="#Calandars"><i class="icon-chevron-right"></i> Global Calandar</button>
<div class="accordion-group">
<div id="userCal" class="collapse indent">
<!---User Calendar Configuration - Working Calendar-->
<div ng-app="UserCal" class="textfix">
<div ng-controller="Cal as vm">
<h2 class="text-center">{{ vm.calendarTitle }}</h2>
<mwl-calendar events="vm.events"
view="vm.calendarView"
view-title="vm.calendarTitle"
view-date="vm.viewDate"
on-event-click="vm.eventClicked(calendarEvent)"
on-event-times-changed="vm.eventTimesChanged(calendarEvent); calendarEvent.startsAt = calendarNewEventStart; calendarEvent.endsAt = calendarNewEventEnd"
edit-event-html="'<i class=\'glyphicon glyphicon-pencil\'></i>'"
delete-event-html="'<i class=\'glyphicon glyphicon-remove\'></i>'"
on-edit-event-click="vm.eventEdited(calendarEvent)"
on-delete-event-click="vm.eventDeleted(calendarEvent)"
cell-is-open="vm.isCellOpen"
day-view-start="06:00"
day-view-end="22:00"
day-view-split="30"
cell-modifier="vm.modifyCell(calendarCell)">
</mwl-calendar>
</div>
</div>
</div>
<div id="GlobalCal" class="collapse indent">
<!---Global Calandar Configuration -- None Working Calendar-->
<div ng-app="UserCal" class="textfix">
<div ng-controller="GlobalCalCon as vm">
<h2 class="text-center">{{ vm.calendarTitle }}</h2>
<mwl-calendar events="vm.events"
view="vm.calendarView"
view-title="vm.calendarTitle"
view-date="vm.viewDate"
on-event-click="vm.eventClicked(calendarEvent)"
on-event-times-changed="vm.eventTimesChanged(calendarEvent); calendarEvent.startsAt = calendarNewEventStart; calendarEvent.endsAt = calendarNewEventEnd"
edit-event-html="'<i class=\'glyphicon glyphicon-pencil\'></i>'"
delete-event-html="'<i class=\'glyphicon glyphicon-remove\'></i>'"
on-edit-event-click="vm.eventEdited(calendarEvent)"
on-delete-event-click="vm.eventDeleted(calendarEvent)"
cell-is-open="vm.isCellOpen"
day-view-start="06:00"
day-view-end="22:00"
day-view-split="30"
cell-modifier="vm.modifyCell(calendarCell)">
</mwl-calendar>
</div>
</div>
</div>
Javascript Behind
angular.module('UserCal', ['mwl.calendar', 'ui.bootstrap', 'ngAnimate'])
.controller('Cal', populateCal)
.controller('GlobalCalCon', populateGlobalCal);
function populateCal($http) {
Do Stuff
angular.copy(MyData, vm.events)
};
function populateGlobalCal($http) {
Do Diffrent Stuff
angular.copy(MyData, vm.events)
};
Well, In here you're using same module UserCal two times. This is your main SPA module so it will be only one time.
Please put ng-app="UserCal" to html/body tag and remove <div ng-app="UserCal" class="textfix"> from HTML code.
Now both the calender will work :)
cheers!
Its because you are using ng-app twice, you have to declare it only once on top of the page or best thing is declare it in ur index.html on html tag
On index.html:
<html ng-app="UserCal">
Or
On current page:
<div ng-app="UserCal">
<div ng-controller="Ctrl1">
// stuff goes herr
</div>
<div ng-controller="Ctrl1">
// stuff goes herr
</div>
</div>

disable dynamic id buttons with jQuery

I'm using VB.Net, MVC 5, razor and jQuery. I have a razor view that creates buttons I'm trying to disable on the user click. I generally accomplish this task using jQuery:
$('#id').prop("disabled", true);
My task is new to me in that my buttons are generated like this:
#For i As Integer = 0 To Model.hrmnValues.Count - 1
#<div class="col-md-3">
<a class="btn btn-primary btn-md" href="#" id="#Model.inventoryCategoryAttIDs(i)"
onclick="acceptChange('#Model.hrmnValues(i)',
#Model.inventoryCategoryAttIDs(i), #Model.uniqueItemID)">Accept Change</a>
</div>
Next
My onClick function is similar to this:
function acceptChange(newValue, categoryAttID, itemID) {
$('#categoryAttID').prop("disabled", true);
}
This obviously does not work, as it is looking for an id with the name of categoryAttID. I have also tried putting the categoryAttID into it's own variable like this:
var idToDisable = "#" + categoryAttID;
and then putting idToDisable into the jQuery call to disable the button, this did not work.
Given this situation how can I disable the button that is clicked?
There will be multiple buttons on this page making use of the function, the function actually performs an ajax call. The idea is to limit the user to performing one ajax call per button.
The html is rendered like this:
<div class="row">
<div class="col-md-3">
<font>First Name</font>
</div>
<div class="col-md-3">
<font>John</font>
</div>
<div class="col-md-3">
<font>No Record Found</font>
</div>
<div class="col-md-3">
<a class="btn btn-primary btn-md" href="#" id="2"
onclick="acceptChange('CHRISTOPHER', 2, 0)">Accept Change</a>
</div>
</div>
<div class="row">
<div class="col-md-3">
<font>Last Name</font>
</div>
<div class="col-md-3">
<font>MURRAY</font>
</div>
<div class="col-md-3">
<font>No Record Found</font>
</div>
<div class="col-md-3">
<a class="btn btn-primary btn-md" href="#" id="3"
onclick="acceptChange('MURRAY', 3, 0)">Accept Change</a>
</div>
You're already passing the button's id to your function as the second argument -
acceptChange('#Model.hrmnValues(i)',#Model.inventoryCategoryAttIDs(i), #Model.uniqueItemID)
So you can change your function to -
function acceptChange(newValue, categoryAttID, itemID) {
$('#' + categoryAttID).prop("disabled", true);
}
Also, there isn't a disabled property available for links (see - Mozilla Developer Network) if you want to disable your elements on click try using a button instead.
Since you appear to be using bootstrap after changing your links to buttons you might want to change your function to -
function acceptChange(newValue, categoryAttID, itemID) {
$('#' + categoryAttID).prop("disabled", true);
$('#' + categoryAttID).addClass("disabled");
}

Grouped validation in Angular with ngRepeat

I have a large form that I have splitted into tabs. Each tab contains multiple different input fields with validation on the inputs. Right now I only have validation per input field, but I'm now trying to add validation per tab. That is, I want the tabs to indicate if any of its input field contains validation errors.
The form is rendered using ngRepeat:
<form name="createForm" novalidate>
<ul class="nav nav-tabs">
<li ng-repeat="tab in tabs" ng-class="{active: $index == 0}">
<a ng-hide="tab.isValid" data-target="#tab{{$index + 1}}" data-toggle="tab" class="invalid">{{tab.title}}</a>
<a ng-show="tab.isValid" data-target="#tab{{$index + 1}}" data-toggle="tab">{{tab.title}}</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in" id="tab{{$index + 1}}" ng-repeat="tab in tabs" ng-class="{active: $index == 0}">
<div ng-include="'/view/create/partials/' + tab.content"></div>
</div>
</div>
</form>
The array of tabs:
$scope.tabs = [
{ title: "Tab1", content: 'tab1.html', isValid: true },
{ title: "Tab2", content: "tab2.html", isValid: true },
{ title: "Tab3", content: "tab3.html", isValid: true }];
The HTML for each tab:
<label for="age">Age</label>
<input type="text" class="form-control" ng-model="person.tab1.Age" name="Age" maxlength="3" data-integer />
<label for="height">Height</label>
<input type="text" class="form-control" ng-model="person.tab1.Height" name="Height" data-float />
The 'data-integer' and 'data-float' are custom validation directives:
var INTEGER_REGEXP = /^\d*$/;
app.directive('integer', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
if (INTEGER_REGEXP.test(viewValue)) {
ctrl.$setValidity('integer', true);
return parseInt(viewValue);
} else {
ctrl.$setValidity('integer', false);
return undefined;
}
});
}
};
});
My idea is to set the 'isValid' variable to false if any of its child input fields are invalid. I'm not sure where to execute this function or how this function would look like. I'm also very welcome for ideas on improvement or other ways to do this.
You group validation by using a form, so in a simple scenario each tab would have a separate form. However, I understand you want to have a root form for handling the submission, but group tab fields together to give a finer-grained UI response to validation.
In that case, you can use nested forms. See this example for more information. You'll have a root form (named, of course) and then declare additional ng-form for each group within it. Note that you must use the ng-form directive specifically for the nested forms because browsers won't let you nest form tags; ng-form directive as an element works around that.
For you, this means you'd keep your parent form as is. Then you just wrap the fields in each tab with their own ng-form directive, giving each a unique name representing each tab. The validity of each tab will be reflected in the root form (createForm) so you can still check if the form is valid overall using the typical approaches e.g. createForm.$invalid, etc. However, you can now check the state of the nested forms, too e.g. createForm.Tab1Form.$valid.
The only catch here is that ng-form does not evaluate the name attribute, it uses the literal value, so you'll have troubles generating that name dynamically from the tab.title like you probably want to do. I got around that by giving them a literal name in the template.
<form name="createForm" novalidate="">
<ul class="nav nav-tabs">
<li ng-repeat="tab in tabs" ng-class="{active: $index == 0}">
<a data-target="#tab{{$index + 1}}" data-toggle="tab" ng-class="{'invalid': createForm.{{tab.title}}.$invalid}">{{tab.title}}</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in" id="tab{{$index + 1}}" ng-repeat="tab in tabs" ng-class="{active: $index == 0}">
<div ng-include="tab.content"></div>
</div>
</div>
</form>
Here's the plunk.
You'll notice that the tab titles are red if the tab's nested form is invalid (type a value into Field 1 and you'll see the validation error class go away). Note that I didn't wire up your Bootstrap JavaScript behaviors in the demo because I'm lazy, but I believe I've illustrated the point.

Categories

Resources