I'm using bootstrap modal with angularjs along with datepair.
http://jonthornton.github.io/Datepair.js/
I'm working on creating a single page leave request calendar where I can add leave request to the calendar via a modal dialog box. When the box pops up, you pick your date and time you'll be absent from work. I'm experiencing an issue where the datepair doesn't work when nested within the modal dialog template.
The modal dialog works in the following code, but a click doesn't seem to trigger the calendar or time popup like in the demo link provided above. Everything works fine outside of the angularjs / modal template.
var app = angular.module('myModule', ['ui.bootstrap']);
app.controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
$scope.animationsEnabled = true;
$scope.open = function (size) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
});
//Please note that $modalInstance represents a modal window (instance) dependency.
//It is not the same as the $uibModal service used above.
app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
$http.get('./requestType.json')
.success(function (data) {
$scope.requestTypeList = data;
});
// initialize input widgets first
$('#requestForm .time').timepicker({
'showDuration': true,
'timeFormat': 'g:ia'
});
$('#requestForm .date').datepicker({
'format': 'm/d/yyyy',
'autoclose': true
});
// initialize datepair
var requestForm = document.getElementById('requestForm');
var datepair = new Datepair(requestForm);
$scope.ok = function () {
$uibModalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
html
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<form ng-submit="submitForm()" form-autofill-fix name="form">
<p id="requestForm">
<label>Request Type</label>
<select ng-model="formData.requestType" ng-options="rt.name for rt in requestTypeList track by rt.id" required class="form-control">
<option value="">- Select Request Type</option>
</select>
<label>Start Date</label><input type="text" class="form-control date start" ng-model="formData.startDate" required/>
<label>Start Time</label> <input type="text" class="form-control time start" ng-model="formData.startTime" required/>
<label>End Time</label><input type="text" class="form-control time end" ng-model="formData.endTime" required/>
<label>End Date</label><input type="text" class="form-control date end" ng-model="formData.endDate" required/>
<ul>
<li ng-repeat="item in items">
{{ item }}
</li>
</ul>
</p>
<input type="submit" value="Add to Agenda" class="btn btn-success"/>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
</div>
</script>
Now requestTypeList works perfectly to load the select menu, but if I click the date field, nothing pops up. What might be wrong?
I think the problem is that ModalInstanceCtrl is executing before template inserted to the DOM. The first rule of angular: do not use angular do not do DOM Manipulations from controllers, services or anywhere else but from directives.
So instead of doing this
$('#requestForm .time').timepicker({
'showDuration': true,
'timeFormat': 'g:ia'
});
from modal's controller make an directive for this:
(function () {
'use strict';
angular
.module('xp-timepicker', [])
.directive('xpTimepicker', timepicker);
function timepicker() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.timepicker({
showDuration: true,
timeFormat: 'g:ia'
});
}
};
}
}());
// to use your directive in your app include it's module as dependency for your app module
angular
.module('app', ['xp-timepicker']);
Then place this directive on input fields you need:
<input type="text" ng-model="time" xp-timepicker>
I made a working plunker for you.
the z-index of bootstrap modal by default is 1050
the z-index of the date widget seems to be 1000 by default
hence the date widget will be "under" the modal.
you will have to do some css fixes if you want the date widget to be visible above the modal window
Related
I am using angular 1.5.8. After following some resources, I got the file upload working. I had to create a custom directive for that.
Directive
//file-upload-model-directive.js
'use strict';
export default function (app) {
app.directive('fileUploadModel', fileUploadModelDirective);
function fileUploadModelDirective () {
'ngInject';
return {
restrict: 'A',
link: linkFn,
require: 'ngModel'
};
function linkFn (scope, element, attrs, ngModel) {
element.bind('change', function(event){
var files = event.target.files;
var file = files[0];
ngModel.$setViewValue(file);
scope.$apply();
});
}
}
}
I am also using angular's form. And I have a "reset" button on this form. I want to clear all the form fields when clicked. And it happens with all form fields except file.
View
<form ng-submit="dataCtrl.upload(form)" name="form">
<div class="form-group" ng-class="{'has-error' : form.file.$invalid && !form.file.$pristine}">
<label>Select file</label>
<input type="file" name="file" ng-model="dataCtrl.newUpload.csvFile" file-upload-model required/>
</div>
<div class="form-group" ng-class="{'has-error' : form.comment.$invalid && !form.comment.$pristine}">
<label>Comment</label>
<textarea class="form-control" name="comment"
ng-model="dataCtrl.newUpload.comment" required></textarea>
</div>
<div class="form-group pull-right">
<button type="submit" class="btn btn-success" ng-disabled="form.$invalid">Upload</button>
<button class="btn btn-default" ng-click="dataCtrl.reset(form)" ng-disabled="!form.$dirty">Reset</button>
</div>
</form>
And the Controller
'use strict';
function DataController($log, catalogCnst, requestSV, $http) {
'ngInject';
this.reset = function(form) {
this.newUpload = {};
// form.file.$setViewValue(null); // this didn't work either
form.$setPristine()
};
this.upload = function(form) {
// some code
};
}
When "reset" is clicked, I see that
form.file.$pristine is false
form.file.$invalid is false
But I still see filename near the file upload element.
I also tried adding watch and handling event on the element in the directive
scope.$watch(attrs.fileUploadModel, function(value) {
console.log('attrs.file');
});
element.on('$pristine', function() {
console.log('destroy');
});
But they didn't get invoked.
How do I do this? Please guide me.
When you clear newUpload, file input does not get cleared. You need to do this separately.
See JSFiddle:
Basically, I added to the directive scope:
scope: {
model: '=ngModel'
},
... and watch:
scope.$watch('model', function(file) {
if (!file) {
element.val('');
}
});
Instead of using button tags, why not use input tags. This, in theory, might work.
<input type="submit" class="btn btn-success" ng-disabled="form.$invalid" value="Upload">
<input type="reset" class="btn btn-default" ng-click="dataCtrl.reset(form)" ng-disabled="!form.$dirty" value="Reset">
Please run this plunker, if you enter a value in the modal and then click on show value, the value is undefined in $scope, how to get the value?
HTML
<div ng-app="app" ng-controller="myCtl">
<input type="button" ng-click="openModal()" value="Open Modal">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h4 class="modal-title">The Title</h4>
</div>
<form name="myForm" novalidate>
Enter a value <input type="text" ng-model="someField" />
</form>
<div class="modal-footer">
<button ng-click="showValue()">Show value</button>
</div>
</script>
</div>
Javascript
var app = angular.module('app', ['ui.bootstrap']);
app.controller('myCtl', function($scope,$uibModal) {
$scope.openModal = function() {
$scope.modalInstance = $uibModal.open({
animation: false,
templateUrl: 'myModalContent.html',
scope: $scope
});
};
$scope.showValue = function() {
alert('value entered=' + $scope.someField);
};
});
You are seeing this because the scope of the modal is isolated. Even though you pass the $scope to modal. It still does not use the same $scope.
For your example the following would work.
Update the modal template:
<button ng-click="showValue(someField)">Show value</button>
Update your controller showValue method as follows:
$scope.showValue = function(theValue) {
$scope.someField = theValue;
alert('value entered=' + $scope.someField);
};
Really though the best way to use the modal is to use the modal instance created by the open method to track the close and dismiss events and handle the result that way. Take a look at the example on on the ui-modal section of the ui-bootstrap documentation
I am using angular-formly to build a form inside an angular-ui-bootstrap modal, the following code works when the form is placed outside the modal template but it doesn't when placed inside the ng-template, it just doesn't print the fields at all.
I believe this should work but I don't know how the life-cycle of angular-formly runs, so I am unable to identify how to make the fields show up inside my bootstrap modal template.
The issue is clearly related to the ng-template, it appears not to render the form even if the fields array is passed correctly.
var app = angular.module("demo", ['dndLists', 'ui.bootstrap', 'formly', 'formlyBootstrap']);
app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items, User) {
var vm = this;
vm.loadingData = User.getUserData().then(function(result) {
vm.model = result[0];
vm.fields = result[1];
vm.originalFields = angular.copy(vm.fields);
console.log(vm);
});
});
app.controller("AdvancedDemoController", function($scope, $uibModal){
$scope.modalOpen = function(event, index, item){
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: 'md',
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
In my view:
<!-- Template for a modal -->
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<div ng-if="vm.loadingData.$$state.status === 0" style="margin:20px 0;font-size:2em">
<strong>Loading...</strong>
</div>
<div ng-if="vm.loadingData.$$status.state !== 0">
<form ng-submit="vm.onSubmit()" novalidate>
<formly-form model="vm.model" fields="vm.fields" form="vm.form">
<button type="submit" class="btn btn-primary submit-button">Submit</button>
</formly-form>
</form>
</div>
<ul>
<li ng-repeat="item in items">
{{ item }}
</li>
</ul>
Selected: <b>{{ selected.item }}</b>
</div>
</script>
Any ideas?
When calling $uibModal.open you need to specify controllerAs: 'vm' (that's what you're template assumes the controller is defined as).
I have a bootstrap modal. On pressing close button, the value of the array is getting changed but it shouldn't.
controller.js
$scope.open = function(){
var modalInstance = $modal.open({
animation: true,
templateUrl: 'views/view1.html',
controller: 'controller2',
resolve: {
items: function(){
return $scope.array;
}
}
});
modalInstance.result.then(function (changed_array){
$scope.array = changed_array;
},function(){
// no change
});
};
code for second controller
angular.module('storagewebApp').controller('controller2',function($scope, $modalInstance, items) {
$scope.array = items;
$scope.ok = function(){
$modalInstance.close($scope.array);
};
$scope.cancel = function(){
$modalInstance.dismiss('cancel');
};
});
view2.html
<div class="modal-header">
<h4 class="modal-title">Set threshold</h4>
</div>
<div class="modal-body">
<div class="form-group" align="left">
<div> E:</div> <input type="text" ng-model="array[0]">
<div> M:</div><input type="text" ng-model="array[1]">
<div>T:</div><input type="text" ng-model="array[2]">
<div>F: </div><input type="text" ng-model="array[3]">
<div> I:</div><input type="text" ng-model="array[4]">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" ng-click="cancel()">Close</button>
<button type="button" class="btn btn-primary" ng-click="ok()">Save</button>
</div>
</div>
The values are changed via input box but on pressing close button values should not be sent to the first controller, but on clicking close button value of changed array is passed to first controller.
This behaviour is a common AngularJS/JavaScript mistake. When you instantiate your modal controller you are passing a reference of your array. Then inside of your modal controller you manipulate that reference, even if you don't pass it back.
When you write:
$scope.array = items
What happends in memory is that $scope.array points to the same location as items. When you modify in any way $scope.array's object you are modifying items also.
As a solution you need to deep copy your initial array into the new one, in this way creating a new object and reference. AngularJS has an inbuilt function that does this: https://docs.angularjs.org/api/ng/function/angular.copy
angular.copy
See this plunkr for example: http://plnkr.co/edit/W6EYUwQ1K1YAnfnJ2r4a?p=preview
You should create a new scope for the modal window. Like this:
var modalScope = angular.extend(
$scope.$new(), {
val1ToPassToModal: $scope.originalValue1,
val2ToPassToModal: $scope.originalValue2,
});
$modal.open({
templateUrl: '…',
controller: '…',
scope: modalScope,
resolve: {
…
}
});
Of course if you don't want to pass new values to the modal window, you can write just:
scope: $scope.$new().
I am encountering a strange behavior when using Angular's ng-repeat on a Bootstrap UI modal.
I have this dummy method in my customerService.js factory:
app.factory('customerService', [ function() {
customerFactory.getCustomers =
function () {
return [{ "name": "Terry Tibbs" },
{ "name": "Bobby Halls" },
{ "name": "Garry Brisket" }]
};
return customerFactory;
}]);
This the customerSelectionModal.html modal template:
<div>
<div class="modal-header">
<h3 class="modal-title">Select a customer</h3>
</div>
<div class="modal-body">
<label data-ng-repeat="cust in customers">
<input name="customer" type="radio" value="{{cust}}" ng-model="$parent.selected.item" />{{cust.name}}
</label>
<div ng-show="selected">You have selected: {{ selected }}</div>
<div ng-show="selected">name: {{ selected.item.name }}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
This is the customerController.js file:
'use strict';
appModule.controller('customerController',
['$scope', '$modal', '$log', 'customerService', function ($scope, $modal, $log, customerService) {
$scope.customers = customerService.getCustomers();
$scope.selectCustomer = function () {
var modalInstance = $modal.open({
templateUrl: paths.templates + 'customerSelectionModal.html',
controller: 'modalInstanceController',
resolve: {
customers: function () {
return $scope.customers;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.customerName = selectedItem.name;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
}]);
Finally the modalInstanceController.js controller for the modal form:
app.controller('modalInstanceController',
function ($scope, $modalInstance, customers) {
$scope.customers = customers;
$scope.selected = {
item: $scope.customers[0]
};
$scope.ok = function () {
alert($scope.selected.item.name);
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
When the modal dialog is displayed initially I get You have selected: {"item":{"name":"Terry Tibbs"}} displayed correctly as this is the default customer
selected by the modalInstanceController controller. However, when I select a customer I get You have selected: {"item":"{\"name\":\"Terry Tibbs\"}"} and clicking the OK button just displays undefined in the alert
window and I don't know why.
The only possible clue is when a customer is selected the name property and it's value are escaped using a \ for some odd reason that I haven't been able to figure out.
Has anyone any clue as to what's going on here?
Lastly, is it possible to set the radio button to the selected customer as it doesn't put a selection against the customer?
I am using the following:
AngularJS v1.3.9
Twitter Bootstrap v3.3.1
Angular UI Bootstrap v0.12.0
Anyway, in the radio button, try to use ng-value instead value attribute.