Angularjs UI-Router state change changes functionality of Google-API directive - angularjs

I am using a google places autocomplete directive: vs-google-autocomplete
which works fine. The problem is, if we change the $state (by back / forward button, or just ui-sref="testroute", or $state.go()), it suddenly does not work the way it worked before.
I set up a plnkr:
google.maps.event.addListener(autocomplete, 'place_changed', function() {
console.log("place_changed");
});
google.maps.event.addDomListener(element[0], 'keydown', function(event) {
if (event.keyCode == 13 || event.keyCode == 9)
console.log("keydown");
});
http://plnkr.co/edit/wbhEWds1pY1bWrguV40U?p=preview
You can enter an address and just hit enter. Then open the console. You will see:
keydown
place_changed
but if you now click on route2 and then back on route1, enter another address and press the enter button, console will log:
place_changed
keydown
The order of the eventhandlers changed. It seems that google search / - functionality is somehow broken, when google is loaded once, then the $state changes and you again try to use google.
Also, there is a second .pac container in the dom, and I guess google is somehow loaded twice or something while the controller just reloads.
Do you know why this behaviour happens and how I can prevent that or do you have at least an idea on how to workaround this problem? I need keydown to be before place_changed. The google search still works in the plnkr, but it is buggy a bit. I think it is a problem with angularjs/$state loading the controller / view and the directive or something.
//edit:
I guess it is not really a problem with the vs-google directive nor google itself, maybe just they way of them being initiated. Vs-google takes the element of the directive and initializes its code. Because google-API is included in the index.html file and a directive loads on state change, it seems that it reinitializes the DOM element or something, but still, the event is just called once

Related

Handling page or tab change in angualajs

How to detect unsaved page changes check while moving another page or tab in angularjs 1.5 application.
One of approach is using directives however how to pass related form named to the directive instead of using hard coded solution?
I tried using the service approach as mentioned below but my nested view controller is not able to access the form name. Iam getting $scope.myForm as undefined.
You can handle page change with the event $locationChangeStart for ng-route or $stateChangeStart for ui-router (perform the logic you want inside):
$scope.$on('$locationChangeStart', function(event) {
if ($scope.myForm.$invalid) {
event.preventDefault();
}
});
To tab change etc, you can disable your tab with something like or watever approach you prefer
ng-disabled="!myForm.$valid"
EDIT
You may look at this post to use a service/factory approach :
https://stackoverflow.com/a/25459689/5138917
The module below seem to work for me
https://github.com/facultymatt/angular-unsavedChanges

Why is $locationChangeStart fired upon page load?

Recently I've stumbled upon a very strange code in production that is seemingly using the fact that under some conditions Angular may fire the $locationChangeStart event upon the initial page load. Moreover the next parameter value will be equal to the current value. That seems very odd to me.
I didn't find any relevant documentation for that but here is the fiddle that shows such a situation http://jsfiddle.net/tJSPt/327/
Probably the only difference is that in production we are using the manual Angular bootstrap.
Can anyone explain or point to the trustful sources of information on why is that event triggered upon the page load? Is that something we have to expect or that is just the particularity of the current Angular implementation or our way of using it?
I have experienced this recently but the reason it happened was because I'm using ui-router and the controllerAs syntax. Perhaps you are too?
I stumbled upon this link that helped me out: History should not be changed until after route resolvers have completed
I listened to the $locationChangeStart broadcast but it hit the breakpoint when I entered the state instead of when exciting.
I fixed mine by doing the following:
I listened to $stateChangeStart instead.
I had to move the code above var vm = this;
Here's my code look like after:
// ...
$scope.$on('$stateChangeStart', function (event) {
if (vm.myForm!= null && vm.myForm.$dirty) {
if (!confirm("Are you sure you want to leave this page?")) {
event.preventDefault();
}
}
});
var vm = this;
// vm.xxx = xxxx; .etc ...

$locationChangeStart never broadcasted; $browser.onUrlChange never called after page load

My angular app is non-standard in that we don't use route provider or UI router. WE use a durandal style of navigation that swaps out views using $http.
locationChangeStart never fires I think due to the fact that we are not changing our URL once in the SPA (though we plan to do so in the future sometimes). I can't test this, but if we remain in the SPA and we change the URL to point to a new view thru code (again, not using routeProvider/UiRouter) should I expect this event to fire?
My main issue is that I cannot handle these events: manual url change, back/forward/history buttons, external link clicks
I see $browser#onUrlChange is the function that broadcasts the $locationChangeStart event but that function only gets called on page load for me, but it seems to be exactly the code that should be getting called.
Documentation:
$browser#onUrlChange
It's only called when the url is changed from outside of angular:
user types different url into address bar
user clicks on history (forward/back) button
user clicks on a link
It's not called when url is changed by $browser.url() method
It says this part also:
NOTE: this api is intended for use only by the $location service. Please use the {#link ng.$location $location service} to monitor url changes in angular apps.
I don't know how to 'use $location service to monitory url changes', so I am listening to the $locationChangeStart event:
$rootScope.$on('$locationChangeStart', function(event, next, current) {
// Breakpoint hit here on page load only
var x = 0;
}
Why doesn't the onUrlChange/$locationChangeStart functions run for me?
thanks!

How come Angular doesn't update with scope here?

I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.
My controller is like this:
/* initialize data */
var fb = new Firebase('https://asdf.firebaseio.com/');
/* set data to automatically update on change */
fb.on('value', function(snapshot) {
var data = snapshot.val();
$scope.propertyConfiguration = data.products;
console.log($scope.propertyConfiguration);
console.log("Data retrieved");
});
/* save data on button submit */
$scope.saveConfigs = function(){
var setFBref = new Firebase('https://asdf.firebaseio.com/products');
setFBref.update($scope.propertyConfiguration);
console.log("configurations saved!");
};
I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")
1) Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.
2) Click to website.com/#Registration, console logs $scope data properly, DOM is loaded correctly.
3) Click back to website.com/#Shared, console logs $scope data properly yet this time DOM loads correctly.
4) Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.
Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.
I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.
FIXED:(sorta)
The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?
Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).
A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.
See this SO Question for a bit more on the topic of digest and $timeout.
This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.
Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.
Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of Object.observe magic to handle these updates.

Webshims - Show invalid form fields on initial load

(Follow on questions from Placeholder Hidden)
I'd like my form to validate existing data when it is loaded. I can't seem to get that to happen
I jQuery.each of my controls and call focus() and blur(), is there a better way than this? I tried to call ctrl.checkValidity(), but it wasn't always defined yet. When it was, it still didn't mark the controls.
I seem to have a timing issue too, while the focus and blur() fire, the UI does not update. It's as if the Webshims are not fully loaded yet, even though this fires in the $.webshims.ready event.
I also tried to call $('#form').submit(), but this doesn't fire the events as I expected. The only way I could make that happen was to include an input type='submit'. How can I pragmatically case a form validation like clicking a submit button would?
Here's a jsFiddle that demonstrates the problem. When the form loads, I want the invalid email to be marked as such. If you click the add button it will be marked then, but not when initially loaded. Why?
Focus and blur in the control will cause it to be marked.
BUT, clicking ADD will too (which runs the same method that ran when it was loaded). Why does it work the 2nd time, but not when initially loaded?
updateValidation : function () {
this.$el.find('[placeholder]').each(function (index, ctrl) {
var $ctrl = $(ctrl);
if( $ctrl.val() !== "" && (ctrl.checkValidity && !ctrl.checkValidity()) ) {
// alert('Do validity check!');
$ctrl.focus();
$ctrl.blur();
}
});
}
I see this in FF 17.0.5. The problem is worse in IE9, sometimes taking 2 or 3 clicks of ADD before the fields show in error. However, I get errors on some of the js files I've liked 'due to mime type mismatch'.
This has to do with the fact, that you are trying to reuse the .user-error class, which is a "shim" for the CSS4 :user-error and shouldn't be triggered from script. The user-error scripts are loaded after onload or as soon as a user seems to interact with an invalid from.
From my point of view, you shouldn't use user-error and instead create your own class. You can simply check for validity using the ':invalid' selector:
$(this)[ $(this).is(':invalid') ? 'addClass' : 'removeClass']('invalid-value');
Simply write a function with similar code and bind them to events like change, input and so on and call it on start.
In case you still want to use user-error, you could do the following, but I would not recommend:
$.webshims.polyfill('forms');
//force webshims to load form-validation module as soon as possible
$.webshims.loader.loadList(['form-validation']);
//wait until form-validation is loaded
$.webshims.ready('DOM form-validation', function(){
$('input:invalid')
.filter(function(){
return !!$(this).val();
})
.trigger('refreshvalidityui')
;
});

Resources