AngularJS - ng-model not updating correctly [duplicate] - angularjs

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">

Related

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!

angular-xeditable onsave event

I am using angular package angular-xeditable the problem is I want to call its event onaftersave after i edit the label here is my html:
<h4 >{{ title.key || 'empty' }}
<span onaftersave="save()" editable-text="title.key ">
<i class="fa fa-pencil-square-o" "></i></span>
</h4>
the event onaftersave is called even if i click on the edit button and also while typing in the field
what I want is to call the event only when I click the button
Try to use this code this will helps to prevent function call.
<span>
<a onaftersave="save();" data-ng-model="title.key" e-placeholder="Your text" editable-text="title.key">
<span>{{title.key || 'empty'}}</span>
</a>
</span>

Toggle button when click out of button

I have two states for a button, "State1" and "State2".
It defaults to "State2". When I click the button, it toggles to "State1".
When I click anywhere outside the button, I need it to change from "State1" back to "State2". This is my code:
<div ng-click="Ctrl.Check = !Ctrl.Check">
<a ng-class="{'btn-danger': !Ctrl.Check, 'btn-default': Ctrl.Check }" >
{{ Ctrl.Check ? 'State1' : 'State2' }}
</a>
</div>
It sounds like you're just checking whether the element has focus. This is much easier than putting down a bunch of ng-click handlers all over your form just to toggle one input.
Use ng-focus and the corresponding ng-blur. If you use HTML elements that can have focus (like a <button>), you can use code like
<button type="button" class="btn"
ng-class="{'btn-danger': !Ctrl.Check, 'btn-default': Ctrl.Check }"
ng-focus="Ctrl.Check = true"
ng-blur="Ctrl.Check = false">
{{ Ctrl.Check ? 'State1' : 'State2' }}
</button>
Demo on plnkr

Batch trigger Angular Bootstrap button click events

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

AngularJs behavior button

I have a button:
<button data-ng-click="toggleElement(asset)" class="btn"><span class="text-center">Add To Cart</span></button>
I would like to hide this button when this element will add in a list and show
a disable button with the title "Added to Cart"!!
I tried it :
<table class="table" data-ng-show="elements!=null && elements.length>0">
<tr data-ng-repeat="element in elements">
<td>
<button data-ng-click="toggleElements(element)" ng-disabled="isDisabled" ng-model="isDisabled" class="btn"><span class="text-center">Add To Cart</span></button>
<td> <button data-ng-click="toggleAsset(elements[$index])" data-ng-disabled="added" class="btn">{{added ? 'Added' : 'Add'}}</button>
</td>
</tr>
</table>
in my controller I have this :
$scope.toggleElements= function (element){
.....
$scope.added = true;
}
Somebody can help me...
I think this will work for you:
<button ng-click="toggleElement(asset)" ng-disabled="asset.added">{{asset.added ? 'Added' : 'Add'}}</button>
You'll need to set the property asset.added inside the toggleElement() method.
You can execute several instructions inside one ng-click directive.
Just separate them with ; like a normal javascript expression.
For example:
<button ng-click="doIt(); hide = true" ng-hide="hide">
click me!
</button>
here is one solution.
Have a look to the plunk
<button ng-repeat="a in [0,1,2]" data-ng-click="added = !added" class="btn" ng-disabled="added">
<span ng-show="!added" class="text-center">Add To Cart</span>
<span ng-show="added" class="text-center">Added To Cart</span>
</button>

Resources