Batch trigger Angular Bootstrap button click events - angularjs

I have a large Angular page with 100+ rows of data, each with a button in the final column which sets a Bootstrap radiomodel based on which button you click.
//....
<tr>
<td>
data1
</td>
<td>
data2
</td>
<td>
<div ng-controller="pickSource" class="btn-group " role="group" aria-label="...">
<label class="btn btn-info btn-xs pickSourceButton takeXxxBtn"
ng-model="radioModel"
btn-radio="'yyy'" >
Approve
</label>
<label class="btn btn-warning btn-xs pickSourceButton takeYyyBtn"
ng-model="radioModel"
btn-radio="'yyy'" >
Reject
</label>
<label class="btn btn-danger btn-xs pickSourceButton noActionBtn"
ng-model="radioModel"
btn-radio="'noAction'" >
Hold
</label>
</div>
</td>
</tr>
//... many of these, the picksource block is actually a directive but i simpified here
Button is like this: (approve|reject|hold)
Controller for this button is like this:
controller('pickSource', function ($scope, $log) {
$scope.radioModel = "noAction";
$scope.$watch('radioModel', function(newValue, oldValue) {
if (newValue !== oldValue) {
$log.log('emitting event');
//even if i remove this below it is slow
$scope.$emit('pickSourceButtonDidChange',someDATA);
}
});
});
I have a batch operation button which will select the same value (i.e. select source for all the rows) for every row.
Currently I am triggering the events like so:
//inside bootstrap dropdown
if(source == "xxx"){
el = document.getElementsByClassName('takexxxBtn');
}
else if (source == "yyy"){
el = document.getElementsByClassName('takeyyyBtn');
}
else{
el = document.getElementsByClassName('noActionBtn');
}
$timeout(function() {
angular.element(el).triggerHandler('click');
}, 0);
However, I am getting really poor performance from this. I imagine this is because the click events each have quite a expensive series of actions (I even took out all the code that is triggered when the button is clicked and still got the same performance hit), but I am wondering if there is any alternative. I cannot simply change the underlying data that the trigger executes because I need the UI to update as well (I need button to look like it's clicked, etc.)
Is there any way to do this?

Using Matthew Berg's (idk how to link to your profile :S ) solution, I emitted an event from the batch action button controller to the parent controller, caught the event in the parent controller and then broadcast the model changes down to all the bootstrap button controllers. This worked excellently.
Thanks Matt

Related

AngularJS - ng-model not updating correctly [duplicate]

This question already has answers here:
Can I nest button inside another button?
(3 answers)
Closed 4 years ago.
I have this piece of HTML:
:
:
<td ng-if="Curr_State == 'Edit' && This_Page.Data_Changed == false" style="max-width:300px">
<button type="button"
class="FD"
ng-click="Initiate_Append_to_JobCard()"
style="height:80px;width:180px;white-space:normal;background:green;padding:10px">
<font size="3" class="ng-binding">Append{{All_Labels.Common.Append}}
</font>
<font size="2">
{{This_Page.Append_Get_Number}}
<table id="Get_Append_Count_an_Execute" ng-if="This_Page.Append_Get_Number == 'Y'">
<tbody>
<tr>
<td>
<label for="Append_Number"> #:</label>
</td>
<td>
<input string-to-number
id="Append_Number"
type="number"
class="form-control"
ng-model="This_Page.Append_Number"
min="0"
step="1"
style="width:70px;margin:0px">
</td>
<td>
<button class="btn btn-success"
ng-click="Do_Append()"
title="Proceed with Append"
type="button">
<font size="2" color="white">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</font>
</button>
</td>
<td>
<button class="btn btn-danger"
ng-click="Cancel_Append()"
title="Cancel Append request"
type="button">
<font size="2" color="white">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</font>
</button>
</td>
</tr>
</tbody>
</table>
</font>
</button>
</td>
:
:
and the following two functions within the controller:
$scope.Initiate_Append_to_JobCard = function() {
$scope.This_Page.Append_Get_Number = "Y" ;
}
//////////////////////////////////////////////////////////////////
$scope.Cancel_Append = function() {
$scope.This_Page.Append_Get_Number = "N" ;
$scope.This_Page.Append_Number = null ;
}
When the outer button is clicked, $scope.This_Page.Append_Get_Number is set to "Y" and the inner objects are properly shown.
When the Cancel button is clicked, the function $scope.Cancel_Append is invoked, the value of $scope.This_Page.Append_Get_Number is set to "N" (first statement of the function) but this change is not propagated towards the HTML. In fact, I added {{This_Page.Append_Get_Number}} which shows initially N, then Y and it then remains unchanged no matter how many times I click on the cancel button.
<button type="button"
class="FD"
ng-click="Initiate_Append_to_JobCard()"
style="height:80px;width:180px;white-space:normal;background:green;padding:10px">
<font size="3" class="ng-binding">Append{{All_Labels.Common.Append}}
</font>
<font size="2">
{{This_Page.Append_Get_Number}} </font></button>
<table id="Get_Append_Count_an_Execute" ng-if="This_Page.Append_Get_Number == 'Y'">
If the table should be inside the button itself, then the click event on the Cancel button will bubble up to the Append button, which will close and open again.
You need to add a
event.stopPropagation()
in your cancel function and also pass the $event to that function
Updated Plunker
https://embed.plnkr.co/CEHNggASaVGqkqfamdEx/
Event Bubbling
When you have an element inside another element and an event occurs on the innermost element (the click event here), then the event is processed by that element and then it is passed up the DOM hierarchy to all the ancestor elements that have a listener for that event and processed by each one.
Here you have a button inside a button, with click event listeners on both. When the inner button (Cancel) is clicked, the listener (Cancel_Append) processes the event (changes the value to N) and then passes the click to the parent/outer button where the listener (Initiate_Append_to_JobCard) processes the event and changes the value to Y again.
Since these happen at almost the same time, we cant see the difference. It can be seen by adding a 'console.log($scope.This_Page.Append_Get_Number)' to both the functions.
To prevent the bubbling of the event, we need to cancel the propagation after the event has been handled. So we need to pass the $event object from the HTML and then after the event is processed by the "Cancel_Append", we need to stop propagating the event further up the DOM using the event.stopPropagation().
Take a look at this link for a better understanding of event bubbling
What is event bubbling and capturing?
make use of ng-show instead of ng-if
<td ng-show="Curr_State == 'Edit' && This_Page.Data_Changed == false" style="max-width:300px">

Custom AngularJS directive with xeditable: doesn't work first time, but works afterward

Building an AngularJS application, I'm using xeditable to allow the user to edit a table, row per row (as described here: xeditable).
The Edit, Remove and Cancel button logic being a bit cumbersome to implement in each table, I created a custom directive: sen-edit-row.
The logic of the buttons in the custom directive works well... except in one case.
When the user clicks [Add], a row is added (just like in xeditable's example), and the new row is immediately editable. Or it should!
The very first time, the row is not editable. The input fields are not created by xeditable. The buttons of my own directive still work.
But the most bizarre thing is: the 2nd time the user clicks [Add], it just works. And then it keeps working!
(this is driving me nuts)
In the HTML, the directive is called this way:
<tr ng-repeat="access in denyAccessTable">
<td class="col-md-4"><div editable-text="access.user" e-form="rowForm">{{ access.user }}</div></td>
<td class="col-md-4"><div editable-text="access.host" e-form="rowForm">{{ access.host }}</div></td>
<td class="col-md-2"><div editable-text="access.mode" e-form="rowForm">{{ access.mode }}</div></td>
<td class="col-md-2"><div sen-edit-row save="saveDenyAccess()" remove="removeDenyAccess($index)" shown="access == newDenyAccess"></div></td>
</tr>
In the JS, the directive is defined as:
angular.module("app", ["xeditable"]).directive("senEditRow", [function() {
return {
restrict: "A",
templateUrl: "sen-edit-row.html",
scope: {
save: "&",
remove: "&",
shown: "="
},
link: function(scope, element, attrs) {
}
}
}])
And the template of the directive:
<div style="white-space: nowrap">
<form class="form-inline" editable-form name="rowForm" onaftersave="save()" ng-show="rowForm.$visible" shown="shown">
<button type="submit" class="btn btn-primary" ng-disabled="rowForm.$waiting">Save</button>
<button type="button" class="btn btn-link" ng-disabled="rowForm.$waiting" ng-click="shown ? remove() : rowForm.$cancel()">Cancel</button>
</form>
<!-- Buttons to edit and remove -->
<div ng-hide="rowForm.$visible">
<span class="glyphicon glyphicon-edit" style="margin-right: 5px;"></span>Edit
<span class="glyphicon glyphicon-trash" style="margin-left: 10px; margin-right: 5px;"></span>Remove
<button class="btn btn-danger" ng-click="remove()" ng-show="showDeleteButton">Remove</button>
<button class="btn btn-link" ng-click="showDeleteButton = false" ng-show="showDeleteButton">Cancel</button>
</div>
</div>
I also created a Plunker that shows evidence of this behavior.
Question: How do I make my directive and xeditable work consistently, including the first time, and activate immediately when a row is added to the table?
Thank you!

ng-select button opens file system dialogue twice

So I am tracking down a bug that I can't quite get a handle around. The problem is this, if you have a button with ng-select on it, unfocus the browser (click on desktop) and then double click on the button, it opens up the browser file system dialogue twice, throwing away the first input. The question is how to force it to only ever open the file dialogue once.
The relevant code:
<!-- Table Buttons -->
<div class="container dashboard__table__action-items-container" ng-if="user.Welcomed">
<button class="btn btn-primary" ngf-select="onFileSelect($files)" ngf-multiple="true" style="margin-right: 15px;">
<i class="fa fa-cloud-upload fa-3" style="margin-right: 4px"></i> Upload Meeting
</button>
...
</div>
Edit:
I tested this behavoir with several browsers and it doesn't happen (firefox, safari, opera). This only happens on chrome. Version for ng-file-upload is 12.2.9, chrome Version 52.0.2743.116.
So I ended up "solving" this by hacking around it, but I still think it is a bug with chrome and/or ng-file-select. For posterity, if you wrap the button in a div that has the ngf-select, you can manually debounce the event propagation.
Relevant code:
html:
<div ngf-select="onFileSelect($files)" ngf-multiple="true" style="width: 0;">
<button class="btn btn-primary" ng-click="debounceMeetingCreation($event)" style="margin-right: 15px;">
<i class="fa fa-cloud-upload fa-3" style="margin-right: 4px"></i> Upload Meeting
</button>
</div>
in the controller:
var meetingCreationClicked;
$scope.debounceMeetingCreation = function ($event) {
if (meetingCreationClicked) {
$event.preventDefault();
$event.stopPropagation();
return false;
}
meetingCreationClicked = true;
$timeout(function(){
meetingCreationClicked = false;
}, 1000);
};

Xeditable form is saving form without even displaying it

I am trying to create an xeditable form as demonstrated here: https://vitalets.github.io/angular-xeditable/#editable-form.
I have followed the instructions exactly but my form is not working. I want to save a resource, but when I click the Edit button, which should display the form, it seems to skip the editing stage and immediately triggers the saveResource function - which should only happen when the form gets saved.
I've compared my code to the documentation again and again and can't work out what I am doing wrong.
HTML
<form editable-form name="editResourceForm" onaftersave="saveResource()">
<p>
<strong editable-text="resource.title" e-name="title">
{{resource.title}}
</strong>
</p>
<div class="col-xs-12 col-sm-4">
<button ng-click="editResourceForm.$show()" ng-show="!editResourceForm.$visible">Edit</button>
<!-- buttons to submit / cancel form -->
<span ng-show="editResourceForm.$visible">
<button type="submit" class="btn btn-primary" ng-disabled="editResourceForm.$waiting">Save</button>
<button type="button" class="btn btn-default" ng-disabled="editResourceForm.$waiting" ng-click="editResourceForm.$cancel()">Cancel</button>
</span>
</div>
</form>
JS
app.controller('Ctrl', function($scope, $filter) {
$scope.resource = {
title: 'awesome resource'
};
$scope.saveResource = function() {
console.log("Save resource");
}
});
JSFIDDLE HERE
You can see that it is trying to save the form, because every time the Edit button is clicked, the console logs "Save resource". This should not happen when the edit button is clicked.
#ckosloski answered this on Github:
I think it's because your edit button does not specify a button type.
By default, the button type is submit. So you are clicking on the
button and it's submitting the form since it's a submit button. Try
adding type="button" to your edit button.
Adding this solved it, as you can see from the updated JSFiddle.

Onchange event with AngularStrap select element

I want to execute a function when the value of the select element changes (the select element in angular-strap is html tag)
My HTML:
<button type="button" class="btn btn-default" ng-model="selectedCriteria" data-html="1" ng-options="choice.value as choice.label for choice in selectChoices" bs-select>
Action <span class="caret"></span>
</button>
My JS:
$scope.selectedCriteria = "Location";
$scope.selectChoices = [{'value':'Location','label':'<i class=\'fa fa-map-marker\'></i> Location'},
{'value':'Age','label':'<i class=\'fa fa-male\'></i> Age'}];
I tried putting an ng-click directive with a function in the controller but it captures the value of the current selected value on click not when the element changes
Thanks
There are a couple of options one is using ngChange Reference
The other is using $watch. See the $watch section of the scopeapi reference
An example using watch (this would be in your controller)
$scope.$watch('selectedCriteria', function() {
$scope.SomeFunction();
});
An example using ngChange
<button type="button" class="btn btn-default"
ng-change="SomeFunction()"
ng-model="selectedCriteria" data-html="1"
ng-options="choice.value as choice.label for choice in selectChoices" bs-select>
Action <span class="caret"></span>
</button>

Resources