UI Bootstrap datepicker duplicated when appended to body - angularjs

I'm using ui.bootstrap.datepickerPopup in a filter header template inside ui.grid. This lets me filter rows by date. I also have a button inside the grid menu that toggles grid.options.enableFiltering.
Due to alignment issues with ui-grid, I have datepicker-append-to-body set to true for my datepickers. The first time I enable filtering, everything works fine. However, when I disable filtering and re-enable it, I get duplicate datepickers.
This is what the problem looks like:
I think the problem is that each time the filters are enabled, the following div is appended to the DOM and never removed when the filters are disabled.
<div uib-datepicker-popup-wrap=""
ng-model="date"
ng-change="dateSelection(date)"
template-url="uib/template/datepickerPopup/popup.html"
class="ng-pristine ng-untouched ng-valid ng-scope ng-empty ng-valid-date-disabled"
>
<!-- ngIf: isOpen -->
</div>
Here's a simplified plunker: http://plnkr.co/edit/eYZt87j2O6A5xhjHj5ZG
I get the same issue if I only use one datepicker inside the Time Range filter header.
Any ideas are greatly appreciated! Don't want to use jQuery.

I don't have an answer on why this is happening but a solution without jQuery would be to remove the pop-up when triggering the filter toggle using document.querySelectorAll()
var elements = document.querySelectorAll("div[uib-datepicker-popup-wrap]");
Array.prototype.forEach.call(elements, function(node) {
node.parentNode.removeChild(node);
});
Plunker here

Related

ngblur and ngfocus on options list shown / hidden in Angular

When using select and option the ngblur isn't triggered once I click off the list without selecting anything.
I need to know when the options of the select and shown or hidden. Is there a directive that supports this ?
I can use ngblur and ngfocus on the Select element but nothing is ever fired on the options being shown or hidden. Any ideas?
I've run some tests on fiddle.
https://jsfiddle.net/EternalLight/x7dxkok2/
<div ng-app="testTt">
<div ng-controller="Controller" style="padding:2em;">
<select ng-focus="onFocus()" ng-blur="onBlur()">
<option>Test 1</option>
<option>Test 2</option>
</select>
<p>{{status}}</p>
</div>
</div>
It seems that the blur event is fired when you click outside of the element when the option list is hidden. When you open the option list, and then click outside, the <select> is still in focus - you can see it by the blue (or any other, depending on your browser) outline around it. Honestly, I would use ng-blur, since it takes another outside click to trigger it, and there's no way for a user to get around it.

Angular : Dropdowns, prevent ng-selected to fire ng-change

I am populating dropdowns in a forloop and wish to set the default value for each dropdown based on an expression. This seems to be working but my problem is when I set the default value via ng-selected it also fires change event which is not ideal.
I would like the change event only to be fired if the dropdown is actually changed.
<div layout="row" ng-if="layer.Styles && layer.Styles.length > 1">
<md-input-container class="md-icon-float md-block layer-style">
<md-select ng-change="vm.changeLayerStyle(layer,selectedStyle)" ng-model="selectedStyle" ng-model-options="{trackBy: '$value.Name'}" aria-label="Change Layer Style">
<md-option ng-repeat="style in layer.Styles" ng-value="style" ng-selected="style.IsDefault == true">
{{style.DisplayName}}
</md-option>
</md-select>
</md-input-container>
</div>
Using angular material for the dropdown
Repeating over option elements have a history of giving people problems. Normally i'd tell you to use ng-options on the select instead, but since you specifically need to use some material components, that is not an option.
I know it's not the prettiest solution, but you could solve the problem by keeping a copy of the last value and compare it to the current one, once the change event is fired. If that value has not changed, then ignore the event. This might need some extra tweaking since setting the layer.Styles will probably just trigger a lot of updates.
Since you didn't give us a plunker to play with, i can only suggest you play around with ng-model-options (which you are already using to control track-by) the updateOn property and setting it to something that won't be triggered by you setting the selected property. The value blur comes to mind here.

Remove Angular UI Bootstrap datepicker popup rendered to body

I'm using Angular UI Bootstrap datepicker popup with the option datepicker-append-to-body="true". Thus the popup is rendered to the <body>.
In my particular scenario I need to remove the DOM element containing the datepicker. Once the element is removed (with element.remove()) the popup stays in the body.
How should I remove the datepicker, so that popup will be also properly removed?
Here is the plunker demonstrating the issue:
http://plnkr.co/edit/zFew72PpY0E2ETwMKRpv?p=preview
I know that removing an element like this in Angular is not the right way, but I'm working on integration of existing jQuery widget with Angular, and this is done in the existing code. I'm trying to find a way to fix this.
Any help is appreciated.
Edit
Sorry, I believe some additional info is needed to clarify the case:
As I mentioned, I integrate a jQuery plugin with Angular. This is grid.
Grid can have editing row. Grid user can put a Angular UI Datepicker in the editing row to edit date field. Once a user clicks Save (or Cancel), the editing row is removed with $element.remove(). So this happens in the external lib code. What I can do, is to patch removing code and call some cleaning up code. The question is what this cleaning up code could be, so it will clean up the datepicker.
ng-if, unfortunately, is not an option, because patching code knows nothing about the content of the cell and fields of the scope. This code is generic, while datepicker is put in the cell by grid user. In fact it could be any other widget.
You could use ng-if to remove or add back to your html. here is a plunker:
$scope.showDatePanel = true;
$scope.removePanel = function() {
$scope.showDatePanel = false;
}
in your html:
<div id="panelToBeRemoved" ng-if="showDatePanel" class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup ng-model="dt" datepicker-append-to-body="true" is-open="popup.opened" />
</p>
</div>
</div>
hope it helps.
uib-datepicker use ng-if. it's mean if you assign true in is-open attribute is dynamically append into your body and if assign false then it's automatically destroy in your body tag. So when you press remove element button than assign is-open false. change in your example.js file ->
$scope.open = function() {
if($window.document.getElementById("panelToBeRemoved") != null){
$scope.popup.opened = true;
}
};

ng-if Function Gets Called on Click

I have my code on Plnkr:
http://plnkr.co/edit/6wz4zub2sEOfhrqps0c8
The html code is quite simple:
<div ng-app="myApp" ng-controller="MainCtrl">
<div ng-dropdown-multiselect="" options="myData" selected-model="myModel" extra-settings="mySettings" events="myEvents"></div>
<div ng-repeat="item in Items">
<item-input></item-input>
</div>
</div>
</div>
Basically, the problem is my function in ng-if gets called every time I click anywhere on the page. I think it has something to do with the multi-select control but I'm not sure where I should fix it. Any help is appreciated.
Thanks.
UPDATED
What I don't really understand is why this behavior stops when I comment out the multi-select dropdown (<div ng-dropdown-multiselect="" options="myData" selected-model="myModel" extra-settings="mySettings" events="myEvents"></div>)?
As shown in the picture below, taken from Chrome DevTools, the multi-select dropdown is generating two click event handlers :
One event is on the button and one on the whole document. Therefore, a click on the document is triggering at least that last event handler, which potentially changes the model. Angular then do a digest cycle, and that implies that the displayMe function bound to the ngIf directive is evaluated in case the element should be removed of the DOM or not.
If you remove the dropdown component, and thus these two click handlers, you are left with 3 text inputs. Indeed, now there are no digest after a click on the document because no handler are executed, but you can still trigger digests essentialy by typing inside the input elements.
This answer by Miško Hevery has some valuable info about the dirty-checking process in Angular 1.x.

Angular UI Bootstrap accordion heading not working correctly with checkbox

I'm using a check box in the heading of the accordion control in bootstrap, but the model will only update the first time it's clicked. Here's the HTML for the accordion:
<accordion ng-repeat="timesheet in timesheets">
<accordion-group>
<accordion-heading>
Title
<input type="checkbox" ng-model="approvals.rejected" ng-click="approvals.timesheetChecked($event)"/>
</accordion-heading>
</accordion-group>
</accordion>
the click method is in my typescript controller:
timesheetChecked($event: Event) {
$event.stopPropagation();
$event.preventDefault();
}
If I just use the stopPropagation() method by itself it updates the model correctly and the check box is checked, however, it will then refresh the page. The preventDefault() method stops this from happening, but then it will then only update the model once and not check the check box.
I have tried using ng-bind and that will actually update the model with correctly, but it will not change the check box to checked.
If I use the check box outside of the accordion it works as expected and I have no problems with it. I'm not really sure what I am doing wrong here?
I believe your code would work, but it only works with Angularjs 1.2.x and ui-bootstrap-tpls version 0.11 (earlier versions may work).
Your code is similar to the following post...
https://stackoverflow.com/a/24502123/3807872
If you are using Angular 1.3.x, then you will want to add the stop propagation inside of the ng-click...
<input type="checkbox" ng-model="approvals.rejected" ng-click="approvals.timesheetChecked($event);$event.stopPropagation();"/>
This example was in thanks to the following post...
https://stackoverflow.com/a/14545010/3807872
This really worked for my situation as well.
By the way, I don't know if you have fixed this issue but I have the same problem, however for displaying the right checked checkbox I've added this:
ng-checked="approvals.rejected == 'true'"
and I only stopPropagation method.
But actually it doesn't work for the first click!

Resources