angular watch for angular-leaflet-directive - angularjs

I am trying to place $watch on a value in angular to setup controls differently depending on instructions from the controller and I have tried to follow the lead in angular-leaflet-directive using leafletScope.$watch("variable").
I have added a new $watch with equality comparison as it's an object:
leafletScope.$watch("controls", function(controlOpts) {...}, true)
I then call it in the controller using:
angular.extend($scope, {
controls: {}
}
This is to initialise the controls as this seems to be required.
I then call it later on an event using:
$scope.controls = { new object }
If I log the change in controls and then also on the $watch event, I get the following sequence:
control $watch event logged
$scope.control change event logged
no further logs
The fact that the watch isn't called after the $scope is changed suggests I am doing this wrong.
Can anyone advise me where, or if I have reached the wrong conclusion in my simple test.
If I do the same and change my "center" model, I get:
center$watch event logged
$scope.center change event logged
center$watch event logged with new value

This turned out to be a clash between angular-leaflet-directive and the html minifier I was using html-minifier. The angular-leaflet-directive notation for leaflet controls is controls so in my html file I had
<leaflet center="centre" controls="controls" layers="layers" width="1200px" height="800px"></leaflet>
the html-minifier took controls="controls" to be a boolean attribute and with the collapse boolean attributes flag on reduced it to
<leaflet center="centre" controls layers="layers" width="1200px" height="800px"></leaflet>
Which wasn't good. I will request this is either better documented in angular-leaflet-directive or they change the name of controls to leaflet-controls or something to avoid this happening to unsuspecting folk like me in the future.
Incidently the true flag on the watch proved to be too resource intensive and I removed it and it still worked..

Related

How do I return focus to an element when the entire page changes?

I have a complicated setup. My application is driven by a set of "rules" which dictate what the user interface is. The UI is rendered by looping through the rules and creating the individual dropdowns. Initially, everything renders properly. However, once a user makes a change to the UI, other rules may be affected. In my application, an api call is made, which then returns a modified set of rules. In the attached plunker, I've simplified things such that only the new set of rules is applied, which causes the page to re-render. The problem is that my users would like to be able to tab between all of the entries on the page and make changes. However, once the page is re-rendered, the currently selected page element is now gone nothing has the focus. I've tried to put the focus back on the proper element by tracking a common Id, but to no avail.
Using either of these doesn't seem to work.
var el = document.getElementById(focusId);
el.focus();
angular.element(el).focus();
I've also tried using the autofocus attribute on the dropdown that I want to have focus, but that didn't work either. I'm using angularjs 1.2. Any ideas are appreciated.
http://plnkr.co/edit/ND9PKqULIOlixWR4XChN?p=preview
If you want to assign auto focus dynamically to a element on the DOM from angular you can use this:
var myEl = angular.element(document.querySelector('select'));
myEl.attr('autofocus',"attr val");
You can also pass in an id like: angular.element(document.querySelector('#focusId'));
You can look here for a prior answer which may be of some more help!
-Cheers!
Problem here is, you are trying to focus the element before the blur event completes. So you need to execute the focus code after blur event. Async execution of your focus code would solve the problem. You can use either setTimeout or $timeout.
setTimeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
or
$timeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
dont forgot to inject $timeout to your controller if you are using second code. Hope this helps :)

Angular scope variable update not reflected in UI

We are working on an HTML page which makes use of a Bootstrap tooltip on a certain <span> tag. For those who have not heard of tooltip, it is a popup of sorts which appears when hovering over the element to which it is attached. Here is a screenshot showing the <span> in question, and what happens on hover:
The premise behind adding the tooltip was that in the event that we truncate the text, the tooltip would provide an option for viewing the entire text.
However, we would now like to condtionally show the tooltip only when there is no ellipsis in the text. We defined the tooltip-enable property in the <span>:
<span uib-tooltip="{{someName}}" tooltip-placement="right" tooltip-enable="{{showToolTip}}">{{someNameShortened}}</span>
The key thing here is tooltip-enable="{{showToolTip}}", which binds the property to a scoped variable in the controller for this page. And here is the relevant (and abbreviated) controller code:
mainApp.controller('repoListController',['$scope', '$rootScope', ...,
function($scope,$rootScope, ...) {
$scope.showToolTip = false;
var repositoryList= function(){
repositoryService.getRepositoryList(function(data) {
var repoList = data;
repoList.shortenedDisplayName = repositoryService.getShortRepoName(repoList.repoName, DISPLAY_NAME_MAX_LENGTH);
// if the repository's name be sufficiently large (i.e. it has an ellipsis)
// then show the tooltip. Otherwise, the default value is false (see above)
if (repoList.repoName.length > DISPLAY_NAME_MAX_LENGTH) {
$scope.showTooltip = true;
}
});
}
repositoryList();
}]);
Based on the research I have done, the common solution for why a change to a scoped variable is not reflected in the UI is to run $scope.$apply(), or some variation on this. Running apply(), as I understand it, will tell Angular JS to do a digest cycle, which will propagate changes in the scope to the UI. However, trying to do an apply() from the code which toggles showToolTip resulted in errory. I inspected the value of $scope.$root.$$phase while running the code which updates the showToolTip variable, and the phase was digest.
So now I am at a loss to explain this. If the code is already in a digest, then why would changes not be reflected in the UI? Also, if the code is already in digest, then how could I force Angular to sync the changes to the UI?
Two things need fixing...
Don't use string interpolation for your boolean showToolTip
<span uib-tooltip="{{someName}}" tooltip-placement="right"
tooltip-enable="showToolTip">{{someNameShortened}}</span>
JavaScript variables / properties are case sensitive. In your getRepositoryList handler, you have $scope.showTooltip. It should be $scope.showToolTip (two capital "T"s)
Crappy Plunker demo ~ http://plnkr.co/edit/W7tgJmeQAJj0fmfT72PR?p=preview

Foundation error/fade flashes with ngAnimate before ngModel loads

I've got a directive, with a field in it. The field is being passed a $scope with the model in it.
The field is ng-required="true".
Because there is a split second where the data isn't loaded into the template, there is an unsightly flash of red error text around the field.
I've tried template caching, changing the class colour, but none of it happens at the right time. What we've done in the interim is remove ng-animate. This stops the delay and flash and fade. However, this is a set back as animations would be swell in other places.
From here I will ask the concrete question:
As ngAnimate is injected in the app.js, is there a way to disable injection at a per element/controller/directive level?
As far as I know, injection cascades when it is included in the global app.js.
What I've done is set a flag to signal when the content has finished loading, and I don't show any content until that's been set to true.
On your controller
$scope.finishedLoading = function() {
... whatever logic you need to figure out if all your data is loaded ...
return $scope.myData != null;
}
Then in some part of your template that wraps the fields you want initially hidden:
<div ng-hide="finishedLoading">Please wait...</div>
<div ng-show="finishedLoading">
.. your page content here ..
</div>

Angularjs Bootstrap UI tooltip scope preventing watch from firing

I'm having an issue with a $watch that is really just boggling my brain. I'm almost certain that it is somehow a scope issue. Here's the basics. I'd like a tooltip on a span surrounding a checkbox and it's label so that the tooltip activates on hover over the checkbox or label. something like this.
(note:I've added the {{values.isChecked}} just to watch the values in the two different scopes)
HTML
{{values.isChecked}}
<span tooltip-placement="right" tooltip="This is my tooltip">
<input type="checkbox" id="myCheckbox" ng-model="values.isChecked">
<label for="myCheckbox"> My Checkbox</label> {{values.isChecked}}
</span>
from angular controller
$scope.values.isChecked = true;
$scope.watch("values.isChecked",function(newValue,oldValue){
alert("Made it to the watch");
}
The most odd behavior is that my watch catches the transition of the value from true to false. But does not catch the transition from false to true. I can click it several times and it will make it to the watch going from true to false, but not false to true. If it catches the true to false, and then catches true to false again, well, it HAD to have changed from false to true in order to trigger the watch again. I know the value is actually changing, the two places wehre I added it to the page via {{values.isChecked}}, both show the values change as expected, it's just not firing my watch when I CHECK the box. only when I UNCHECK it.
All my code is on a different box on an isolated network, so I can't actually copy and paste any, so tried to just type in the relevant stuff.
Also if I just take out the Span that has the tooltip on it, it works just fine. I'm aware that bootstrap UI's tooltip does create a new scope. so suspect that, but don't know why it works for one transition, but not the other.
I have even gone as far as capturing the scope inside the tooltip and adding my watch there such as...
myChildScope = angular.element('#myCheckBox').scope()
myChildScope.$watch("values.isChecked",function(newValue,oldValue){
...
It behaves incorrectly, the exact same way. Also behaves the exact same (bad) way if I try to add an ng-click or ng-change to the checkbox element.
Two things to try, I'm not sure how your code is setup but for issues of a watch not catching things when you think it should, generally one of these will work.
$scope.watch("values.isChecked",function(newValue,oldValue){
alert("Made it to the watch");
}, true);
The true tells it to compare for object equality using angular.equals instead of comparing for reference equality. Since it's a boolean primitive... probably more of what you want to use.
The other option, which may or may not help in your case, it to use
$scope.watchCollection("values",function(newValue,oldValue){
alert("Made it to the watch");
});
And see if anything in the values object changes.
Also, you could change isChecked to an object,
$scope.isChecked = { checked: false }
and $watch the object rather than the boolean itself.

When I update my model in ng repeat the directive ng-show does not change

I have a page where I update my model dynamically
<div ng-repeat="action in actions">
<div ng-show="action.showMyDiv">Hello </div>
<div>
when I update my model dynamically from an action in the current page with
$scope.actions[id]['showMyDiv']= true,
the div does not display.
Please , could you help me ?
Thanks in advance .
If you are updating the value manually, you can wrap it in $scope.$apply():
$scope.$apply(function(){
$scope.actions[id]['showMyDiv']= true
})
EDIT:
2 cents on $scope.$apply:
The way angular works out bindings is by doing dirty checking at every render (plenty sources about the process if you search around). As you use a angular default method (ng-click, for instance) a flag is set to warn the app that the page needs "refreshing", and you see the changes happen.
If you change anything manually, you need to warn the app that it needs refreshing, and you can do that with $apply.
See this example for clarification. Notice that two changes are made "manually", at 2s and 4s in, using window.timeout (which you shouldn't use, but rather use the $timeout service), but only after the second one are the changes propagated to the view.

Resources