AngularJS: ng-class not refreshing when Bootstrap modal re-opens - angularjs

I have a Bootstrap modal with a form inside a div set with ng-class:
<div id="modalNewOrder" class="modal fade" data-modalName="order" role="dialog" ng-controller="NewOrderController as NewOrderCtrl">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close pull-left" data-dismiss="modal">X</button>
<h4 class="modal-title">
New order
</h4>
</div>
<div class="modal-body">
<div ng-class="NewOrderCtrl.position">
<form>
// input text here
</form>
When the modal is loaded, its NewOrder.position sets its css to center. When the form is processed, NewOrder.position is updated to top, and the results of are shown below it.
Now the problem: I want NewOrder.position to be reset to center when the modal is re-opened. However, even if I reset NewOrder.position, it seems the ng-class is not being updated. How do I do that?
I'm using this on my controller, but its not working to update ng-class:
$("#modalNewOrder").on("hidden.bs.modal", function () {
$(this).find('form')[0].reset();
self.clearQuery();
self.position = "mycss-position-center";
});
***** UPDATE *****
Following Naren's suggestion and experimenting a little, I've updated my code. In my html I have:
<div ng-class="{'mycss-position-top' : NewOrderCtrl.top, 'mycss-position-center' : !NewOrderCtrl.top}">
and
<form ng-submit="processing()">
And in my controller, I have:
var self = this;
self.top = false;
$("#modalNewOrder").on('shown.bs.modal', function() {
self.top = false;
});
$("#modalNewOrder").on("hidden.bs.modal", function () {
self.top = false;
});
self.processing = function(){
self.top = true;
};
The problem now is that in the first re-opening of the modal, self.top is evaluated as "true", even if the "on show" function is processed and show "false" in console.log. Only if I close the modal again and re-open it once more, then it goes do false.
* UPDATE *
The proposed solution was working. I've found a syntax error in another place which was causing this last wrong behavior.

Final Answer:
Please don't use JQuery plugins in angular, angular does not detect the functions or variable changes. If you google the plugin name with angular (angular bootstrap) you will run into a library that enables you to use Bootstraps functionality with angular itself (angular UI Bootstrap). This is the proper way of writing code, If you use JQuery function, even if you solve and issue like this, you will run into another issue (because angular cannot detected the changes completely), Please do some research for a angular plugin before going with the Pure JQuery plugin approach.
JSFiddle Demo
Old Answer:
Why not always center the form as default and on form submit update the corresponding class.
HTML:
<div id="modalNewOrder" class="modal fade" data-modalName="order" role="dialog" ng-controller="NewOrderController as NewOrderCtrl">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close pull-left" data-dismiss="modal">X</button>
<h4 class="modal-title">
New order
</h4>
</div>
<div class="modal-body">
<div class="mycss-position-center" ng-class="{'mycss-position-top' : process}">
<form ng-submit="processing()">
// input text here
</form>
JS:
app.controller('NewOrderController', function($scope) {
$scope.process = false;
$scope.processing = function(){
$scope.process = true;
};
});

I thinks your ng-class is not initialisation when you call boostrap modal.
You can use watch syntax like that:
$scope.$watch(' NewOrder.position', function () {
$scope.NewOrder.position = "mycss-position-center";
}, true);
Some thing like that..

Related

Angularjs ng-show is not working inside the modal

Im opening modal popup on click of one button. The modal popup is opening and i want to show one progress bar inside the modal popup. After getting the response i want to hide it.
The progress bar is not showing for the fisrt time if im closing and opening the popup again then it's showing.
Below is my controller code :
$scope.clickUpload = function(){
$('#import_poi_modal').modal('show');
setTimeout(function(){
$scope.fileChangeProgress = true;
console.log($scope.fileChangeProgress)
},1000);
}
HTML:
<div class="modal fade" id="import_poi_modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="center-content" ng-show="fileChangeProgress"> //here im using the variable to show and hide
<div class="col-6">
<div class="progress m-t-30 m-b-30" >
<div class="progress-bar progress-bar-orange active progress-bar-striped" style="width: 100%; height:14px;" role="progressbar"> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
I have tried with setTimeout. Still it's not working.
Since setTimeout is a javascript function(not an Angular one), you will need to use $apply to notify Angular for the changes.
setTimeout(function(){
$scope.$apply(
function() {
$scope.fileChangeProgress = true;
console.log($scope.fileChangeProgress);
}
);
},1000);
Another solution is to use the Angular native $timeout support:
$timeout(function(){
$scope.fileChangeProgress = true;
console.log($scope.fileChangeProgress)
},1000);
Should serve you the same purpose without $apply.
You can also refer to this $apply in setTimeout

how to update all instances of the same controller

I know how to share data between controllers using service but this case is different so please rethink the question.
I have something like this for the UI:
<jsp:include page="../home/Header.jsp" />
<div data-ng-view></div>
<jsp:include page="../home/Footer.jsp" />
Inside the ng-view, I instantiated a controller instance using "data-ng-controller="BuildController as ctrl". It will run this function that might take up to 2 hours. After it's completed, the buildCompletionMsg is updated and pop up a box saying it's completed.
self.buildServers = function(servers, version) {
BuildService.buildList(servers, version).then(
function(data) {
self.buildCompletionMsg = data;
$('#confirmationModal').modal('show');
},
function(errResponse) {
console.error("Error getting servers." + errResponse);
}
);
};
The problem is that I want the modal to be in the Header.jsp file so doesn't matter which view the user is in, they would see the notification. Therefore in Header.jsp I have another controller instance using "data-ng-controller="BuildController as ctrl" and bind it using
<div data-ng-controller="BuildController as ctrl">
<div class="modal fade" id="confirmationModal" role="dialog" aria-labelledby="confirmLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-body">
<h3>{{ ctrl.buildCompletionMsg }}</h3>
</div>
</div>
</div>
</div>
</div>
As you can see, even if I do something like:
self.buildCompletionMsg = BuildService.getCompletionMsg();
it would only update the ctrl instance of the ng-view page, and the one inside Header.jsp is still null.
How can I update all the instances of BuildController in different pages or just update the one in the Header.jsp file?
I found the answer to my own question. The solution is to to have an object reference or array in the service (it does not work for simple string) like this:
angular.module('buildModule').factory('BuildService', ['$http', '$q', function($http, $q) {
var self = {};
self.completionStatus = { data: "" };
then upon $http success update the completionStatus
self.status.data = response.data;
And in the controller, the variable is set directly to this object
self.buildCompletionMsg = BuildService.completionStatus;
This updates the variable {{ buildCompletionMsg }} on all the pages.

How to show preloader inside button instead text Angular JS?

I use Angular JS library ngActivityIndicator.
I tried to show preloader inside element div instead text when button is pushed:
<div ng-activity-indicator="CircledDark">
<div ng-click="Add();" class="btn shareBtn">
<div ng-view></div>
<span>Text</span>
</div>
</div>
I posted <div ng-view></div> inside and have added directive ng-activity-indicator="CircledDark" to parent element.
When I click button, I call function that display preloader:
$activityIndicator.startAnimating();
But preloader is created outside of button:
<div ng-show="AILoading" class="ai-circled ai-indicator ai-dark-spin"></div>
This is official documantation, from here I have taken example:
https://github.com/voronianski/ngActivityIndicator#directive
How to show preloader inside button instead text Angular JS?
I think the only solution is to separate your ng-view and button, at least they do something similar in their sample page. So, in your markup you could do something like this:
<div ng-click="add()" class="btn btn-default" ng-activity-indicator="CircledDark">
<span ng-show="!AILoading">Add</span>
</div>
<div ng-view ng-show="!AILoading">{{ delayedText }}</div>
And in your controller:
$scope.delayedText = "";
$scope.add = function() {
$activityIndicator.startAnimating();
$timeout(function() {
$scope.delayedText = "Here we are!";
$activityIndicator.stopAnimating();
}, 3000);
};
Here is full Demo, you can play with CSS a little so the size of the button won't change when text is replaced by icon.

Type error : Cannot read property 'childNodes' of undefined

I am trying to display a modal when the view is loaded.
Code :
//View
<div id="showCom" ng-controller="showmodalCtrl" ng-init="init()">
<div class="modal fade" id="companyModal" role="dialog">
<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">Please Select Company</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label class="control-label">Recipient:</label>
<input type="text" class="form-control">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
//Controller
.controller('showmodalCtrl', ['$scope', function($scope){
$scope.init = function(){
$('#companyModal').modal("show");
}
}])
Everything works fine and the modal is displayed but I get this error on the console :
Type error : Cannot read property 'childNodes' of undefined
I have no idea why this is happening.
Also if I remove the "form" from the modal , i don't get any error. So I guess the problem is with the form but I am not able to resolve it.
Thanks in advance.
I had the same problem and I managed to resolve it by wrapping the modal open function inside a $timeout() or a normal setTimeout() function.
setTimeout(function(){
jQuery('#messageModal').modal('show');
}, 0);
I had the same error message when trying to use a JQuery plugin. Running it after document ready resolved the issue for me. The plugin was running fine but the angular code that followed in the current scope was not.
It was the "Set timeout"-answer in this thread that led me to try this solution.
Solution for me:
angular.element(document).ready(function () {
//Angular breaks if this is done earlier than document ready.
setupSliderPlugin();
});
As another solution in AngularJS context I'm using this approach:
Include $timeout module in your JS controller
Run init() function with all initializers like this:
$timeout(init, 0);
Works good.
Sorry for edit. Is your HTML well formed? Check that all the tags match.
In your code snippet, it looks like you're missing a closing div. check that - the one that closes the controller div.
I had a similar problem. It would throw an exception when i tried to add some html to dom and compile it using $compile in angularJs.
Inside of the html was another directive that tried to add some dom element from the page to somewhere else in the page.
I just had to call .clone of that element before i moved it to the new location.
And the exception was gone.
$popupContent = $(attrs.popupSelector).clone();
$container = $('<div class="popup-container"></div>');
$container.append($popupContent);
$container.hide();
$('body').append($container);

Using AngularJS, how do I set $scope properties in the controller of the parent page

Thanks for looking.
I have the following markup for a modal which shares the same angular controller as it's parent page:
<!-- START Add Event Video -->
<script type="text/ng-template" id="EventVideo.html">
<div class="event-modal">
<div class="modal-header"><h3>Event Video</h3></div>
<div class="modal-body">
<p>Please enter the URL of either a <strong>YouTube</strong> or <strong>Vimeo</strong> video.</p>
<span ng-if="!Event.VideoUrlIsValid" style='color:#9f9f9f;'>This doesn't look like a valid YouTube or Vimeo Url. Your video may not work.</span>
<div class="row" ng-controller="EventCreateController">
<div pr-form-input span="12" name="videoUrl" ng-model="Event.Item.VideoUrl" placeholder="YouTube or Vimeo URL" isRequired="false" no-asterisk></div>
</div>
</div>
<div class="modal-footer"><button class="btn btn-primary" ng-click="Event.UI.EventVideoModal.Close()">Done</button></div>
</div>
</script>
<!-- END Add Event Video -->
And here is the relevant JavaScript:
EventVideoModal: {
Open: function () {
$scope.EventVideoModal = $modal.open({
templateUrl: 'EventVideo.html',
controller: 'EventCreateController',
scope: $scope
});
},
Close: function () {
$scope.EventVideoModal.close();
}
}
Please note the Event.Item.VideoUrl model reference.
The modal allows a user to set the URL of a video, and the goal is to have that set $scope.Event.Item.VideoUrl in the controller and then close the modal. The parent page and the modal both share the same controller, so I had hoped that this would work.
The modal behavior is fine (opens and closes as it should), but the $scope.Event.Item.VideoUrl property is not getting set.
Any advice is appreciated.
Problem Solved!
Thanks to Bogdan Savluk, I realized that I had a scope inheritance problem. So, removing both the explicit reference to the controller in the modal HTML as well as in the JavaScript constructor, resolved my problem:
<!-- START Add Event Video -->
<script type="text/ng-template" id="EventVideo.html">
<div class="event-modal">
<div class="modal-header"><h3>Event Video</h3></div>
<div class="modal-body">
<p>Please enter the URL of either a <strong>YouTube</strong> or <strong>Vimeo</strong> video.</p>
<span ng-if="!Event.VideoUrlIsValid" style='color:#9f9f9f;'>This doesn't look like a valid YouTube or Vimeo Url. Your video may not work.</span>
<!-- <div class="row" ng-controller="EventCreateController"> <--REMOVE THIS! -->
<div class="row">
<div pr-form-input span="12" name="videoUrl" ng-model="Event.Item.VideoUrl" placeholder="YouTube or Vimeo URL" isRequired="false" no-asterisk></div>
</div>
</div>
<div class="modal-footer"><button class="btn btn-primary" ng-click="Event.UI.EventVideoModal.Close()">Done</button></div>
</div>
</script>
<!-- END Add Event Video -->
And here is the relevant JavaScript:
EventVideoModal: {
Open: function () {
$scope.EventVideoModal = $modal.open({
templateUrl: 'EventVideo.html',
//controller: 'EventCreateController', <--REMOVE THIS!!
scope: $scope
});
},
Close: function () {
$scope.EventVideoModal.close();
}
}
If you are passing scope to $modal.open() than scope for modal would be created as child scope from passed scope... - so you will have access to all properties from it.
But in case when you are passing the same controller to it - that controller would be applied to new scope and will override all properties from parent.
So in general, as I see the only thing you need to do to achieve desired result is to remove controller from configuration passed to $modal.open() or replace it with something that is specific only for that modal.

Resources