AngularJS ng-class Expression Not Updating Properly - angularjs

So I'm trying to build a custom autocomplete dropdown for a text input. To do it, I am listening for the keydown event and if it's an up or down arrow press, I'm setting a $scope.arrowSelectedItem variable to the proper one in the list. (As a side note, all the functionality works as far as selecting an item from the list that pops up. All I'm trying to do is highlight the current one that they've marked with the up/down arrows).
On the markup side, the items in the autocomplete list are output with ng-repeat, with ng-repeat="item in itemList". The ng-class expression I'm using is ng-class="{highlighted: item === arrowSelectedItem}". I know that the $scope.arrowSelectedItem is being updated on each arrow press by using console.log, but for some reason the class isn't being updated to the list item properly.
What I've found is that after the first time of hitting an arrow key, if I make the text input box lose focus, then the class is added. Then if I click back in the box, move the arrow to select a different item, click out of the input box, then click back in, the class is added to the new one. I know that sounds weird, but that's what I've found.
What I'm not sure about is why the ng-class expression isn't being evaluated on every arrow key press. Does anyone have any ideas why?

The answer here is that "raw" DOM events which fire outside of one of angular's built in directives (such as click events via ng-click etc) will not trigger a $digest cycle update. Until this happens the $scope properties will not be updated.
If you are in a position where you are listening for DOM events by using another framework, or simply using addEventListener(), you will need to let angular know about any changes by using $scope.$apply(), or by wrapping the code in a $timeout().
If you do this in your event handler, angular will trigger a new $digest cycle update for every keypress and your new scope values will propagate to the view.

Related

Two-way databinding to mddialog

I cannot figure out how to make a two-way-binding to an mddialog. Can anyone give me an example, where data passed to a mddialog is updated when it is updated in the original scope?
I have tried parsing data using 'locals' with no avail, setting the dialog scope to the main scope, etc. But I must be doing something wrong.
As an example I expect a mddialog to be open (using $mdDialog.show()). The modal template show a number, that can be increased by one when clicking a button on the dialog template. At the same time, the number is also outside the modal (parent), with another button that can decrease the number when clicked. The number is updated in both places when either button is clicked.

How to apply an animation on an item that is getting removed without using a timeout

I'm using Angular 2 - RC3.
My problem is, I have an item which is shown or hidden using a *ngIf.
When the item is getting shown or hidden I want to apply an animation.
I'am able to apply an animation when the item first shows simply by adding a class. But the problem is when it's getting removed, the item gets removed from the screen before the animation can start/finish.
I know you can overcome this by setting a timer to delay removing action so that the animation can finish. But I don't want to put timers everywhere I need this functionality.
Is there an easier way in Angular to achieve this?
I don't want to use JQuery is either.
Use [hidden] directive on your element, as it does not prevent the element from rendering like *ngIf does.
[hidden] mean it takes up space in your DOM document but it's display is set to : none !important.
*ngIf on the other hand removes the element from the DOM completly

Invoking a callback before or after showing a popover using angular-ui-bootstrap

I have a very simple code snipper in my page where I have a span. Hovering over this span displays a popover for which I am using angular-ui-bootstrap.
<span uib-popover="This is a popover from Akhilesh"
ng-mouseenter="vm.logToConsole('I am trying hard...')"
popover-trigger="mouseenter">Hover over me to see a popup..!!</span>
Basically I have written a function which makes and API call when the user hovers over this span. The problem here is that let's say I have 10 span tags one below the other and the user quickly moves from 1st span to 10th span (in the process hovering over all 8 spans in between), the API call will get triggered for all the spans. This is what I do not intend to have.
Any idea how can I implement the debounce functionality here?
Use a delay, like one second, after the mouse enters the region, then if the mouse hasn't entered another area, make the API call.
The popover-is-open attribute was added under the 0.13.4 release that can be used to watch the state of your popover like so:
<span uib-popover="This is a popover from Akhilesh"
popover-is-open="vm.isOpen"
popover-trigger="mouseenter">Hover over me to see a popup..!!</span>
Then in your controller:
$scope.$watch('isOpen', function() { });
But if you are just trying to keep the popovers from opening so quickly, consider using the popover-open-delay attribute.
Depending on your use, I found the best method is to simply add ng-mouseover, ng-click etc to the element and define a function to be called.
You can even create a variable and attach it to that objects scope on the fly to keep track of the state (open close).
Kind of hacky, but there is currently no way to define a function that is called on open and on close within ui-bootstrap popover.

Differentiating arrow clicks between multiple UI-Select controls on one page

Hi I would like to customize behavior of the ui-select little bit. I use two bootstap themed ui-select controls on my page with the help of templatecaches. In the template, I wired up arrow button click event using ng-click tag. That way I can easily catch the click event on the arrow button, and in my controller I can open a popup using function, for instance:
<button ng-click = "someFunctionInTheScope()">
For instance if I have two of those ui-select elements in my view, I need to differentiate which arrow button is clicked to display the correct popup. Since I am using the same template for two ui-select controls and since theoretically I can have any number of these controls on my page, I can not easily add a parameter to the method in the template to differentiate which arrow image of which ui-select control is clicked:
<button ng-click= "someFunctionInTheScope(1)">
Because both ui-select control would be using the same template code and 1 would be passed to the controller function for both of them.
Therefore I need to find a more clever way of changing the template dynamically once and for each control.
So I thought about having something like
<button ng-click= "someFunctionInTheScope($select.id)">
but when I debug it I see that functions parameter is undefined, every time it is clicked.
Can somebody please show me how to hack this?
There is no id property on the $select object. You're best bet is to pass something through the scope of the element containing the ui-select boxes. In other words, your code needs to generate a unique identifier for each ui-select box you have. This could be the $index property of an ng-repeat block, a timestamp, or something dependent on other context.
A little more context and I can provide a more specific answer.

Angular Bootstrap: How to show a popover based on a child elements' trigger?

Demo Fiddle
I'd like to show some help text using a popover for the entire group of fields, not for each individual input.
In the fiddle, I'm simply using a mouseenter trigger to show how it should look like, but what I really want is to trigger it on focus for any input, but have the popover be positioned based on the parent element.
In non-angular land, I'd trigger a custom event (say groupenter) when any one of the fields is in focus, and have the popover listen to that event. I'd also debounce the corresponding groupleave event so the popover won't flicker as I tab around the inputs.
What's an angular-y way to accomplish that here?
(I think this patch helps, but it just got committed days ago)
Got it working!
I had to fork tooltip.js to add 'groupenter': 'groupleave' to triggerMap since there's no public api to add to the map.

Resources