I'm trying to dynamically generate a button in Angular. On click, that button needs to call the deleteRow() function and pass in a username. The applicable username is successfully passed to the controller and the resulting button code appears to be properly generated. However, they button ends up passing undefined to the deleteRow() function. Is this a problem with how I'm using $compile?
validationApp.controller('usersController', function ($scope, $compile, $http, $timeout){
$(document).on("click", ".open-confirm-dialog", function () {
var username = $(this).data('id');
var btnHtml = '<button class="btn btn-danger" data-title="Delete" ng-click="deleteRow(' + username + ')">DELETE</button>';
var temp = $compile(btnHtml)($scope);
angular.element(document.getElementById('deleteButton-dynamic')).append(temp);
});
$scope.deleteRow = function(username){
alert(username); //This shows 'undefined' in the pop-up
var request = $http({
method: "post",
url: "scripts/delete.php",
data: { un: username },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
request.success(function() { });
location.reload();
};
HTML as follows:
<div class="row" ng-controller="usersController">
<div class="table-responsive col-md-12" ng-show="filteredItems > 0">
<table id="users" class="table table-bordred table-striped">
<thead>
<th ng-click="sort_by('username');">Username: <i class="glyphicon glyphicon-sort"></i></th>
<th ng-click="sort_by('submitted_info');">Submitted Info.: <i class="glyphicon glyphicon-sort"></i></th>
</thead>
<tbody>
<tr ng-repeat="data in filtered = (list | orderBy : predicate :reverse)">
<td>{{data.username}}</td>
<td>{{data.submitted_info}}</td>
<td></span></td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12" ng-show="filteredItems == 0">
<div class="col-md-12">
<h4>No customers found</h4>
</div>
</div>
<div class="col-md-12" ng-show="filteredItems > 0">
<div pagination="" page="currentPage" on-select-page="setPage(page)" boundary-links="true" total-items="filteredItems" items-per-page="entryLimit" class="pagination-small" previous-text="«" next-text="»"></div>
</div>
<!-- Confirm Modal -->
<div id="confirmModal" user-id="" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirmModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body">
Are you sure you want to delete this user from the database?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<span id="deleteButton-dynamic"></span>
<!--Working HardCoded Button
<button class="btn btn-danger" data-title="Delete" ng-click="deleteRow('user1')">WorkingButton</button>
-->
</div>
</div>
</div>
</div>
Angular assumes the value passed to deleteRow is part of the expression, therefore it checks the scope for a key that matches the value of username. Change the ng-click expression by wrapping the concatenated username string in quotes. E. G. deleteRow(\''+ username + '\')
Suggest using a directive.
To do this you would need to do the following based on the code you gave:
define the directive javascript itself
// First define the directive controller
function dynamicButton ($scope, $http){
$scope.deleteRow = function(){
// here $scope.username is defined based on the value from the parent controller
};
});
// This registers the directive with angular
validationApp.directive(dynamicButton.name, function(){
return {
controller: dynamicButton.name,
restrict: 'E',
templateUrl: 'pathtoyourhtmlpartial',
scope: {
username: '='
}
}
}
Update the html, i.e. call the directive from the original controller and save the new partial for the button.
trigger the directive in your original controller. (e.g. $scope.buttonSwitchedOn), when that it true, angular will automatically load in and run your directive
Related
I am trying to figure out how to get this working.
I am using AngularJS because I do not want to load the complete NPM of Angular and we are using Razor Syntax extensively for the Web Layer
On the Create.cshtml page
<button type="button" ng-click="addApp('Cheese')"
class="btn-sm btn-default no-modal">
Add Applications
</button>
Below is how I have the directory stucture is as follows:
WebLayer
+--wwwroot
+--js
+--applications (Feature)
+applications.component.js
+applications.module.js
+application.template.html
+--delegates (Feature)
+delegates.component.js
+sames as above
+--sponsor-applictions
+same as above
+--lib
+angularjs and all other angularjs files
+app.config.js
+app.module.js
Now I have no problems with getting data below in the sponsor-applictions.component.js I am getting my JSON Object Models arrays from my API.
//Author Moojjoo
//Date 5/3/2019
//sponsor-applications
'use strict';
var testUrl = window.location.hostname;
if (testUrl.includes("localhost")) {
var apiUrl = 'https://localhost:44364';
}
else if (testUrl.includes("uat")) {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for UAT
}
else {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for PRD
}
// Register `sponsorApplications` component, along with its associated controller and template
angular.
module('App').
component('sponsorApplications', {
templateUrl: '../../js/sponsor-applications/sponsor-applications.template.html',
controller: ['$scope', '$http', function SponsorApplications($scope, $http) {
var user_id = $("#User_Id").val();
if (user_id != "") {
$http.get(apiUrl + '/api/v1/Sponsors/' + user_id + '/Applications').then(function successCallback(response) {
$scope.sponsorApplications = response.data;
console.log($scope.sponsorApplications);
}, function errorCallback() {
//var type = 'warning';
//var title = 'User Lookup Required!';
//var body = 'Please enter a User Login ID for lookup'
alert('Please try again later, network error, email sent to application administrator')
});
}
//TODO - Have to get rows from Modal to Page
// Add Rows
$scope.addApp = function (arg) {
debugger;
console.log(arg);
alert(arg);
//$scope.table.push($scope.newApp);
// $scope.newApp = new Object();
};
// Remove Rows
$scope.remove = function (index) {
debugger;
$scope.sponsorApplications.splice(index, 1);
};
}
]
});
I am banging my head against the keyboard trying to figure out why the addApp('Cheese') will not event for for ng-click. $scope.remove function works without any issues.
I really need to understand what I am missing. Thank you and if you need more details simply add a comment.
Edit adding full code
app.config
'use strict';
angular.
module('App').
config(['$routeProvider',
function config($routeProvider) {
$routeProvider.
when('/Sponsors/Create', {
template: '<sponsor-applications></sponsor-applications>'
}).
when('/Sponsors/Create', {
template: '<applications></applications>'
}).
when('/Sponsors/Create', {
template: '<sponsor-delegates></sponsor-delegates>'
}).
when('/Sponsors/Create', {
template: '<delegates></delegates>'
})
}
]);
app.module.js
'use strict';
angular.module('App', [
'ngRoute',
'sponsorApplications',
'applications',
'sponsorDelegates',
'delegates'
])
.run(function () {
console.log('Done loading dependencies and configuring module!');
});
sponsor-applications.component.js
//Author Robert B Dannelly
//Date 4/8/2019
//sponsor-applications
'use strict';
var testUrl = window.location.hostname;
if (testUrl.includes("localhost")) {
var apiUrl = 'https://localhost:44364';
}
else if (testUrl.includes("uat")) {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for UAT
}
else {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for PRD
}
// Register `sponsorApplications` component, along with its associated controller and template
angular.
module('App').
component('sponsorApplications', {
templateUrl: '../../js/sponsor-applications/sponsor-applications.template.html',
controller: ['$scope', '$http', function SponsorApplications($scope, $http) {
var user_id = $("#User_Id").val();
if (user_id != "") {
$http.get(apiUrl + '/api/v1/Sponsors/' + user_id + '/Applications').then(function successCallback(response) {
$scope.sponsorApplications = response.data;
console.log($scope.sponsorApplications);
}, function errorCallback() {
//var type = 'warning';
//var title = 'User Lookup Required!';
//var body = 'Please enter a User Login ID for lookup'
alert('Please try again later, network error, email sent to application administrator')
});
}
//TODO - Have to get rows from Modal to Page
// Add Rows
$scope.addApp = function (arg) {
debugger;
console.log(arg);
alert(arg);
//$scope.table.push($scope.newApp);
// $scope.newApp = new Object();
};
// Remove Rows
$scope.remove = function (index) {
debugger;
$scope.sponsorApplications.splice(index, 1);
};
}
]
});
sponsor-applications.module.js
'use strict';
// Define the `sponsorApplicaitons` module
angular.module('sponsorApplications', []);
sponsor-applications.template.html
<style>
/* Overwrites */
.btn {
width: 100%;
}
</style>
<table class="table-bordered table-striped" style="width:100%;">
<thead style="font-weight:bold">
<tr>
<td>Remove</td>
<td>Application ID</td>
<td>Application Name</td>
</tr>
</thead>
<!-- The naming must be exact application matches the $scope.sponsorApplications
in sponsor-applications.component.js-->
<tr ng-repeat="app in sponsorApplications">
<td>
<a class="btn" ng-click="remove($index)"><i class="fa fa-times" aria-hidden="true"></i></a>
</td>
<td>{{ app.application_ID }}</td>
<td>{{ app.application_Name }}</td>
</tr>
</table>
Create.cshtml -- ASP.NET Core w/ Razor Syntax ----
#model WEB.ViewModels.AddSponsorViewModel
#using WEB.HtmlHelpers
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<!-- Alert Display will be used as a standard on all page simply add this to your page
https://www.trycatchfail.com/2018/01/22/easily-add-bootstrap-alerts-to-your-viewresults-with-asp-net-core/
https://www.trycatchfail.com/2018/02/21/easy-bootstrap-alerts-for-your-api-results-with-asp-net-core/-->
#await Html.PartialAsync("~/Views/Shared/_StatusMessages.cshtml")
<h2>Sponsor Information</h2>
<form asp-action="Create" id="CreateSponsor">
#Html.AntiForgeryToken()
#*<input type=hidden asp-for="User_ID" />*#
<input type="hidden" id="User_Id" name="User_Id" value="" />
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="col-md-12">
<row>
<div class="form-group col-md-5">
<label asp-for="Domain_ID" class="control-label"></label>
<br />
<input asp-for="Domain_ID" class="form-control" />
<span asp-validation-for="Domain_ID" class="text-danger"></span>
</div>
<div class="col-md-2">
</div>
<div class="form-group col-md-5">
<label asp-for="Name" class="control-label"></label>
<br />
<input asp-for="Name" class="form-control" />
#*#Html.AutocompleteFor(model => Core.Models.SearcherUser.Name, model => Core.Models.SearcherUser.Email, "GetSponsor", "Approval", false)*#
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</row>
</div>
<div class="col-md-12">
<row>
<div class="form-group col-md-12">
<label asp-for="Email" class="control-label"></label>
<br />
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</row>
</div>
<div class="col-md-12" style="margin-top:20px">
<row>
<div class="col-md-6">
<strong>Delegates</strong> <asp:button formnovalidate="formnovalidate" id="addDelegate" style="cursor: pointer" class="btn-xs btn-primary" data-toggle="modal" data-target="#delegatesModal">Add</asp:button>
<!-- AngularJS defined in wwwroot > js > sponsor-applications -->
<sponsor-delegates></sponsor-delegates>
</div>
<div id="divMyAppCtrl" class="col-md-6">
<strong>Applications</strong> <asp:button formnovalidate="formnovalidate" id="addApplication" style="cursor: pointer" class="btn-xs btn-primary" data-toggle="modal" data-target="#appModal">Add</asp:button>
<!-- AngularJS defined in wwwroot > js > sponsor-applications -->
<br />
<sponsor-applications></sponsor-applications>
</div>
</row>
</div>
<div class="col-md-12" style="margin-top:50px;">
<row>
<div class="col-md-2">
<input type="submit" value="Delete" disabled class="btn btn-default" />
</div>
<div class="col-md-offset-6 col-md-2" style="text-align:right">
<input type="submit" value="Save" class="btn btn-default" />
</div>
<div class="col-md-2">
<button class="btn btn-default" type="button" id="cancel" onclick="location.href='#Url.Action("Index", "Sponsors")'">Cancel</button>
</div>
</row>
</div>
</div>
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
<!-- Modal to select delegates for sponsor -->
<div class="modal fade" tabindex="-1" role="dialog" id="delegatesModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<strong>Please select the Delegates</strong>
<div id="delgates_tbl">
<!-- AngularJS defined in wwwroot > js > applications -->
<delegates></delegates>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn-sm btn-default no-modal" data-dismiss="modal" id="closeDelegate">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
Add Applications Button in Modal
<!-- Modal to select application for sponsor -->
<div class="modal fade" tabindex="-1" role="dialog" id="appModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<strong>Please select the applications</strong>
<div id="divModalApps">
<!-- AngularJS defined in wwwroot > js > applications -->
<applications></applications>
</div>
</div>
<div class="modal-footer">
<button type="button"
ng-click="addApp('Cheese')"
class="btn-sm btn-default no-modal">
Add Applications
</button>
<button type="button" class="btn-sm btn-default no-modal"
data-dismiss="modal" id="closeApps">
Close
</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
----
the button HTML is on the Create.cshtml page, but so are all the templates aka
<sponsor-applications>
</sponsor-applications>
Also note that in the _Layouts.cshtml page all js files are referenced. ~/app.module.js, ~/app.config.js, ~/js/sponsor-delegates/sponsor-delegate.module.js, ~/js/sponsor-delegates/sponsor-delegates.component.js
sponsor-applications.component.js
//Author Robert B Dannelly
//Date 4/8/2019
//sponsor-applications
'use strict';
var testUrl = window.location.hostname;
if (testUrl.includes("localhost")) {
var apiUrl = 'https://localhost:44364';
}
else if (testUrl.includes("uat")) {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for UAT
}
else {
var apiUrl = 'https://localhost:44364'; //TODO when the URL is decided for PRD
}
// Register `sponsorApplications` component, along with its associated controller and template
angular.
module('App').
component('sponsorApplications', {
templateUrl: '../../js/sponsor-applications/sponsor-applications.template.html',
controller: ['$scope', '$http', function SponsorApplications($scope, $http) {
var user_id = $("#User_Id").val();
if (user_id != "") {
$http.get(apiUrl + '/api/v1/Sponsors/' + user_id + '/Applications').then(function successCallback(response) {
$scope.sponsorApplications = response.data;
console.log($scope.sponsorApplications);
}, function errorCallback() {
//var type = 'warning';
//var title = 'User Lookup Required!';
//var body = 'Please enter a User Login ID for lookup'
alert('Please try again later, network error, email sent to application administrator')
});
}
//TODO - Have to get rows from Modal to Page
// Add Rows
**********************BELOW is the correct SYNTAX********************
this.addApp = function (arg) {
alert(arg);
debugger;
console.log(arg);
};
//$scope.addApp = function (arg) {
// debugger;
// console.log(arg);
// alert(arg);
// //$scope.table.push($scope.newApp);
// // $scope.newApp = new Object();
//};
// Remove Rows
$scope.remove = function (index) {
debugger;
$scope.sponsorApplications.splice(index, 1);
};
}
]
});
Create.cshtml Changes
In the template
<div id="divMyAppCtrl" class="col-md-6">
<strong>Applications</strong> <asp:button formnovalidate="formnovalidate" id="addApplication" style="cursor: pointer" class="btn-xs btn-primary" data-toggle="modal" data-target="#appModal">Add</asp:button>
<!-- AngularJS defined in wwwroot > js > sponsor-applications -->
<br />
<sponsor-applications ng-ref="sponsorApplications"></sponsor-applications>
</div>
Add Applications Modal in the footer
<div class="modal-footer">
<button type="button" ng-click="sponsorApplications.addApp('CLicked')" class="btn-sm btn-default no-modal">Add Applications</button>
<button type="button" class="btn-sm btn-default no-modal" data-dismiss="modal" id="closeApps">Close</button>
</div>
I have a modal with a hidden field of my current id that I need.
I need to click a button in my modal to confirm if I should delete a user. I setup a form not sure if this is the best option and in javascript set the attr of form to the route but the route isn’t finding the correct path although in the URL it says it correctly what am I missing maybe the route should be in PHP instead of JS?
<div class="modal" id="mdelete" role="dialog" aria-labelledby="moddelete">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="moddelete">Confirm Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete</p>
</div>
<div class="modal-footer">
<form method="POST" id="formdelete">
<input type="hidden" name="txtid" id="txtid" />
<input type="text" name="uid" id="uid" />
<button type="button" class="btn btn-danger " data-dismiss="modal">No</button>
<span class="text-right">
<button type="submit" class="btn btn-primary btndelete">Yes</button>
</span>
</form>
</div>
</div>
Show
Edit
<button type="button" class="btn btn-danger ml-2" data-toggle="modal"
data-target="#mdelete" data-id="{{$user->id}}"
data-name="{{$user->username}}">Delete</button>
$(document).ready(function() {
$('#mdelete').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var userid = button.data('id');
var uname = button.data('name');
var modal = $(this);
modal.find('#txtid').val(userid);
modal.find('#uid').val(userid);
modal.find('.modal-body').text('Are you sure you want to delete ' + uname);
})
$('#formdelete').submit(function() {
var userid = $('#txtid').val();
$('#formdelete').attr("action", "route('$users.destroy',$user->"+ userid +")");
$('#formdelete').submit();
});
});
Your attempt to generate route will fail because you are not using templating correctly:
$('#formdelete').attr("action", "route('$users.destroy',$user->"+ userid +")");
You can try to have your route accept optional user field, and then generate route to it, and with js append user ID value something like so:
<script>
$(function() {
var form = $('#formdelete');
var path = '{{ route("users.destroy") }}';
$('#formdelete').submit(function(event) {
var form = $(this);
var userid = form.find('#txtid').val();
$('#formdelete').attr("action", path + '/' + userid);
$('#formdelete').submit();
});
});
</script>
I'm using angularjs to create a modal used by a form.
In my javascript I receive only two fields of my form (name and clientVersion) the other two are omitted and I don't know why.
This is my modal:
<div class="modal" id="addUserModal" data-ng-app="myApp">
<div class="modal-dialog" data-ng-controller="modalController">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">New user</h4>
</div>
<div class="modal-body">
<form novalidate class="simple-form">
<!-- form start -->
<div class="box-body">
<div class="form-group">
<label>Name</label> <input id="name" type="text"
data-ng-model="newUser.name" class="form-control"
maxlength="30" placeholder="Version name" required>
</div>
<div class="form-group">
<label>Role</label> <select class="form-control select2"
style="width: 100%;" name="role" data-ng-model="newUser.role"
data-ng-options="user.idRole as role.role for role in roles track by role.role">
</select>
</div>
<div class="form-group">
<label>Client Version </label> (optional) <select
class="form-control select2" style="width: 100%;"
name="version" data-ng-model="newUser.clientVersion"
data-ng-options="version.idClientVersion as version.name for version in versions track by version.name">
</select>
</div>
<div class="form-group">
<label>Enable </label> <input type="checkbox"
data-ng-model="newUser.enabled" name="my-checkbox" checked>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
data-dismiss="modal">Close</button>
<button id="uploadVersionButton" type="button"
class="btn btn-primary" data-ng-click="createUser(newUser)">Create
user</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
In my javascript code I have
//Angular section for Select2 of modal create user
var app = angular.module('myApp',[]);
app.controller('modalController', function($scope, $http) {
$http({
method: 'GET',
url: 'roles'
}).then(function successCallback(response) {
$scope.roles = response.data.result;
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$http({
method: 'GET',
url: 'version',
}).then(function successCallback(response) {
$scope.versions = response.data.result;
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$scope.createUser = function(newUser) {
$scope.master = angular.copy(newUser);
};
});
and newUser has only those two fields. Do you know why?
First error is in
data-ng-options="role.idRole as role.role for role in roles track by role.role"
I gave the wrong idRole.
The checkbox is a prolem of bootstrap plugin, without it all works.
I found this directive and work:
app.directive('bootstrapSwitch', [
function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
element.bootstrapSwitch();
element.on('switchChange.bootstrapSwitch', function(event, state) {
if (ngModel) {
scope.$apply(function() {
ngModel.$setViewValue(state);
});
}
});
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue) {
element.bootstrapSwitch('state', true, true);
} else {
element.bootstrapSwitch('state', false, true);
}
});
}
};
}
]);
but perhaps it is better to use
$('input[name="my-checkbox"]').on('switchChange.bootstrapSwitch', function(event, state)
and store state into a variable.
I've followed this tutorial about AngularJS Multi-Step Form Using UI Router. The form works and I can save my data but now I'm having questions about how to validate each step in the form.
I have the following form with input fields:
Step 1
License Plate
Step 2
Name
Street
Zipcode
City
Email
Telephone
Step 3
Choose a date & time from a calendar
It looks somewhat like this:
I have a general base view like this:
<body ng-app="formApp">
<div id="top"></div>
<div class="container">
<!-- views will be injected here -->
<div ui-view></div>
</div>
</body>
In my app.js I have the following (not complete, left the non important things out):
// app.js
// create our angular app and inject ngAnimate and ui-router
// =============================================================================
angular.module('formApp', ['ngAnimate', 'ui.router', 'ui.calendar'])
// configuring our routes
// =============================================================================
.config(function($stateProvider, $urlRouterProvider, $interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
$stateProvider
// route to show our basic form (/form)
.state('form', {
url: '/form',
templateUrl: 'views/form.html',
controller: 'formController'
})
// nested states
// each of these sections will have their own view
// url will be /form/interests
.state('form.license', {
url: '/license',
templateUrl: 'views/form-license.html'
})
// url will be nested (/form/profile)
.state('form.profile', {
url: '/profile',
templateUrl: 'views/form-profile.html'
})
// url will be /form/payment
.state('form.appointment', {
url: '/appointment',
templateUrl: 'views/form-appointment.html'
})
// url will be /form/success
.state('form.success', {
url: '/success',
templateUrl: 'views/form-success.html'
});
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/license');
})
// our controller for the form
// =============================================================================
.controller('formController', function($scope, $http, $compile, $location, uiCalendarConfig) {
$scope.formData = {};
$scope.formData.profile = {};
$scope.next = function(step){
if(step == 1)
{
}
else if(step == 2)
{
}
};
// function to process the form
$scope.processForm = function(isValid) {
};
});
My general form.html:
<!-- form.html -->
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div id="form-container">
<form id="appointment-form" name="appointmentform" ng-submit="processForm(appointmentform.$valid)">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
</div>
</div>
The first step in my form is in form-license.html:
<!-- form-license.html -->
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
But now I'm wondering how I can validate this when they click on next button ... . It's not working with the normal required attribute.
Can somebody help me with this?
UPDATE:
Now I have in my first step the following:
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, processForm)" ui-sref="form.profile" ng-disabled="!licenseValidated" class="btn btn-next btn-block">
Volgende
</a>
</div>
In my controller:
var validateLicense = function (newVal) {
var validated = false;
// Run your custom validation checks
if(newVal)
{
validated = true;
}
return validated;
};
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
Ok, that works. But in my second step I have multiple fields like this:
<div class="profile">
<div class="form-group">
<label class="col-sm-3 control-label" for="name">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="name" ng-model="formData.profile.name">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="street">Street</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="street" ng-model="formData.profile.street">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="zipcode">Zipcode</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="zipcode" ng-model="formData.profile.zipcode">
</div>
</div>
<div class="form-group row">
<div class="col-xs-8 col-xs-offset-2">
<a ng-click="next(1)" ui-sref="form.license" class="btn btn-block btn-previous col-xs-3">
VORIGE
</a>
<a ng-click="next(2)" ui-sref="form.appointment" class="btn btn-block btn-next col-xs-3">
Volgende
</a>
</div>
</div>
</div>
Do I need to create for every one of them a $scope.watch? And do I need to add them to ng-disabled of my button?
You could simply disable the next button if any of the validation steps doesn't pass.
Something like:
// Inside your controller.
// Don't overload the scope.
// Only assign what will be needed through your HTML or other AngularJS Scopes
var validateLicense = function (newVal) {
// If you are only checking for content to be entered
return (newVal !== '' && newVal !== undefined);
};
var validateInfo = function (newVal) {
if (newVal.length > 0) {
// Check to make sure that all of them have content
for (var i = 0, l = newVal.length; i < l; i++) {
if (newVal[i] === undefined || newVal[i] === '') {
return false;
}
}
// We didn't find invalid data, let's move on
return true;
}
return false;
};
var validateDate = function (newVal) {
var validated = false;
// Run your custom validation checks
return validated;
}
// Initialize the disabled "Next" buttons
$scope.licenseValidated = false;
$scope.infoValidated = false;
// Watch a single item in a form, if filled in we will let them proceed
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
// Watch a multiple items in a form, if ALL are filled in we will let them proceed
// Note that the order in this array is the order the newVal will be,
// So further validation for formData.number would be on newVal[1]
$scope.$watchGroup(['formData.name', 'formData.number', 'formData.address'], function (newVal) {
$scope.infoValidated = validateInfo(newVal);
});
form-license.html add the ng-disabled attribute on your next button:
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!licenseValidated">
Volgende
</a>
form-info.html repeat above steps
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!infoValidated">
Volgende
</a>
And so on...
See this Fiddle for Demo
You have a couple of options available to you depending on how you want to approach it.
To start, you should use ng-form for each of the 3 form steps. This will allow you to validate each individually without having to worry about the other sections.
So as an example your first form step might turn into:
<ng-form name="LicenseForm">
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, LicenseForm)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
</ng-form>
This gives you access to the form validation properties for just this step. At this point you can update your controller to use .$invalid or .$valid on the form object that is now being passed in the next submit button, meaning you can now do something like:
$scope.next = function(step, form) {
if (form.$invalid) {
console.log('Form is invalid!');
return;
}
// move to next section
...
};
I'm banging my head against the wall here. I'm using ng-repeat to populate a table. Inside each row i have 2 buttons, one for updating the row content and for uploading files. The upload button opens a bootstrap modal window, where the user selects the files and clicks on submit.
The submit button uses ng-click to run a function which uses $index as parameter. But the $index value is always the same no matter which row is selected.
The thing I don't understand is that I use the exact same syntax (although outside of a modal window) on my update button, which works just fine.
HTML:
<tr ng-repeat="item in items | filter:search " ng-class="{'selected':$index == selectedRow}" ng-click="setClickedRow($index)">
<td>{{$index}}</td>
<td ng-hide="idHidden" ng-bind="item.Id"></td>
<td ng-hide="titleHidden">
<span data-ng-hide="editMode">{{item.Title}}</span>
<input type="text" data-ng-show="editMode" data-ng-model="item.Title" data-ng-required />
<td>
<button type="button" class="btn btn-primary uploadBtn" data-ng-show="editMode" data-toggle="modal" data-target="#uploadModal">Upload file <i class="fa fa-cloud-upload"></i></button>
<!-- Upload Modal -->
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="uploadModalLabel">Options</h3>
</div>
<div class="modal-body">
<h4>Upload Documents</h4>
<form>
<div class="form-group">
<select data-ng-model="type" class="form-control" id="fileTypeSelect">
<option value="Policy">Policy</option>
<option value="SOP">SOP</option>
</select>
<br>
<div class="input-group"> <span class="input-group-btn">
<input type="file" id="file">
</span>
</div>
<br>
<button type="button" class="btn btn-default" data-ng-click="uploadAttachment($index, type)">Upload</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<button type="button" data-ng-hide="editMode" data-ng-click="editMode = true;" class="btn btn-default pull-right">Edit <i class="fa fa-pencil-square-o"></i></button>
<button type="button" data-ng-show="editMode" data-ng-click="editMode = false; updateItem($index)" class="btn btn-default">Save</button>
<button type="button" data-ng-show="editMode" data-ng-click="editMode = false; cancel()" class="btn btn-default">Cancel</button>
</td>`
JS:
$scope.uploadAttachment = function executeUploadAttachment(index, type) {
var listname = "Risk Register";
var id = $scope.items[index].Id;
console.log(indexID);
readFile("uploadControlId").done(function(buffer, fileName) {
uploadAttachment(type, id, listname, fileName, buffer).done(function() {
alert("success");
}).fail(function() {
alert("error in uploading attachment");
})
}).fail(function(err) {
alert("error in reading file content");
});
}
So the function uploadAttachment($index, type) which is triggered by ng-click doesn't pass the right index number. It always passes the same, no matter what row it is clicked in.
I have omitted some of the code that is irrelevant. If needed i can provide the whole thing.
Any suggestions to what I am missing?
Edit:
I have tried to implement DonJuwe suggestions.
I have added this inside my controller:
$scope.openModal = function(index) {
var modalInstance = $modal.open({
templateUrl: 'www.test.xxx/App/uploadModal.html',
controller: 'riskListCtrl',
resolve: {
index: function() {
return index;
}
}
});
};
This is my modal template:
<div class="modal-header">
<h3 class="modal-title" id="uploadModalLabel">Options</h3>
</div>
<div class="modal-body">
<h4>Upload Documents</h4>
<form>
<div class="form-group">
<select data-ng-model="type" class="form-control" id="fileTypeSelect">
<option value="Policy">Policy</option>
<option value="SOP">SOP</option>
</select>
<br>
<div class="input-group"> <span class="input-group-btn">
<input type="file" id="file">
</span>
</div>
<br>
<button type="button" class="btn btn-default" data-ng-click="uploadAttachment($index, type)">Upload</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
And finally my function which resides inside RiskListCtrl (the only controller i use):
$scope.uploadAttachment = function executeUploadAttachment(index, type) {
var listname = "Risk Register";
var id = $scope.items[index].Id;
console.log(indexID);
readFile("uploadControlId").done(function(buffer, fileName) {
uploadAttachment(type, id, listname, fileName, buffer).done(function() {
alert("success");
}).fail(function() {
alert("error in uploading attachment");
})
}).fail(function(err) {
alert("error in reading file content");
});
}
It seems that $scope.items[index].Id is empty. Error: Cannot read property 'Id' of undefined
The modal window has its own scope. That means you need to resolve data you want to pass into the modal's scope. To do so, use resolve within the modals open(options) method.
Before I will give you an example, I want to suggest having only one modal for all your table items. This will let you keep a single template where you can easily use id (now, you create a template for each of your table items which is not valid). Just call a controller function and pass your $index:
<button type="button" class="btn btn-primary uploadBtn" data-ng-show="editMode" ng-click="openModal($index)">Upload file <i class="fa fa-cloud-upload"></i></button>
In your controller, create the modal instance and refer to the template:
$scope.openModal = function(index) {
var modalInstance = $modal.open({
templateUrl: 'myPath/myTemplate.html',
controller: 'MyModalCtrl',
resolve: {
index: function() {
return index;
}
}
});
};
Now you can access index in your MyModalCtrl's scope by injecting index:
angular.module('myModule', []).controller('MyModalCtrl', function($scope, index) {
$scope.index = index;
});
Since, you are getting the index value outside model then you can also use ng-click and then call a function in your controller and store the index value in a temporary variable and then when you are using submit button then just take make another variable and assign the value of temporary variable to your variable. for example:
<button type="button" data-ng-show="editMode" data-ng-click="editMode = false; updateItem($index)" class="btn btn-default">Save</button>
and then make a function in your controller
$scope.updateItem = functon(index)
{
$scope.tempVar = index;
}
now use the value of tempVar in you function
$scope.uploadAttachment = function executeUploadAttachment(index, type) {
var index = tempVar; //assign the value of tempvar to index
var listname = "Risk Register";
var id = $scope.items[index].Id;
console.log(indexID);
readFile("uploadControlId").done(function(buffer, fileName) {
uploadAttachment(type, id, listname, fileName, buffer).done(function() {
alert("success");
}).fail(function() {
alert("error in uploading attachment");
})
}).fail(function(err) {
alert("error in reading file content");
});
}