Angular validate form with dynamic name - javascript

Typically to validate a form in Angular, I would use something like this on the ng-submit directive:
<form name="formName" ng-submit="formName.$valid && submitForm()"></form>
This works great when the form has a name that I set myself while building the form. However, in my current situation, I am trying to create multiple forms based on a list of objects. In this case, each form has a name that is determined on the fly.
When the user submits one of these forms, how can I validate it before running the submitForm() function for that form?
Here's a jsfiddle of the simplified problem: http://jsfiddle.net/flyingL123/ub6wLewc/1/
My question is, how can I access the name of the form in order to validate it? Here is the code from the fiddle:
var app = angular.module('app', []);
app.controller("AppController", ['$scope', function($scope) {
$scope.forms = [{
id: 1,
value: "val1"
}, {
id: 2,
value: "val2"
}, {
id: 3,
value: "val3"
}];
$scope.submitForm = function() {
alert('submitted');
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div id="app" ng-app="app" ng-controller="AppController">
<div class="formWrapper" ng-repeat="form in forms">
<form name="{{ 'form' + form.id }}" ng-submit="submitForm()" novalidate>
<input ng-model="form.value" required />
<button type="submit">Submit</button>
</form>
</div>
</div>

You can always use this to access the scope in your templates.
{{this.foo === foo}} <!-- This will always show "true" -->
Therefore, you can simply use this[myDynamicFormName] to access the form:
<form name="{{'form' + form.id}}" ng-submit="this['form' + form.id].$valid && submitForm()"></form>

Related

angular - #angular/common without NPM

I am trying to use the ng-template directive in my code to be able to show a div after a button click event but I cannot get it to work since I need to have the angular/common library. I tried to reference it by adding a script tag
<script src="https://cdn.jsdelivr.net/npm/#angular/common#11.0.2/bundles/common.umd.min.js" integrity="sha256-+HBVhNZwWCgkN0Z0tvWyjqjm+yI9F/szNt3Yz4/0/ws=" crossorigin="anonymous"></script>
But every time I test my app, the following error appears in the console.
common.umd.min.js:35 Uncaught TypeError: Cannot read property 'InjectionToken' of undefined
I am stuck and don't know how can I be able to use the ng-template directive
HTML (index.html):
<ng-template>
<div class="container">
<h2>Edit or Delete an Evangelist</h2>
<form name="userEditForm">
<p>person id: <input type="text" id="name" ng-model="userEdit.personid" disabled /></p>
<p>name: <input type="text" id="name" ng-model="userEdit.name" /></p>
<p>location: <input type="text" id="location" ng-model="userEdit.location" /></p>
<button id="btn-edit-evangelist" class="btn btn-primary" ng-click="editName(userEdit)">Save</button>
<button id="btn-canceledit-evangelist" class="btn btn-default btn" ng-click="delName(userEdit);">Delete User</button>
</form>
</div>
<div *ngIf="isEdit">
</div>
</ng-template>
Angular (main-app.js):
"use strict";
angular.module('MainApp', [
])
.controller("MainController", function ($scope, $http) {
//initialize scope variables
$scope.user = {
name: function (theName) {
if (angular.isDefined(theName)) {
$scope._name = theName;
}
return $scope._name;
}(),
location: function (theLocation) {
if (angular.isDefined(theLocation)) {
$scope._location = theLocation;
}
return $scope._location;
}()
};
function redirectToEdit(user) {
this.isEdit = !this.isEdit;
var id = user.personid;
$http.get('https://webapimongodb.herokuapp.com/api/name/' + id, {
params: { personid: user.personid, name: user.name, location: user.location },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
.success(function (res) {
console.log(res);
$scope.userEdit.personid = res.personid;
$scope.userEdit.name = res.Name;
$scope.userEdit.location = res.Location;
});
}
})
Will it be possible to have the #angular/common module referenced via a script tag or do I need to have it referenced via NPM?
I think the issue here is the version being used.
The code you provided is written in Angular.js code and #angular/common is an Angular module
To get your code working you need this instead
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.0/angular.min.js"></script>
Here you can find the Angular.js docs, which, by the way, is deprecated.
And this article explains the difference among Angular and Angular.js

Strange behaviour Angular driven select list

According to paper "How to set the initial selected value of a select element using Angular.JS ng-options & track by" by #Meligy which I used as a guidance to learn and solve my problem with implementing a select list (ng-options), I still encounter some strange collaterale behaviour.
Although the basic behaviour finally does what it should do, see Test Plunk, I still encounter strange behaviour on the selected item in that list. Not in my test plunk though, implemented in my developement site.
app.controller("TaskEditCtrl", function($scope) {
$scope.loadTaskEdit = loadTaskEdit;
function loadTaskEdit() {
taskLoadCompleted();
tasktypesLoadCompleted();
}
function taskLoadCompleted() {
$scope.tasks = [{
Id: 1,
Name: "Name",
Description: "Description",
TaskTypesId: 4
}
];
$scope.current_task_tasktypesid = $scope.tasks[0].TaskTypesId;
}
function tasktypesLoadCompleted() {
var tasktypes = [{ Id: 1, Name: "A" },
{ Id: 2, Name: "B" },
{ Id: 3, Name: "C" },
{ Id: 4, Name: "D" }];
$scope.available_tasktypes_models = tasktypes
}
$scope.submit = function(){
alert('Edited TaskViewModel (New Selected TaskTypeId) > Ready for Update: ' + $scope.tasks[0].TaskTypesId);
}
loadTaskEdit();
});
HTML
<form class="form-horizontal" role="form" novalidate angular-validator name="editTaskForm" angular-validator-submit="UpdateTask()">
<div ng-repeat="task in tasks">
<div>
<select ng-init="task.TaskTypes = {Id: task.TaskTypesId}"
ng-model="task.TaskTypes"
ng-change="task.TaskTypesId = task.TaskTypes.Id"
ng-options="option_tasttypes.Name for option_tasttypes in available_tasktypes_models track by option_tasttypes.Id">
</select>
</div>
</div>
<div class="">
<input type="submit" class="btn btn-primary" value="Update" ng-click="submit()" />
</div>
</form>
As said, see my test plunk which shows exactly what it supposed to do. Moreover, using 5 self-explaining images, I do hope to make my troulbe bit clearer what's the problem.
I'm a bit lost to figure out what's so troublesome. My 'water' is telling me something wrong or missing in css. Did have anybody out their ever have face comparable? What could cause me this trouble? Does have anybody out there have a clue?
Thanks in advance
[1
[]2
[]3
[]4
Apparently I'm a rookie on css. Any suggestion is welcome!
CSS
#region "style sheets"
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/css/site.css",
"~/content/css/bootstrap.css",
"~/content/css/bootstrap-theme.css",
"~/Content/css/font-awesome.css",
"~/Content/css/morris.css",
"~/Content/css/toastr.css",
"~/Content/css/jquery.fancybox.css",
"~/Content/css/loading-bar.css"));
#endregion "style sheets"
The key with the dropdown is to set the model to the object that was selected. I updated your code to behave the way that I believe you are asking for it to work.
The key differences are:
Set the ng-model of the dropdown to the selected object and not the id of the selected item. This will give you access to the full selected object and all it's properties.
Remove the ng-change binding - this is not necessary with 2 way data binding, and the value on the model (whatever is put in for ng-model) will automatically be updated.
In your HTML you were using properties that were never declared in the Controller $scope. I updated those to reflect the available variables that were in scope.
For more information on dropdowns please see the angular documentation. It's very useful for figuring these types of issues out - https://docs.angularjs.org/api/ng/directive/select
// Code goes here
var app = angular.module("myApp", []);
app.controller("TaskEditCtrl", function($scope) {
$scope.tasks = {};
$scope.current_task_tasktypesid = null;
$scope.selected_task_tasktype = null;
$scope.loadTaskEdit = loadTaskEdit;
function loadTaskEdit() {
taskLoadCompleted();
tasktypesLoadCompleted();
//EDIT: DEFAULT DROPDOWN SELECTED VALUE
$scope.selected_task_tasktype = $scope.available_tasktypes_models[2];
}
function taskLoadCompleted() {
$scope.tasks = [{
Id: 1,
Name: "Name",
Description: "Description",
TaskTypesId: 4
}
];
$scope.current_task_tasktypesid = $scope.tasks[0].TaskTypesId;
}
function tasktypesLoadCompleted() {
var tasktypes = [{ Id: 1, Name: "A" },
{ Id: 2, Name: "B" },
{ Id: 3, Name: "C" },
{ Id: 4, Name: "D" }];
$scope.available_tasktypes_models = tasktypes
}
$scope.submit = function(){
alert('submitted model: ' + $scope.selected_task_tasktype.Id);
}
loadTaskEdit();
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.2.9" src="http://code.angularjs.org/1.2.9/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp" ng-controller="TaskEditCtrl as edit">
<form class="form-horizontal" role="form" novalidate angular-validator name="editTaskForm" angular-validator-submit="UpdateTask()">
<div ng-repeat="task in available_tasktypes_models">
<div>Task (Id): {{task.Id}}</div>
<div>Name: {{task.Name}}</div>
<div>Descripton: {{task.Description}}</div>
</div>
<p>Current Task.TaskTypesId: {{selected_task_tasktype.Id}}</p>
<div>
<select
ng-model="selected_task_tasktype"
ng-options="option_tasttypes.Name for option_tasttypes in available_tasktypes_models track by option_tasttypes.Id">
</select>
</div>
<p>{{task.TaskTypes}}</p>
<p>{{selected_task_tasktypesid = task.TaskTypes}}</p>
<div class="">
<input type="submit" class="btn btn-primary" value="Update" ng-click="submit()" />
</div>
</form>
</body>
</html>
First, I need to state the implementation of #Meligy and the suggested input of 'dball' are correct. So, go with the flow of your choice.
Keep notice on your style sheets.
Finally, I figured out that the style property 'color' with the value 'white' of selector #editTaskWrapper as identifier of the parent
<div id="editTaskWrapper">
acted as the bad guy. One way or the other, if I comment 'color: white' in
#editTaskWrapper {
background-color: #337AB7;
/*color: white;*/
padding: 20px;
}
the selected item in the selectlist becomes visible. All other controls and values are not affected, only the selected list item.

How to create json object in Angularjs

i want to create json object in Angularjs. when add on addBasket add new basket object to array and when click on addOrder add new order to array. in fact date field not repeat in loop. dothis way is good to create json object? or may be exist good solution to create json object.
Edit question:
I would like to create object in the json format. I put the format below. I wrote the code that I've put it. In fact, my problem is to add an object to the orders array. how add new object to orders array?
[
{
"date":'2015-30-7',
"baskets": [
{
"orders": [
{
"id": "12"
}
],
"customer": {
"phone": "555555"
},
"discount": "8"
}
]
}
]
var app = angular.module('app', []);
app.controller('myController', function($scope, $http) {
$scope.typistData = [];
$scope.typistData.push({
baskets: [{
orders:[]
}]
});
$scope.addBasket = function() {
$scope.typistData.push({
baskets: [{
orders:[]
}]
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='myController'>
<input type="text" ng-model="data.name">
<hr>
<div ng-repeat="data in typistData" ng-if="typistData">
<form novalidate ng-submit="submitOffices()">
<div>
<ng-form ng-repeat="basket in data.baskets">
<div>
<input type="text" ng-model="basket.customer.phone">
<br>
<input type="text" ng-model="basket.discount">
<input type="text" ng-model="basket.orders[0].id">
<input type="button" value="addOrder">
</div>
<hr>
</ng-form>
</div>
</form>
</div>
<button ng-click="addBasket()">Add Basket</button>
<div>
<pre>{{ typistData | json }}</pre>
</div>
</div>
From what I have understood from your question, you want to able to add a basket to the baskets of any of the typists, and also you want to add an order to the orders of one of the baskets of one of the typists.
For handling that I think you can pass in the right objects for addBasket and addOrder inside ng-repeat with ng-click (it works because it passes the reference). So a possible working change can be this :
View part :
<div ng-repeat="data in typistData" ng-if="typistData">
<form novalidate ng-submit="submitOffices()">
<div>
<ng-form ng-repeat="basket in data.baskets">
<div>
<input type="text" ng-model="basket.customer.phone">
<br>
<input type="text" ng-model="basket.discount">
<input type="text" ng-model="basket.orders[0].id">
<!-- Here you call the addOrder with basket. -->
<button ng-click="addOrder(basket)">Add Order</button>
</div>
<hr>
</ng-form>
<!-- Here you call the addBasket with data. .-->
<button ng-click="addBasket(data)">Add Basket</button>
</div>
</form>
</div>
Controller part :
var app = angular.module('app', []);
app.controller('myController', function($scope, $http) {
$scope.typistData = [];
$scope.typistData.push({
baskets: [{
orders:[]
}]
});
/* This is wrong, because it doesn't add a basket, instead it
another typist's details.
$scope.addBasket = function() {
$scope.typistData.push({
baskets: [{
orders:[]
}]
});
Correct way of adding it is to use the passed in reference to the
data of a particular typist, and add a basket to its baskets.
*/
$scope.addBasket = function(typist) {
// Adding a basket to the typist's baskets, and you want the
// basket to contain an empty orders array initially right.
typist.baskets.push({
orders:[]
});
};
// To add an order to one of the basket of the baskets of a
// particular typist.
$scope.addOrder = function(typistBasket) {
typistBasket.orders.push({
// Whatever you want the order to have.
});
};
});

Angularjs combo box key value

I am using Angularjs and I have a dropdown box that lists several items. If the item the user needs is not in the list I need to allow the user to enter the data. Is this possible? How would I do it?
Try this out
Working Demo
html
<div ng-app="myapp">
<fieldset ng-controller="FirstCtrl">
<select ng-options="p.name for p in people" ng-model="selectedPerson"></select>
<br>
Name:<input type="text" ng-model="name"/>
<button ng-click="add(name)">Add</button>
</fieldset>
</div>
script
var myapp = angular.module('myapp', []);
myapp.controller('FirstCtrl', function ($scope) {
$scope.people = [{
name: 'John'
}, {
name: 'Rocky'
}, {
name: 'John'
}, {
name: 'Ben'
}];
$scope.add = function(value)
{
var obj= {};
obj.name = value;
$scope.people.push(obj);
$scope.name = '';
}
});
You seem to want something like a tagging widget. Maybe looking at angular-tags you can accomplish what you want

AngularJS group check box validation

I have a list of check boxes, of which at least one is compulsory. I have tried to achieve this via AngularJS validation, but had a hard time. Below is my code:
// Code goes here for js
var app = angular.module('App', []);
function Ctrl($scope) {
$scope.formData = {};
$scope.formData.selectedGender = '';
$scope.gender = [{
'name': 'Male',
'id': 1
}, {
'name': 'Female',
'id': 2
}];
$scope.formData.selectedFruits = {};
$scope.fruits = [{
'name': 'Apple',
'id': 1
}, {
'name': 'Orange',
'id': 2
}, {
'name': 'Banana',
'id': 3
}, {
'name': 'Mango',
'id': 4
}, ];
$scope.submitForm = function() {
}
}
// Code goes here for html
<!doctype html>
<html ng-app="App">
<head>
<!-- css file -->
<!--App file -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<!-- External file -->
</head>
<body>
<div ng-controller="Ctrl">
<form class="Scroller-Container">
<div ng-app>
<form class="Scroller-Container" ng-submit="submit()" ng-controller="Ctrl">
<div>
What would you like?
<div ng-repeat="(key, val) in fruits">
<input type="checkbox" ng-model="formData.selectedFruits[val.id]" name="group[]" id="group[{{val.id}}]" required />{{val.name}}
</div>
<br />
<div ng-repeat="(key, val) in gender">
<input type="radio" ng-model="$parent.formData.selectedGender" name="formData.selectedGender" id="{{val.id}}" value="{{val.id}}" required />{{val.name}}
</div>
<br />
</div>
<pre>{{formData.selectedFruits}}</pre>
<input type="submit" id="submit" value="Submit" />
</form>
</div>
<br>
</form>
</div>
</body>
</html>
Here is the code in plunker: http://plnkr.co/edit/Bz9yhSoPYUNzFDpfASwt?p=preview Has anyone done this on AngularJS? Making the checkboxes required, forces me to select all the checkbox values. This problem is also not documented in the AngularJS documentation.
If you want to require at least one item in group being selected, it's possible to define dynamic required attribute with ng-required.
For gender radio buttons this would be easy:
<input
type="radio"
ng-model="formData.selectedGender"
value="{{val.id}}"
ng-required="!formData.selectedGender"
>
Checkbox group would be easy too, if you used array to store selected fruits (just check array length), but with object it's necessary to check if any of values are set to true with filter or function in controller:
$scope.someSelected = function (object) {
return Object.keys(object).some(function (key) {
return object[key];
});
}
<input
type="checkbox"
value="{{val.id}}"
ng-model="formData.selectedFruits[val.id]"
ng-required="!someSelected(formData.selectedFruits)"
>
Update:
const someSelected = (object = {}) => Object.keys(object).some(key => object[key])
Also keep in mind that it will return false if value is 0.
Thanks to Klaster_1 for the accepted answer. I've built on this by using ng-change on the checkbox inputs to set a local scope variable, which would be used as the ng-required expression. This prevents the running of someSelected() on every digest.
Here is a plunkr demonstrating this: http://embed.plnkr.co/ScqA4aqno5XFSp9n3q6d/
Here is the HTML and JS for posterity.
<!DOCTYPE html>
<html ng-app="App">
<head>
<meta charset="utf-8" />
<script data-require="angular.js#1.4.9" data-semver="1.4.9" src="https://code.angularjs.org/1.4.9/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="AppCtrl">
<form class="Scroller-Container" name="multipleCheckbox" novalidate="">
<h3>What would you like?</h3>
<div ng-repeat="fruit in fruits">
<input type="checkbox" ng-model="formData.selectedFruits[fruit.name]" ng-change="checkboxChanged()" ng-required="!someSelected" /> {{fruit.name}}
</div>
<p style="color: red;" ng-show="multipleCheckbox.$error.required">You must choose one fruit</p>
</form>
<pre>Fruits model:
{{formData.selectedFruits | json}}</pre>
</div>
</body>
</html>
angular
.module('App', [])
.controller('AppCtrl', function($scope) {
var selectedFruits = {
Mango: true
};
var calculateSomeSelected = function() {
$scope.someSelected = Object.keys(selectedFruits).some(function(key) {
return selectedFruits[key];
});
};
$scope.formData = {
selectedFruits: selectedFruits
};
$scope.fruits = [{
'name': 'Apple'
}, {
'name': 'Orange'
}, {
'name': 'Banana'
}, {
'name': 'Mango'
}];
$scope.checkboxChanged = calculateSomeSelected;
calculateSomeSelected();
});
Okay maybe very late to the party but if you have an array of objects which you are looking at, the solution of just checking the length of array of selected objects worked for me.
HTML
<div ng-repeat="vehicle in vehicles">
<input type="checkbox" name="car" ng-model="car.ids[vehicle._id]" ng-required=" car.objects.length <= 0"> {{vehicle.model}} {{vehicle.brand}} <b>{{vehicle.geofenceName}}</b>
</div>
JS
$scope.vehicles = [{},{}] // Your array of objects;
$scope.car = {
ids: {},
objects: []
};
$scope.$watch(function() {
return $scope.car.ids;
}, function(value) {
$scope.car.objects = [];
angular.forEach($scope.car.ids, function(value, key) {
value && $scope.car.objects.push(getCategoryById(key));
});
}, true);
function getCategoryById(id) {
for (var i = 0; i < $scope.vehicles.length; i++) {
if ($scope.vehicles[i]._id == id) {
return $scope.vehicles[i];
}
}
}
I understand what is happening in this solution that you are checking all the checkbox options to know which one is clicked. But from what I know this is by far the easiest solution(to understand and implement) and it works like a charm if you know that your options will be limited.
For a more time optimized solution. Check the answers above.
We can achieve your requirement without traversing all check-boxes instance.
Here is the sample code
<script>
angular.module('atLeastOneExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.e = function(s){
eval(s);
};
}]);
</script>
Here i am wrapping javascript eval function in "e" function to access it.
<form name="myForm" ng-controller="ExampleController" ng-init="c=0">
<label>
Value1: <input type="checkbox" ng-model="checkboxModel.value[0]" ng-change="e('($scope.checkboxModel.value[0])?$scope.c++:$scope.c--')"/>
</label><br/>
<label>
Value2: <input type="checkbox" ng-model="checkboxModel.value[1]" ng-change="e('($scope.checkboxModel.value[1])?$scope.c++:$scope.c--')"/>
</label><br/>
value = {{checkboxModel.value}}<br/>
isAtLeastOneChecked = {{c>0}}
</form>

Categories