How to prevent button click from collapsing pane - javascript

I have a ui-bootstrap pane (header & body) with a button on the header. Clicking the header area should collapse the body. Clicking the button should just activate an alert popup. Unfortunately, clicking that button ALSO collapses the pane. I suspect that the pane behind the button is somehow capturing the click. How can I prevent this?
Here's the plunker:
http://plnkr.co/edit/teCnC7KtvlQtosZurlqD?p=preview
<div class="panel panel-primary">
<div class="panel-heading" ng-click="vm.onClickHeader()">
<span ng-hide="vm.collapsed" style="padding-top: 8px">
<span ng-click="vm.onClickBtn()"><i class="fa fa-chevron-left fa-2x"></i></span>
</span>
</div>
<div class="panel-body" ng-hide="vm.collapsed" style="padding: 2px; padding-right: 4px">
<div>Click the button above to hide me!</div>
</div>
</div>
var app = angular.module('app');
app.directive('myPanel', MyPanel);
function MyPanel() {
return {
templateUrl: 'my-panel.html',
restrict: 'E',
controller: MyController,
controllerAs: 'vm',
scope: true
};
};
function MyController() {
this.collapsed = false;
this.onClickBtn = function onClickBtn() {
alert('click!')
};
this.onClickHeader = function(evt) {
this.collapsed = !this.collapsed;
}
}

The problem is the click event is propagating up the DOM from the <span>. You can add a $event.stopPropagation() to your ng-click directive. (Some people don't like this because they feel it's "dirty" to add such code into a directive so you could always pass $event as a parameter on vm.onClickBtn() and call .stopPropagation() in your controller.)
<span ng-click="vm.onClickBtn(); $event.stopPropagation();"><i class="fa fa-chevron-left fa-2x"></i></span>

On inner ng-click just stop event bubbling by calling stopPropagation on $event object.
<span ng-click="vm.onClickBtn();$event.stopPropagation()">
<i class="fa fa-chevron-left fa-2x"></i>
</span>
Demo here

Related

Edit items by clicking a button. Angular.js

I currently have a directive to dynamically edit a field. I have in the header of the accordion a field to edit and another field in the content of the accordion. If I click the edit button, the fields in the respective row can be edited, and this works fine. My problem is when I save or when I cancel it (when I click on the save or cancel button) immediately disappears the text field of both the header and the contents of the header. I need the text field ONLY disappear for the item in which I am going to save or cancel. When you click on the edit button, the 2 text fields should appear in both the header and the content (this works). And when clicking save or cancel, the text field in the selected element should disappear / appear.
<div ng-controller="AccordionDemoCtrl">
<uib-accordion close-others="true">
<div ng-repeat="faq in faqs">
<div class="col-sm-11" >
<div uib-accordion-group class="panel-default" is-open="faq.open">
<uib-accordion-heading >
<span ng-click="ignoreClick($event);" ><a href='' click-to-edit edit-state='faq.editState' ng-model='faq.pregunta' typeinput='textarea' >{{faq.pregunta}}</a></span> <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': faq.open, 'glyphicon-chevron-right': !faq.open}"></i>
</uib-accordion-heading>
<span click-to-edit edit-state='faq.editState' ng-model="faq.respuesta" >{{faq.respuesta}}</span>
</div>
</div>
<div class="col-sm-1" >
<button type="button" ng-click="toggleEditState($index)" class="btn btn-default">
<span class="glyphicon glyphicon glyphicon-edit"></span>
</button>
</div>
</div>
</uib-accordion>
</div>
https://plnkr.co/edit/S4OllJV64EYFNo6WIjVH?p=preview
use one-way (one-directional) binding for editState
scope: {
model: '=ngModel',
editState: '<'
},
https://plnkr.co/edit/dNehOxAIRHsRqgK9wXJx?p=preview
I believe you'll need to manage two separate booleans at the controller level so one directive doesn't overwrite the status of the other and then you have to set both booleans with your open button. I kept the master state so your glyph button will open or close both and the save/cancel should operate independently of each other.
$scope.editState = false;
$scope.editHeader = false;
$scope.editBody = false;
$scope.toggleEditState = function(index, val) {
debugger;
$scope.editState = !$scope.editState;
$scope.faqs[index].editHeader = $scope.editState;
$scope.faqs[index].editBody = $scope.editState;
}
<div ng-repeat="faq in faqs">
<div class="col-sm-11" >
<div uib-accordion-group class="panel-default" is-open="faq.open">
<uib-accordion-heading >
<span ng-click="ignoreClick($event);" ><a href='' click-to-edit edit-state='faq.editHeader' ng-model='faq.pregunta' typeinput='textarea' >{{faq.pregunta}}</a></span> <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': faq.open, 'glyphicon-chevron-right': !faq.open}"></i>
</uib-accordion-heading>
<span click-to-edit edit-state='faq.editBody' ng-model="faq.respuesta" >{{faq.respuesta}}</span>
</div>
</div>
<div class="col-sm-1" >
<button type="button" ng-click="toggleEditState($index)" class="btn btn-default">
<span class="glyphicon glyphicon glyphicon-edit"></span>
</button>
</div>
https://plnkr.co/edit/91cGWoTKyJsZQKxYapiT?p=preview
You could also communicate the master status with the directives if you wanted their input fields to open and close both.

javascript click event not firing

I'm having trouble figuring out why my click event is not firing. I am trying to click on the trash can icon and have a function run that deletes the list element. After the click event, it fails to execute anything else in the script. It doesn't even console.log anything directly below. Below is the markup and my script.
HTML CODE
<div class="col-6">
<div class="js-list-item">
<h3 class="js-list-title"></h3>
<i class="fa fa-pencil" aria-hidden="true"></i>
<i class="fa fa-trash-o" aria-hidden="true"></i>
<p>Date: <span class="js-date"></span></p>
<p>Schedule: <span class="js-schedule"></span> at <span class="js-schedule-time">
</span></p>
</div>
</div>
javascript code
function handleDelete() {
$('.js-list-item').on('click', 'fa-trash-o', function(e) {
console.log('test');
e.preventDefault();
deleteShow(
$(e.currentTarget).closest('.js-list-item').attr('id')
);
});
}
The second parameter of the .on() function is a selector.
http://api.jquery.com/on/
Change:
$('.js-list-item').on('click', 'fa-trash-o', function(e) {
to:
$('.js-list-item').on('click', '.fa-trash-o', function(e) {
('click', 'fa-trash-o', function(e)
fa-trash-o is not a proper selector, use class or id

ng-blur how to not affect all html element

I want to be able to click some part of page to not have it call the hideAll function
app.controller('NotificationController', function($scope, $http) {
$scope.visible = true;
$scope.changeStatus = function(){
$scope.visible = !$scope.visible;
};
$scope.hideAll= function(){
$scope.visible=false;
};
});
Here is a link showing this:
Press
I want it so that when press on page this block I don't want to trigger the blur
<div class="notifications js-notifications" ng-init="visible=false" ng-show="visible">
....
</div>
Any idea how I make this work?
EDIT:
Full html:
<li id="notifications" ng-app="notifications" ng-controller="NotificationController as notification">
<a href="#" ng-disabled="checked" ng-click="changeStatus()" ng-blur="hideAll()" class="button-default show-notifications js-show-notifications active">
<i class="fa fa-bell-o" style="font-size: 17px;">
<div class="notifications-count js-count" data-count="<% notys.length %>"><% notys.length %></div>
</i>
</a>
<div class="notifications js-notifications" ng-init="visible=false" ng-show="visible">
<h3>Notifications</h3>
<ul class="notifications-list">
<li class="item no-data">You don't have notifications</li>
<li ng-repeat="x in notys" class="item js-item" data-id="<% x.id %>">
<a href="<% x.project_id %>" class="notification-link">
<div class="details">
<span class="title">New group created: <b> <% x.subject %> </b>. New project assigned to you <b> <% x.body %> </b></span>
<span class="date"><% x.created_at %></span>
</div>
</a>
</li>
</ul>
Show all notifications
</div>
</li>
You might need a custom blur directive to handle events on a separate notification container. Something along these lines:
directive('customBlur', function(){
return {
restrict: 'A',
scope: {
'customBlur': '='
},
link: function(scope, element, attr) {
element.on('click', function(event){
var targetAttr = angular.element(event.target).attr('custom-blur')
if (typeof targetAttr !== 'undefined' && scope.customBlur) {
scope.$apply(function(){
scope.customBlur = false;
});
}
});
}
}});
Here is a working plukner
Description given in W3 specification docs,
blur
The blur event occurs when an element loses focus either via the pointing device or by tabbing navigation. This event is valid for the following elements:
LABEL, INPUT, SELECT, TEXTAREA, and BUTTON.
Bubbles: No
Cancelable: No
Context Info: None
It will give you unpredictable output for other tags. If you have noticed, div tag doesn't capture focus hence no blur event.
But if you want to use event like blur, you can create custom directive which can be combinations of these four events DOMFocusOut, DOMFocusIn, mouseenter or mouseover, mouseleave.

In Angular-Kendo treeview, using a class name to hide/display a span element

I'm using the Angular-Kendo UI treeview widget in conjunction with the k-template directive to display a list of icons to the right of each tree node.
With this way of doing it, my jquery .click() event should hide/show my reptIcons span, but it's only working on the very first tree node at the top.
It should work on every node I click on, but it's reverting to the first node in my tree.
Here's an image to show exactly what I mean:
Here is the html with the kendo-tree-view directive and my custom data-toggle-tree-icons directive:
<div id="" class="reports-tree" kendo-tree-view="nav.treeview"
k-options="nav.treeOptions"
k-data-source="nav.reportsTreeDataSource"
k-on-change="nav.onTreeSelect(dataItem)" >
<span class="tree-node" k-template>
{{dataItem.text}}<data-toggle-tree-icons/>
</span>
and then the javascript to handle the click event:
$('.reports-tree').click(function (e) {
$("span[class$='reptIcons'").hide();
$("#myIcons").closest('span').css("display", "inline");
$("#myIcons").css("margin-left", "5px");
});
The directive code is:
directive('toggleTreeIcons', function ($compile) {
// on Kendo treeview, using the k-template option to embed tree icons
return {
restrict: 'AE',
templateUrl: 'app/shared/include/treeicons-include.html'
}
});
contents of treeicons-include.html
<span id="myIcons" class="reptIcons" style="display: none; width: 50px;">
<a title="add new folder" ng-click="nav.addAfter(nav.selectedItem)"><i class="fa fa-folder-open"></i></a>
<a title="add report here" ng-click="nav.addBelow(nav.selectedItem)"><i class="fa fa-plus"></i></a>
<a title="remove" ng-click="nav.remove(nav.selectedItem)"><i class="fa fa-remove"></i></a>
<a title="rename" onclick="showRename(this);"><i class="fa fa-pencil"></i></a>
</span>
Ideally I'd like to use the tree-node class above, and when a tree node is selected I would use the jQuery .click() to show the myIcons span shown above.
I'm having particular trouble figuring out the best way to wire up that .click() event above. And I know I'm so close to nailing this.
thank you !
Bob
You may be able to do this in your k-on-change="nav.onTreeSelect(dataItem)". I can't see the full implementation, but best guess would suggest you can do something like this, where $(this) ideally is the treeview node selected.
.onTreeSelect = function(dataItem) {
$('.reports-tree .reptIcons').hide();
$(this).closest('.reptIcons').css({'display : inline', 'margin-left' : '5px'})
}
Edit
Per suggestion in the discussion, if the simplistic approach above will not suffice, a custom directive can be crafted to handle this as such
.directive('toggleMe', ['$compile', function ($compile) {
return {
restrict: 'AE',
transclude: true,
template: '<span id="myIcons" class="reptIcons" style="display:inline;width:50px;align:right;">' +
' <a title="add new folder" ng-click="nav.addAfter(nav.selectedItem)"><i class="fa fa-folder-open"></i></a> ' +
'<a title="add report here" ng-click="nav.addBelow(nav.selectedItem)"><i class="fa fa-plus"></i></a> ' +
'<a title="remove" ng-click="nav.remove(nav.selectedItem)"><i class="fa fa-remove"></i></a> ' +
'<a title="rename" onclick="showRename(this);"><i class="fa fa-pencil"></i></a>' +
'</span>',
link: function (scope, elem, attrs) {
}
}
}])
With implementation
<li ng-repeat="item in nav.items">
<span tabindex="-1" ng-click="nav.onItemSelect(item)" ng-blur="nav.selected = null">
<span>{{item}}</span>
<data-toggle-me ng-show="nav.selected === item"></data-toggle-me>
</span>
</li>
.controller('NavBarCtrl',['$scope', function($scope) {
var nav = this;
nav.items = ['Item A', 'Item B', 'Item C'];
nav.onItemSelect = function(item) {
nav.selected = item;
}
}]);
JSFiddle Link

Collection-Repeat and $ionicModal Strange Behavior

I am making a directory app that comprises roughly 200 list items (employees). The app worked as intended using ng-repeat, however, it was sluggish to load. I switched to Collection-Repeat to take advantage of the speed boost but I am getting bizarre behaviors that I can't figure out.
The list items are rendering correctly, alphabetically with the category titles added successfully. The problem is, each list item has a ng-click attribute that opens an $ionicModal. The modal for each item opens, but the loaded data is incorrect.
When the modal opens, it starts at the bottom of the page - I can see the contents for half a second before it animates to the middle of the screen. To start, the loaded data is correct. As it animates, it switches to another employees data. I can't seem to figure out why. I'm new to angular/ionic so any pointers would be great. Thanks!
EDIT - Out of curiousity, I added a second ng-controller="ModalCtrl" ng-click="openModal();" to each element as a button. Clicking on the element does the usual - opens the modal with the wrong employee. Clicking on the newly created button however creates TWO modals (stacked on eachother) BOTH with the correct employee. Removing either instance to the ng-controller or ng-click puts me back at square one with only one modal of incorrect data. Why is this? Why does adding a second ng-click correct the problem (despite having two modals)?
EDIT - Here is a link to a codepen sample (dumbed down, but proves my issue: http://codepen.io/anon/pen/zijFv?editors=101
My HTML looks like this:
<div class="list">
<a class="item my-item"
collection-repeat="row in contacts"
collection-item-height="getItemHeight(row)"
collection-item-width="'100%'"
ng-class="{'item-divider': row.isLetter}">
<!-- ADDED BUTTON SEE EDIT COMMENT ABOVE -->
<button ng-if="!row.isLetter" ng-controller="ModalCtrl" ng-click="openModal();">Click</button>
<img ng-controller="ModalCtrl" ng-click="modal.show()" ng-if="!row.isLetter" ng-src="data:image/jpeg;base64,{{row.image}}">
<h2>{{row.title || (row.firstname+' '+row.lastname)}}</h2>
<p ng-if="!row.isLetter"><em>{{row.jobtitle}}</em></p>
</a>
</div>
My Modal HTML is this:
<header class="bar bar-header bar-lsi">
<h1 class="title">Contact Information</h1>
<div class="button button-clear" ng-click="closeModal()">
<span class="icon ion-close"></span>
</div>
</header>
<ion-content has-header="true" style="margin-top: 0px !important;">
<div class="list card" style="border-radius: 0px !important;">
<div class="item item-avatar item-text-wrap">
<img ng-src="data:image/jpeg;base64,{{row.image}}">
<h2>{{row.firstname}} {{row.lastname}}</h2>
<p>{{row.jobtitle}}</p>
</div>
<a href="tel:{{row.phone}}" class="item item-icon-left">
<i class="icon ion-iphone"></i>
{{row.phone}}
</a>
<a href="mailto:{{row.email}}" class="item item-icon-left">
<i class="icon ion-email"></i>
{{row.email}}
</a>
</div>
</ion-content>
And then I have my basic controller:
.controller('ModalCtrl', function($scope, $ionicModal) {
$ionicModal.fromTemplateUrl('my-modal.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
});
$scope.openModal = function() {
$scope.modal.show();
};
$scope.closeModal = function() {
$scope.modal.hide();
};
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
})
I think the problem is that you aren't passing to the modal template any value. It's getting residual values. I see too that you are using too much ng-controller and ng-click in items list and what is inside it. I mean, if you use ng-click for A.item, you don't need to use ng-click for the image inside it.
Let's see some code:
<a class="item my-item"
collection-repeat="row in contacts"
collection-item-height="getItemHeight(row)"
collection-item-width="'100%'"
ng-class="{'item-divider': row.isLetter}"
ng-controller="ModalCtrl" ng-click="openModal(row);">
<img ng-if="!row.isLetter" ng-src="http://placehold.it/65x65">
<h2>{{row.title || (row.firstname+' '+row.lastname)}}</h2>
<p ng-if="!row.isLetter"><em>{{row.jobtitle}}</em></p>
</a>
As you can see, I've removed all ng-click and ng-controller inside A tag, and I've left only what is attributes of A tag. You can notice too that I pass the object row to the openmModal() function.
In controller, I've made next changes:
$scope.openModal = function(item) {
$scope.modal.row = item;
$scope.modal.show();
};
And in the modal template I've used modal.row as variable with the data from the item list touched. So in template I use it like this:
<div class="item item-avatar item-text-wrap">
<img ng-src="http://placehold.it/65x65">
<h2>{{modal.row.firstname}} {{modal.row.lastname}}</h2>
<p>{{modal.row.jobtitle}}</p>
</div>
<a href="tel:{{modal.row.phone}}" class="item item-icon-left">
<i class="icon ion-iphone"></i>
{{modal.row.phone}}
</a>
<a href="mailto:{{modal.row.email}}" class="item item-icon-left">
<i class="icon ion-email"></i>
{{modal.row.email}}
</a>
I've test it in your codepen and it works. Try it and tell me if it works for you.

Categories