$modal is not open for the second time in Angularjs - angularjs

$modal is called in the $ngonint hook. First time when controller loads, it works perfectly. But for the second time, it is not showing up when the controller loads.
Expectation:
Consider two controllers first and second.
Navigate to second controller from first controller by clicking link.
We need to show below modal when page loads whenever navigate to second controller page.
Actual Result:
For the first time, when it navigate to second controller, it showed the modal as expected. once go back to first page and come again to second controller, modal is not showed up.
Can anyone help me to solve this issue ? Thanks.
Below are the code sample:
Controller
$onInit() {this.showModal();} // Need to show modal when page loads
public showModal= function () {
var modalInstance = $modal.open({
template: ModalTemplate,
controller: [
'$scope',
'$modalInstance',
function ($scope, $modalInstance) {
$scope.answer = '';
$scope.close = function () {
$modalInstance.dismiss();
};
$scope.next = function () {
$modalInstance.close($scope.answer);
};
},
],
windowClass: 'open-account'
});
modalInstance.result.then((response) => {
console.log(response);
})
}
ModalTemplate.html
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-hidden="true">
<fa-icon icon="times" prefix="fal" class="close-fa fa-lg"></fa-icon>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-4">
<div class="form-group radio-group-label">
<label class="control-label">
Click
</label>
</div>
</div>
<div class="col-xs-2">
<div class="form-group">
<label class="control-label" for="answer-yes">
<input type="radio" name="answer" ng-model="answer" value="yes"
id="answer-yes" />
Yes
</label>
</div>
</div>
<div class="col-xs-2">
<div class="form-group">
<label class="control-label" for="answer-no">
<input type="radio" name="answer" ng-model="answer" value="no"
id="answer-no" />
No </label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-success" ng-disabled="!answer" ng-click="next()">Next</button>
</div>

Related

angularjs form inside bootstrap modal popup

I have a form inside a bootstrap modal.But I can't able to get the values of form on controller side with scope variable.
https://plnkr.co/edit/FjKXUpoBDdvQqomI97ml?p=preview
My original code has another problem also. when I click on submit button it will refresh the whole page and submit function is not executing.
My form:
<div class="modal-body">
<form class="form-horizontal" name="contact" ng-submit="contactForm()">
<div class="form-group">
<label class="col-lg-3 control-label">Name* :</label>
<div class="col-lg-8">
<input class="form-control" type="text" id="name" name="_name" ng-model="_name" > </div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">Email* :</label>
<div class="col-lg-8">
<input class="form-control" type="email" id="email" name="_email" ng-model="_email" required> </div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">Mobile* :</label>
<div class="col-lg-8 row">
<div class="col-lg-4">
<input type="text" class="form-control" ng-model="_cc" placeholder="+91" name="_cc"> </div>
<div class="col-lg-8">
<input class="form-control" type="text" ng-model="_mobile" maxlength="10" ng-pattern="/^[0-9]{5,10}$/" id="mobile" name="_mobile"></div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">Message :</label>
<div class="col-lg-8">
<textarea class="form-control" rows="2" name="_condition" ng-model="_condition"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-7 col-lg-5">
<button type="submit" class="btn btn-primary" id="contactSubmit" >Submit</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div></div>
</form>
</div>
Controller:
$scope.contactForm = function(){
console.log($scope._condition,$scope._name,$scope._email,$scope._cc,$scope._mobile);
};
You are opening the modal using plain jQuery approach which is not going to work in Angular, because opened modal is not connected to Angular application, so it doesn't know that modal has to be handled, HTML parsed, etc.
Instead you should use directives properly, or in case of modal dialog you can simply use existent ones, like Angular UI project, which brings ready Bootstrap directives for Angular. In your case you need $modal service and inject the $http service to have your data posted.
Here is the working plunker using angular-ui bootstrap.
PLUNKER:https://plnkr.co/edit/rjtHJl0udyE0PTMQJn6p?p=preview
<html ng-app="plunker">
<head>
<script src="https://code.angularjs.org/1.2.18/angular.js"></script>
<script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
<script src="script.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3>I'm a modal!</h3>
</div>
<form ng-submit="submit()">
<div class="modal-body">
<label>Email address:</label>
<input type="email" ng-model="user.email" />
<label>Password</label>
<input type="password" ng-model="user.password" />
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<input type="submit" class="btn primary-btn" value="Submit" />
</div>
</form>
</script>
<button class="btn" ng-click="open()">Open Modal</button>
</div>
</body>
</html>
JS:
angular.module('plunker', ['ui.bootstrap']);
var ModalDemoCtrl = function ($scope, $modal, $log,$http) {
$scope.user = {
email: '',
password: null,
};
$scope.open = function () {
$modal.open({
templateUrl: 'myModalContent.html', // loads the template
backdrop: true, // setting backdrop allows us to close the modal window on clicking outside the modal window
windowClass: 'modal', // windowClass - additional CSS class(es) to be added to a modal window template
controller: function ($scope, $modalInstance, $log, user) {
$scope.user = user;
$scope.submit = function () {
$log.log('Submiting user info.'); // kinda console logs this statement
$log.log(user);
$http({
method: 'POST',
url: 'https://mytesturl.com/apihit',
headers: {
"Content-type": undefined
}
, data: user
}).then(function (response) {
console.log(response);
$modalInstance.dismiss('cancel');
}, function (response) {
console.log('i am in error');
$modalInstance.dismiss('cancel');
});
//$modalInstance.dismiss('cancel'); // dismiss(reason) - a method that can be used to dismiss a modal, passing a reason
}
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
},
resolve: {
user: function () {
return $scope.user;
}
}
});//end of modal.open
}; // end of scope.open function
};

Angular validation directive not working properly

I am using custom angular directive for showing validation. Directive code is as below
angularFormsApp.directive('showErrors',['$timeout', function ($timeout) {
return {
restrict: 'A',
require: '^form',
link: function (scope, el, attrs, formCtrl) {
// find the text box element, which has the 'name' attribute
var inputEl = el[0].querySelector("[name]");
// convert the native text box element to an angular element
var inputNgEl = angular.element(inputEl);
// get the name on the text box so we know the property to check
// on the form controller
var inputName = inputNgEl.attr('name');
var helpText = angular.element(el[0].querySelector(".help-block"));
// only apply the has-error class after the user leaves the text box
inputNgEl.bind('blur', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
helpText.toggleClass('hide', formCtrl[inputName].$valid);
});
scope.$on('show-errors-event', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
});
scope.$on('hide-errors-event', function () {
$timeout(function () {
el.removeClass('has-error');
}, 0, false);
});
}
}
}]);
and Html is as below
<div class="container" id="login-form">
<img src="assets/img/logo-big.png">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h2>Login Form</h2>
</div>
<div class="panel-body">
<form name="loginForm" class="form-horizontal" novalidate>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12">
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-user"></i>
</span>
<input type="text" class="form-control" placeholder="Username" autocomplete="Off" ng-required="true" name="username" autofocus ng-model="loginUser.username">
</div>
<span class="help-block" ng-if="loginForm.username.$error.required">Username is required</span>
</div>
</div>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12" >
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-key"></i>
</span>
<input type="password" class="form-control" placeholder="Password"
name="password"
ng-model="loginUser.password" autocomplete="Off"
ng-required="true">
</div>
<span class="help-block" ng-if="loginForm.password.$error.required">Password is required</span>
</div>
</div>
<div class="form-group mb-n">
<div class="col-xs-12">
Forgot password?
<div class="checkbox-inline icheck pull-right p-n">
<label for="">
<input type="checkbox"></input>
Remember me
</label>
</div>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<div class="clearfix">
Register
Login
</div>
</div>
</div>
</div>
</div>
</div>
Controller code :
var loginController = function ($scope, $window, $routeParams, $uibModal, $location, $filter, $rootScope, DataService, SharedProperties) {
$rootScope.bodylayout = 'focused-form animated-content';
$scope.loginUser = {
username: "",
password:""
}
$scope.load = function () {
//getAppointmentInfo();
};
$scope.SubmitLoginForm = function () {
$scope.$broadcast('show-errors-event');
if ($scope.loginForm.$invalid)
return;
}
}
angularFormsApp.controller("loginController", ["$scope", "$window", "$routeParams", "$uibModal", "$location", "$filter", "$rootScope", "DataService", "SharedProperties", loginController]);
Now When I open form below input control validation span is displaying by default . When I click on Login button then its showing in red and working fine.
problem is it shouldn't show by default when Page is opend.. Please see image below
Instead of this
loginForm.password.$error.required
try this
(loginForm.$submitted || loginForm.username.$dirty) && loginForm.password.$error.required
Take a look at the ng-messages directive. Its fairly elegant. Example:
<form name="myForm">
<input type="text" ng-model="field" name="myField" required minlength="5" />
<div ng-messages="myForm.myField.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message="minlength">The value entered is too short</div>
</div>
</form>
You can then combine it with any form validation. Just place the error messages from the validators onto the elements $error object and they are automatically rendered in your UI.
I ended up here as part of my search for an issue. In my case I was using a directive with a changing minimum value like this:
ngOnChanges(changes: SimpleChanges) {
if (changes.minDate && this.control) {
this.control.updateValueAndValidity({ onlySelf: true });
}
}
This means that the form will not be updated, I removed onlySelf and it worked correctly.
this.control.updateValueAndValidity();
Just leaving this as a breadcrumb in case someone else does something similar.

what is happening to my angular scope?

I am using angularstrap modal service to open a login modal on any page when login is required. I open one like this:
var scope = {'foo': 'bar'};
var myOtherModal = $modal({scope: scope, template: 'modal/login.html', show: false});
the login.html contains the modal markup but it also has a controller bound to it:
<div ng-controller="SignInController" class="modal" tabindex="-1" role="dialog">
<input ng-modal="foo"/>
In the controller code, how do I get access to the foo prop on the scope that I am passing in?
What is happening to my scope? Is a scope object created by $modal the one and the same that is used by the controller? It appears that its not the case.
What is the best way to solve this problem? (Ability to open a login dialog from anywhere and have control over its scope from the controller)
Thanks
Think of opening a modal as a function call... where you pass data in and get data back. It's not the ONLY way to approach it but I think it's a clean way to approach it.
I generally follow this pattern, giving the modal it's own controller & passing data in & getting data back by passing it into the promise:
var ModalController = function($scope, $modalInstance, input) {
$scope.input = input;
var output = {
username: "",
password: ""
};
$scope.ok = function () {
$modalInstance.close($scope.output);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
$scope.openModal = function(data) {
var modalInstance = $modal.open({
templateUrl: 'popupDialog.tpl.html',
controller: ['$scope', '$modalInstance', 'input', ModalController],
resolve: {
input: function() {
return data;
}
}
});
modalInstance.result.then(function(output) {
// TODO: do something with the output.username & output.password...
// call Login Service, etc.
});
};
EDIT: Adding popup html...
<form class="form-horizontal">
<div class="modal-header">
<h3>Please Log In</h3>
</div>
<div class="modal-body">
<form name="form" class="form-horizontal">
<div class="row">
<label class="col-sm-3 text-info control-label" for="inputUsername">Username</label>
<input class="col-sm-8 form-control input-sm" type="text" id="inputUsername" name="inputUsername" ng-model="output.username" />
</div>
<div class="row">
<label class="col-sm-3 text-info control-label" for="inputPassword">Password</label>
<input class="col-sm-8 form-control input-sm" type="text" id="inputPassword" name="inputPassword" ng-model="output.password" />
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-primary" type="submit" ng-click="ok()">Ok</button>
<button class="btn btn-sm btn-warning" ng-click="cancel()">Cancel</button>
</div>
</form>

One model not accessible, one is

I have two tiny projects, both use essentially the same setup, one returns a model value one returns the model value as undefined. At this point I am lost as to why these two examples are behaving differently. I checked the failing example, and the model correctly populates
Successful Behavior
Partial
<div class="row">
<div class="col-md-4 .col-md-offset-4 content-pane no-margin">
<h4>Input Your Inquiry</h4>
<form ng-submit="submit(invoice)">
<div class="control-group">
<div class="controls">
<div class="input-prepend">
<span class="add-on">Invoice Number</span>
<input id="who" type="text" class="input-xlarge" ng-model="invoice.number">
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-prepend">
<span class="add-on">PO Number</span>
<input id="who" type="text" class="input-xlarge" ng-model="invoice.po">
</div>
</div>
</div>
<input type="submit">
</form>
</div>
<div ng-if="status" class="col-md-4 .col-md-offset-4 content-pane no-margin">
<h4>Invoice Status</h4>
<div>{{status}}</div>
</div>
</div>
App.js
app.config(function($routeProvider){
$routeProvider
.when('/',
{
templateUrl: 'app/partials/inquiry-input-form.html',
controller: 'MainCtrl'
})
.otherwise(
{
templateUrl: 'app/partials/404.html'
})
});
app.controller('MainCtrl', function($scope,formsResults) {
$scope.submit = function() {
var invoiceNum = $scope.invoice.number;
var po = $scope.invoice.po;
formsResults.getInvoiceStatus(po,invoiceNum).success(function(data) {
var status = data.records;
if (angular.isUndefined(status)) {
$scope.status = 'Pending Reciept or Entry';
} else {
$scope.status = status[0].field_124208[0];
}
});
};
});
app.factory('formsResults', function($http) {............
Returning Model as undefined in controller
Partial
<tabset>
<tab active=workspace.active>
<tab-heading>
Search All Fields
</tab-heading>
<div>
<br /><br />
<form class="form-horizontal" ng-submit="submitAll(searches)">
<div class="form-group">
<div class="col-md-4">
<input id="textinput" name="textinput" type="text" class="form-control input-md" ng-model="searches.term">
<span class="help-block">Enter the term you wish to search on.</span>
{{searches.term}}
</div>
</div>
<!-- Button -->
<div class="form-group">
<div class="col-md-4">
<button id="singlebutton" name="singlebutton" class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
</div>
</tab>
<tab>
<tab-heading>
Search By Field
</tab-heading>
<div>
<br /><br />
<form class="form-horizontal" ng-submit="submitSpecific(search)">
<div class="form-group">
<div class="col-md-4">
<select id="selectbasic" name="selectbasic" class="form-control">
<option value="4">Country</option>
</select>
<span class="help-block">Enter the field you wish to search on.</span>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<div class="col-md-4">
<input id="textinput" name="textinput" type="text" class="form-control input-md">
<span class="help-block">Enter the term you wish to search on.</span>
</div>
</div>
<div class="form-group">
<div class="col-md-4">
<button id="singlebutton" name="singlebutton" class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
</div>
</tab>
</tabset>
App.js
var app = angular.module ('app',['ngRoute','ngSanitize','ui.bootstrap','dialogs.controllers','dialogs']);
app.config(function($routeProvider){
//http://docs.angularjs.org/tutorial/step_07
$routeProvider
.when('/',
{
templateUrl: 'app/partials/home_tpl.html',
controller: 'HomeCtrl'
})
.when('/search',
{
templateUrl: 'app/partials/search-form.html',
controller: 'SearchCtrl'
})
.when('/add-sop',
{
templateUrl: 'app/partials/support-process.html',
controller: 'EditSOPCtrl'
})
.otherwise(
{
redirectTo: '/'
})
});
app.controller('HomeCtrl', function($scope) {
});
app.controller('SearchCtrl', function($scope,formsSopSearchCriteria) {
$scope.submitAll = function() {
var searchTerm = $scope.searches.term;
console.log(searchTerm);
var searchPackage={
"offset":0,
"limit":0,
"fields":["record_id","dt_created","created_by","dt_updated","updated_by",149654,149655,149692],
"filters":{"and":[{"field_id":"149655","operator":"contains","value":searchTerm}]},
"sort":{}
};
searchPackage=JSON.stringify(searchPackage);
$scope.searchPackage = searchPackage;
formsSopSearchCriteria.getMatches(searchPackage).success(function(data) {
$scope.sopRecords = data.records;
});
};
$scope.submitSpecific = function() {
var searchPackage = ""
searchPackage=JSON.stringify(searchPackage);
$scope.searchPackage = searchPackage;
formsSopSearchCriteria.getMatches(searchPackage).success(function(data) {
$scope.sopRecords = data.records;
});
};
});
app.factory('formsSopSearchCriteria',function($http) {........
Any ideas? My understanding is that it as an issue of scope. But in the failing example, the partial I provide is mapped to the SearchCtrl in the routeProvider. As a result the model should be available in the controller, just like the example that is working. I suspect I must be missing something, but I am not clear what I am missing.
You need to create $scope.searches as an empty object on initialization:
app.controller('SearchCtrl', function($scope,formsSopSearchCriteria) {
$scope.searches = {};
Or for clarity:
$scope.searches =
{
term: ''
};
If you don't, it will not be created until the first input is entered (I believe it is the $parse service that the ng-model directive uses that creates the searches object in this case).
The problem is that the object might not be created in the scope you want it to.
The reason it works in the first case is because there are no child scopes, so the invoice object is created in the controller scope.
In the second case there is one or many child scopes (probably from the tabs, I haven't dug any deeper into it), so the searches object is created in one of those instead.
Due to how prototypical inheritance works in Javascript, if you predefine the objects in the controllers' scopes, the properties will be created correctly.

Angular repeat bind to independent objects

I have a small Angular application which repeats a list of project objects and allows editing each individual object.
// app.html
<div ng-controller="ProjectsCtrl">
<div ng-repeat="project in projects">
<!-- EDIT -->
<h3>{{ project.id }}</h3>
<h3>{{ project.title }}</h3>
...
<!-- END EDIT -->
<p>
<button class="btn btn-info" ng-click="updateProject(project); showEditProject=true">Edit Project</button>
<button class="btn btn-info" ng-click="showEditProject=false">Cancel</button>
...
</p>
<div class="box row animate-show-hide" ng-show="showEditProject">
<form name="editProjectForm" class="form-horizontal">
<input ng-model="editProject.title" type="text" id="title" name="title" class="form-control" placeholder="Title" required /><br />
...
</form>
</div>
</div>
</div>
// app.js
projectsApp.controller('ProjectsCtrl', function ($scope, $http, ConcernService) {
...
$scope.updateProject = function(obj) {
ConcernService.list('projects/', obj).then(function(){
$scope.editProject = obj;
});
};
This all works fine but as each object is passed on the ng_clickall the editProject objects are bound to the same model. Is it possible to allow the form to open and each object is bound to each project independently?
Your updateProject method in the parent scope is just assigning project to editProject. You can just reference poject in the edit form. There is no need for the editProject field.
// app.html
<div ng-controller="ProjectsCtrl">
<div ng-repeat="project in projects">
<p>
<button class="btn btn-info" ng-click="updateProject(project); showEditProject=true">Edit Project</button>
<button class="btn btn-info" ng-click="showEditProject=false">Cancel</button>
...
</p>
<div class="box row animate-show-hide" ng-show="showEditProject">
<form name="editProjectForm" class="form-horizontal">
<input ng-model="project.title" type="text" id="title" name="title" class="form-control" placeholder="Title" required /><br />
...
</form>
</div>
</div>
</div>
// app.js
projectsApp.controller('ProjectsCtrl', function ($scope, $http, ConcernService) {
...
$scope.updateProject = function(obj) {
ConcernService.list('projects/', obj).then(function(){
$scope.editProject = obj;
});
};
Put the updateProject function in another controller and attach that controller to the div that has the ng-repeat. For example:
projectsApp.controller('ProjectCtrl', funciton($scope, $http, ConcernSerice) {
$scope.updateProject = function(obj) {
ConcernService.list('projects/', obj).then(function(){
$scope.editProject = obj;
});
};
}
and then:
<div ng-repeat="project in projects" ng-controller="ProjectCtrl">
...
</div>
The ng-repeat creates a scope for each project. This will makes it so that when you do $scope.editProject = obj; this will be done on this scope rather than on the single parent scope created by your ProjectsCtrl.
UPDATE:
If you need to be able to reset any edits you made you will need to save a copy of your object. You can probably do this in your updateProject:
$scope.updateProject = function(obj) {
ConcernService.list('projects/', obj).then(function(){
$scope.previousValue = angular.copy(obj);
$scope.editProject = obj;
});
};
Then you would have to create a method for canceling that copies the values back:
$scope.cancel = function(){
$scope.showEditProject = false;
angular.copy($scope.previousValue, $scope.editProject);
};
Then call it from your template:
<button class="btn btn-info" ng-click="cancel()">Cancel</button>

Resources