Angularjs ng-submit not working inside ng-repeat form - javascript

I have this form
<tr ng-repeat="quote in quotes">
<form ng-submit="submit()" name="qut">
<td class="text-left">
{[{ quote.business_name }]}
</td>
<td class="text-left">
<span ng-if="quote.quote">
{[{ quote.quote }]}
</span>
<span ng-if="!quote.quote">
<input ng-model="qt" class="form-control" type="text" name="quote" />
</span>
</td>
<td class="text-left">
<span ng-if="quote.status==1">
<input type="submit" class="btn btn-out" value="Quote" />
</span>
</td>
</form>
</tr>
In my controller I have
$scope.submit = function() {
console.log('form');
};
If I change ng-submit="submit()" to ng-click="submit()" in button it works, not sure why I am unable to submit the form

The problem is that you have an illegal html structure by nesting a table > tr element with a form. That causes the inner input[type=submit] not to identify his parent form and trigger the submit.
I could get your example working by replacing tables and tr with div and td with spans.
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.quotes = [{
business_name: "business_name 1",
quote: "quote1",
status: 1
}, {
business_name: "business_name 2",
quote: "quote2",
status: 1
}]
$scope.submit = function() {
console.log('form');
};
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-controller="myController">
<div ng-repeat="quote in quotes">
<form ng-submit="submit()" name="qut{{$index}}">
<span class="text-left">
{{ quote.business_name }}
</span>
<span class="text-left">
<span ng-if="quote.quote">
{{ quote.quote }}
</span>
<span ng-if="!quote.quote">
<input ng-model="qt" class="form-control" type="text" name="quote" />
</span>
</span>
<span class="text-left">
<span ng-if="quote.status==1">
<input type="submit" class="btn btn-out" value="Quote" />
</span>
</span>
</form>
</div>
</div>

Because multiple same form names are being created.
What you should do is you can create dynamic form names inside ng-repeat.
<tr ng-repeat="quote in quotes">
<form ng-submit="submit(qut{{$index}}.$valid)" name="qut{{$index}}">
<td class="text-left">
{[{ quote.business_name }]}
</td>
<td class="text-left">
<span ng-if="quote.quote">
{[{ quote.quote }]}
</span>
<span ng-if="!quote.quote">
<input ng-model="quote.quote" class="form-control" type="text" name="quote{{$index}}" />
</span>
</td>
<td class="text-left">
<span ng-if="quote.status==1">
<input type="submit" class="btn btn-out" value="Quote" />
</span>
</td>
</form>
</tr>
$scope.submit = function(value) {
console.log('form',value);
};

Related

Copy input fields created from *ngFor to input fields in an outer modal

I want to build a pop-up modal edit form to change individual table values in a dynamic grid. Right now, the input fields appear on button click using the function editToggle(i). No more than 4 input fields will ever appear because they are meant to edit the 4 values in my data model. However, the input fields (and values) are being dynamically generated with *ngFor. I need some way to pass/copy those input fields to my modal to edit there instead of on the grid itself (where they currently appear after clicking the edit button).
I have tried to use [(ngModel)] to clone but it does not work. I have tried to pass them using functions but the values return null. Because the HTML only shows one input field (because they are being dynamically created with *ngFor) I do not know of a way to individually pass the values.
<div>
<table align="center">
<tr>
<th>
List of Providers
</th>
</tr>
</table>
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Client Name</th>
<th>Version</th>
<th>API Key</th>
<th>Protected Secret</th>
<th>EDIT/DELETE</th>
</tr>
<tr ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td *ngFor="let col of columns">
<span class="field" *ngIf="i !== index">
{{prov[col]}}
</span>
<span *ngIf="i === index">
<input [(ngModel)]="inputClientName" class="table" value="{{prov[col]}}" (change)="EditItem(i, col, $event.target.value)" type="text" placeholder="{{prov[col]}}">
</span>
<td>
<span *ngIf="editing && i === index">
<button (click)="save()">Save</button>
</span>
<span *ngIf="i !== index">
<button class="edit" name="editButton" (click)="editToggle(i); openEditForm()">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</span>
</td>
</tr>
</table>
<!-- The EDITING Modal -->
<div id="editForm" class="modal_edit">
<div class="modal-content_edit">
<span (click)="save()" class="close">×</span>
<h2 style="margin-bottom: 70px">Edit OAuthAppProvider</h2>
<div>
<label style="margin-bottom: 20px">
Client Name:
</label>
<input [(ngModel)]="inputClientName" id="editClientName" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
Version
</label>
<input id="editClientVersion" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
API Key:
</label>
<input id="editClientAPIKey" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
Protected Secret
</label>
<input id="editClientProtectedSecret" type="text">
</div>
<button style="float: right" class="add" (click)="save()">
<h4 style="font-style: bold">Save</h4>
</button>
<button class="cancel" (click)="save()">
<h4 style="font-style: bold">Cancel</h4>
</button>
</div>
</div>
</div>
export const PROVIDERS: any[] =
[
{
AppID: "11",
ClientName: "sampleclientname1",
apiKey: "sampleapikey1",
Version: "1.0",
protectedsecret: "samplesecret1"
},
{
AppID: "12",
ClientName: "sampleclientname2",
apiKey: "sampleapikey2",
Version: "1.0",
protectedsecret: "samplesecret2"
},
{
AppID: "13",
ClientName: "sampleclientname3",
apiKey: "sampleapikey3",
Version: "1.0",
protectedsecret: "samplesecret3"
},
{
AppID: "14",
ClientName: "sampleclientname4",
apiKey: "sampleapikey4",
Version: "1.0",
protectedsecret: "samplesecret4"
}
]
You can set a variable named something like selectedRowData and set the provider as its value when the user clicks the edit button. The value attribute of the inputs on the modal can be set to the properties of the selected row. It's difficult to tell what the functionality of the other methods is supposed to be without the component code so I made some assumptions. Let me know if you have any other questions about it.
Here's a link to a StackBlitz.
EDIT
The data is only being bound one way via the [value] attribute and there isn't a form object keeping track of all the changes like there would be using Reactive Forms so a model should be created first.
I commented out the original solution and added updates below. The selectedRowData variable is instantiated with a provider object with empty properties. The modal has been updated to use two-way binding with [(ngModel)]. The StackBlitz has also been updated.
The table is updated as the user types their edits into the form. The save button doesn't need to be used unless the data needs to be persisted somewhere.
Check out the Angular Forms Documentation it should help with how to pass form data around between components. What you've created here is similar to Template-driven Forms.
Component
// selectedRowData = null;
selectedRowData = {
AppID: "",
ClientName: "",
apiKey: "",
Version: "",
protectedsecret: ""
};
editToggle(rowData) {
this.selectedRowData = rowData;
}
Table
<div>
<table align="center">
<tr>
<th>
List of Providers
</th>
</tr>
</table>
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Client Name</th>
<th>Version</th>
<th>API Key</th>
<th>Protected Secret</th>
<th>EDIT/DELETE</th>
</tr>
<tr ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td *ngFor="let col of columns">
<span class="field" *ngIf="i !== index">
{{prov[col]}}
</span>
<span *ngIf="i === index">
<input [(ngModel)]="inputClientName" class="table" value="{{prov[col]}}"
(change)="EditItem(value)" type="text" placeholder="{{prov[col]}}">
</span>
<td>
<span *ngIf="editing && i === index">
<button (click)="save()">Save</button>
</span>
<span *ngIf="i !== index">
<button class="edit" name="editButton" (click)="editToggle(prov); openEditForm()">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</span>
</td>
</tr>
</table>
Modal
<!-- The EDITING Modal -->
<div id="editForm" class="modal_edit">
<div class="modal-content_edit">
<span (click)="save()" class="close">×</span>
<h2 style="margin-bottom: 70px">Edit OAuthAppProvider</h2>
<div>
<label style="margin-bottom: 20px">
Client Name:
</label>
<!-- <input id="editClientName" type="text" [value]="selectedRowData?.ClientName"> -->
<input id="editClientName" type="text" [(ngModel)]="selectedRowData.ClientName">
</div>
<div>
<label style="margin-bottom: 20px">
Version
</label>
<!-- <input id="editClientVersion" type="text" [value]="selectedRowData?.Version"> -->
<input id="editClientVersion" type="text" [(ngModel)]="selectedRowData.Version">
</div>
</div>
<div>
<label style="margin-bottom: 20px">
API Key:
</label>
<!-- <input id="editClientAPIKey" type="text" [value]="selectedRowData?.apiKey"> -->
<input id="editClientAPIKey" type="text" [(ngModel)]="selectedRowData.apiKey">
</div>
<div>
<label style="margin-bottom: 20px">
Protected Secret
</label>
<!-- <input id="editClientProtectedSecret" type="text" [value]="selectedRowData?.protectedsecret"> -->
<input id="editClientProtectedSecret" type="text" [(ngModel)]="selectedRowData.protectedsecret">
</div>
<button style="float: right" class="add" (click)="save()">
<h4 style="font-style: bold">Save</h4>
</button>
<button class="cancel" (click)="save()">
<h4 style="font-style: bold">Cancel</h4>
</button>
</div>
</div>
</div>

ng-repeat duplicates ng-click function

so I created a button to open a calendar with an ng-click function:
<button type="button" class="btn btn-default" ng-click="main.open_date()">
<i class="glyphicon glyphicon-calendar"></i>
</button>
I put it inside an ng-repeat, and when I added more fields & tested the button, this happened:
Aren't rows created by ng-repeat unique because they have their own index?
What am I missing here?
update: here is the code for my main.open_date():
me.open_date = function(key){
if(!key){key='date';}
me.uibdates[key] = true;
}
template code:
<tr ng-repeat="detail in main.employee_details track by $index">
<td>
<ui-select ng-model="detail.status" theme="bootstrap">
<ui-select-match placeholder="Select status" allow-clear>{$$select.selected.name$}</ui-select-match>
<ui-select-choices repeat="status in main.status | propsFilter: {name: $select.search} | limitTo: 100">
<div ng-bind-html="status.name | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
</td>
<td>
<p class="input-group">
<span class="input-group-btn">
<button type="button" class="btn btn-default"
ng-click="main.open_date()">
<i class="glyphicon glyphicon-calendar"></i>
</button>
</span>
<input type="text" class="form-control" placeholder="Select date"
uib-datepicker-popup="MM/dd/yyyy"
ng-model="detail.date" is-open="main.uibdates['date']"
ng-change="main.date_change()" />
</p>
</td>
<td>
<input type="text" class="form-control" ng-model="detail.remark" placeholder="">
</td>
<td>
<button style="display: inline; width: 35px;"class="form-control btn btn-primary btn-sm" ng-click="main.add_field($index);" ng-if="$index == (main.employee_details.length - 1)">
<span class="glyphicon glyphicon-plus"></span>
</button>
<button style="display: inline; width: 35px;" class="form-control btn btn-danger btn-sm" ng-click="main.delete_field($index);" ng-if="main.employee_details.length != 1">
<span class="glyphicon glyphicon-trash"></span>
</button>
</td>
</tr>
In my imagine, you can customize your code:
HTML:
<div ng-repeat="item in listItems track by item.id + $index">
<input class="datetime-picker" id="datepicker_{{item.id}}" />
<button ng-click="main.open_date(item.id)" class="open-datetime-picker">Open</button>
</div>
Angularjs
main.open_date = function(id){
$("datepicker_" + id).datetimepicker();
}
Everything inside the ng-repeat are repeated except the index. If you didn't use the index, the button is not unique.
in function main.open_date() can you use id instead class or html tags
like this
$('#datetimepicker1').datetimepicker();
Did you add $index in ng-repeat? Example: <div ng-repeat="item in listItems track by $index"></div>
Or, if your item have an ID field: <div ng-repeat="item in listItems track by item.id + $index"></div>

JSRender pass two "datasets" to template

I have this working template:
<script id="UpdateTemplate" type="text/x-jsrender">
<div class="ms-PanelPoultry">
<button class="ms-Button" id="*****" style="visibility: hidden";>
<span class="ms-Button-label">Open Panel</span>
</button>
<div class="ms-Panel ****">
<div class="ms-Panel-contentInner">
<p class="ms-Panel-headerText"></p>
<div class="ms-Panel-content">
<span class="ms-font-m">
<span style="color:#006; font-size:large">***</span>
<hr>
<form id="*****">
<table width="100%" border="0">
{{for}}
{{if (#index) % 3 === 0 }}
</tr>
<tr>
<td>
<div class="form-group">
<label for={{>name}}>{{>label}}</label>
<input type={{>type}} class="form-control" id={{>name}}>
</div>
</td>
{{else}}
{{if #index === 0 }}
<tr>
<td>
<div class="form-group">
<label for={{>name}}>{{>label}}</label>
<input type={{>type}} class="form-control" id={{>name}}>
</div>
</td>
{{else}}
<td style="padding-left:15px;">
<div class="form-group">
<label for={{>name}}>{{>label}}</label>
<input type={{>type}} class="form-control" id={{>name}}>
</div>
</td>
{{/if}}
{{/if}}
{{/for}}
</table>
<hr>
<table>
<tr>
<td>
<button type="submit" id="EditProductiebedrijfButton" class="btn btn-primary">Submit</button>
<button class="btn btn-secondary" type="Cancel" onClick="panelInstance.dismiss();">Cancel</button>
</td>
</tr>
</table>
</form>
</span>
</div>
</div>
</div>
</div>
</script>
It creates an form in an ms-panel with this data:
var Updatefields = [
{ name: "field1", type: "text", label: "blabla" },
{ name: "field2", type: "text", label: "bla" },
{ name: "field3", type: "date", label: "blablaaa" }
];
This is all working fine and rendering my form. But I want to pass some extra data to the "header of the template". Where the "*****" are now. For example the "form id".
How can I achieve that?
Also I would like to use a "prefix" for all my "name" values. For example name is "field3" as id for the input field I would like to have "field3Update"
I tried to do some string concat but that failed sofar.
Edit: Last question was very simple. Turned out to do this: id={{>name}}Update
Either pass in additional data as data, along with the fields array:
$("#xxx").render({
fields: fields,
other: otherData
});
Where otherData is
{
whatever: ...,
...
}
and then write
<form id="{{>other.whatever...}}...">
<table...>
{{for fields}}
or pass in additional data as helpers (http://www.jsviews.com/#tmplrender#helpers):
$("#xxx").render(fields, otherData, true);
and then write
<form id="{{>~whatever...}}...">
<table...>
{{for}}

Showing/Hiding Fields on checkbox event using jQuery

I tried to use this issue as a guide, but I'm not sure what I'm doing wrong with my below code. I'd appreciate it if someone could help out.
A little background: I have limited ability to change the HTML as it's in Confluence using an add-on called Confiforms. I can hide/show fields using Confiforms, but I generally find that it takes a performance hit when I configure it that way.
HTML:
<tr>
<td class="label editLabel">Customer Impact?</td>
<td>
<p class="auto-cursor-target">
<span class="i_holdingrow_impact">
<span id="i_holdingrow_impact">
<input cf-field="impact" class="radio" type="checkbox" name="impact" id="i_impact">
</span>
</span>
</p>
</td>
</tr>
<tr>
<td/>
<td>
<p>
<span class="i_holdingrow_impactDesc">
<span id="i_holdingrow_impactDesc">
<textarea cf-field="impactDesc" id="i_impactDesc" name="impactDesc" rows="4" class="textarea large-field cf_textarea"/>
</span>
</span>
</p>
</td>
</tr>
Relevant jQuery:
$('input[type="checkbox"]').change(() => {
if ($(this).is(':checked')) {
$(this).closest('tr').next('tr').show();
}
else {
$(this).closest('tr').next('tr').hide();
}
});
Thanks!
You should be very careful when using jquery with arrow functions:
$('input[type="checkbox"]').change(function() {
if ($(this).is(':checked')) {
$(this).closest('tr').next('tr').show();
} else {
$(this).closest('tr').next('tr').hide();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td class="label editLabel">Customer Impact?</td>
<td>
<p class="auto-cursor-target">
<span class="i_holdingrow_impact">
<span id="i_holdingrow_impact">
<input cf-field="impact" class="radio" type="checkbox" name="impact" id="i_impact">
</span>
</span>
</p>
</td>
</tr>
<tr>
<td/>
<td>
<p>
<span class="i_holdingrow_impactDesc">
<span id="i_holdingrow_impactDesc">
<textarea cf-field="impactDesc" id="i_impactDesc" name="impactDesc" rows="4" class="textarea large-field cf_textarea"></textarea>
</span>
</span>
</p>
</td>
</tr>
</table>
The arrow-function preserve the context of this, thus it's no longer the relevant element you attached the event to.
In case you do want to use arrow function, you can use the following:
$('input[type="checkbox"]').change((evt) => {
el = evt.currentTarget;
if ($(el).is(':checked')) {
$(el).closest('tr').next('tr').show();
} else {
$(el).closest('tr').next('tr').hide();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td class="label editLabel">Customer Impact?</td>
<td>
<p class="auto-cursor-target">
<span class="i_holdingrow_impact">
<span id="i_holdingrow_impact">
<input cf-field="impact" class="radio" type="checkbox" name="impact" id="i_impact">
</span>
</span>
</p>
</td>
</tr>
<tr>
<td/>
<td>
<p>
<span class="i_holdingrow_impactDesc">
<span id="i_holdingrow_impactDesc">
<textarea cf-field="impactDesc" id="i_impactDesc" name="impactDesc" rows="4" class="textarea large-field cf_textarea"></textarea>
</span>
</span>
</p>
</td>
</tr>
</table>

ng-form with $setPristine()

<tr ng-form="fbForm_{{$index}}">
<td><center><p ng-hide="fb.editMode">{{fb.clientId}}</p>
<input type="text" class="form-control" name="clientId" ng-show="fb.editMode" required
ng-model="fb.clientId"/></center>
<span ng-show="fbForm_{{$index}}.clientId.$dirty && fbForm_{{$index}}.clientId.$error.required">Client Id is required.</span>
</td>
<td><center><p ng-hide="fb.editMode">{{fb.clientSecret}}</p>
<input type="text" class="form-control" name="clientSecret" ng-show="fb.editMode" required
ng-model="fb.clientSecret"/></center>
<span ng-show="fbForm_{{$index}}.clientSecret.$dirty && fbForm_{{$index}}.clientSecret.$error.required">Client secret is required.</span>
</td>
<td style="width:10%">
<p ng-hide="fb.editMode"><a ng-click="toggleEdit()" href="javascript:;">Edit</a> | <a ng-click="deletefacebook()" href="javascript:;">Delete</a></p>
<p ng-show="fb.editMode"><a class="btn btn-primary simple_button" ng-disabled="fbForm_{{$index}}.$pristine || fbForm_{{$index}}.$invalid" data-ng-click="save($index); fbForm_{{$index}}.$setPristine()" href="javascript:;">Save</a> | <a class="btn btn-primary simple_button" ng-click="cancel($index,fb.fbConfigId)" href="javascript:;">Cancel</a></p>
</td>
</tr>
http://plnkr.co/edit/LnnJQj1WwQaxLjlIWXq2?p=preview
for ng-disabled I am able to use {{$index}}
Getting syntax error in pristine function form name.
ngDisabled and ngClick directives expect expressions, so it should be something like this:
ng-disabled="this['fbForm_' + $index].$pristine || this['fbForm_' + $index].$invalid"
ng-click="save($index); this['fbForm_' + $index].$setPristine()"

Categories