Lazy load google maps before constructing class in angular factory - angularjs

I have a module that uses google maps to make a map, get the users location and place a marker on the map where they are. I wanted a custom animated marker so I had to make a factory initializing a custom OverlayView Class. I also wanted the map loaded asynchronously so I created a factory like this
to handle loading google maps.
I can load the map fine by doing this:
initializer.gMapReady.then(function(){
//call service method to make map
});
But I dont know how to use my initializer for my OverlayView factory that looks like this:
.factory('locMarker', function() {
LocationMarker.prototype = new google.maps.OverlayView(); // This returns the error, google is not defined
function LocationMarker(opts) {
this.setValues(opts);
}
LocationMarker.prototype.draw = function() {
//
// Code for drawing the marker on the map
//
}
};
return LocationMarker;
})
Whenever I load the page the map shows up fine but my custom marker never displays. I get a reference error, google is not defined, that points to the line LocationMarker.prototype = new google.maps.OverlayView(); If I don't lazy load google maps and just add the script to the index page everything runs fine.
Is there a way for me to call my maps initializer from within the locMarker factory so that google maps is loaded before it tries to construct the class?
Update:
As estus mentioned in the comments, I could use a resolver in the states/routes but I am trying to make this module not require that if possible.

Related

How to use Yandex.Metrika in Meteor+React app?

I have a Meteor app, all UI components are built with React. I use FlowRouter for routing.
Now I want to add analytics with Yandex.Metrika service.
I found this package for React: https://github.com/narkq/react-yandex-metrika
But I how I have to use it? For what reason do I need <YM /> component from this example?
import {Initializer as YM} from 'react-yandex-metrika';
// Use the initializer to add the script to your page somewhere.
var MyComponent = React.createClass({
render: function() {
return (
<div>
// SNIP
<YM />
// SNIP
</div>
);
}
});
And where should I initialize the tracker object? I read this:
// This is supposed to be executed only in browser and only once.
// Because of that, the most sensible place for this code is right after you javascript bundle.
ym.init([987654321]);
But what is javascript bundle and where should I place my im.init(id) code?
Actually all what I need is to have funsctions to send data to Metrika, such as hit (pageview analog rom ga), reachGoal and so on.
thank you for your answers!
For what reason do I need component from this example?
You need it to load metrika's main code
How metrika works:
webmaster (you) inserts small piece of js code (loader) to all pages. this code append async script with main code (it's a bit bigger than loader) and create instance of metrika object ('counter') - new Ya.Metrika(...params). Instance will be available in global variable named yaCounterXXXXX, where XXXXX is your counter's id.
So, <YM /> component is loader from previous paragraph.
Actually all what I need is to have funsctions to send data to
Metrika, such as hit (pageview analog rom ga), reachGoal and so on.
There is doc about that at the bottom of readme. But I don't see filter by counter id for that methods. Maybe I make a pr to add this functionality. In any case you can use global variable yaCounterXXXXX like this yaCounterXXXXX.hit(url, params) or yaCounterXXXXX.reachGoal(goalId, params)
I hope I helped you.

Getting hold of loaded javascript objects in Protractor

In our application we load requirejs, which in return loads angularjs, and also other javascript modules. I am wondering if there any way to get hole of these LOADED modules (angularjs, javascript modules) in protractor test. Note, we want the instance that is loaded by the browser when running Protractor, we don't want to create instance by ourselves.
Any suggestion or example?
Thanks in advance.
Nick Tomlin's answer is what you can do if a module returns serializable data structure as a value. You call require and call with the module's value the callback that executeAsyncScript gives you to allow returning asynchronous values. This will work, for instance, if your module returns "foo" or { foo: 'bar' } or structures that are generally serializable.
However, it won't always work. Complex modules cannot be retrieved that way. Roughly speaking you should expect what you send through executeScript and executeAsyncScript and what they return to have the same limitations as JSON.stringify does. One major exception is that Selenium will wrap DOM objects returned from these calls into a structure that allows to identify them on the script side, and that allows passing them back to the browser. (Then again, there are limitations there too. This is why you get stale element exceptions, for instance.)
If you try to retrieve modules that export functions, you'll probably get something but it won't be complete. Try this, for instance:
browser.executeAsyncScript(function () {
arguments[0]({ foo: function () {}});
}).then(function (value) {
console.log(value);
});
The output I get is:
Object { foo: Object {} }
The function has been turned into an empty object.
I do not use angular with require.js, but i'm assuming you could access the require'd angular the same way you would in a module:
var pageAngular = browser.driver.executeAsyncScript(function () {
var callback = arguments[arguments.length - 1];
require(['angular'], function (angular) {
callback(angular);
})
});
The use of executeAsync is necessary here, since AMD modules are loaded asynchronously.
Do note that as #louis noted, the return of executeAsyncScript is going to be a serialized object, not the "live" instance of angular. If you need to interact with angular within the context of your page, you should do so within the callback torequire.
Something like this should do it:
var angular = browser.driver.executeScript("return window.angular;");

Is it possible to define an angularjs constant inside a controller or config block?

I am trying to set up some app wide constants from a http endpoint. I don't want (or need ) to do it via the manual bootstrap way (ala this ). Ideally I want to load the constants in after the user has signed in. So I assume I need to define the constants somewhere I can run some code (and use $http). But whenever I define a constant inside a controller or a config block, the constant gives an unknown provider error when I pass it into another module. As soon as I move the definition outside of the controller it works. So for example, if I have ...
var app = angular.module('testApp',[]);
app.constant('test', 'test value');
then I can pass that into another module's controller like this:
var app2 = angular.module('anotherModule',[testApp]);
app2.controller('TestCtrl', ['test',
function(test) {
console.log(test)`
}..
and this will output 'test value' as you would expect. But if the constant is defined inside a code block then it seems it doesn't register as a provider. So, for example:
var app = angular.module('testApp',[]);
app.controller('firstCtrl',function(){
app.constant('test', 'test value');
}
If I run a page with that controller, the constant seems to register (in the sense that it is listed inside the _invokeQueue array on the testApp module) but the injector service doesn't have a provider for it and I get an unknown provider method for it on anotherModule.
I initially felt that a factory or service was overkill for what I was trying to do but maybe that is the way I should go. But I would also love to understand why a constant defined like this isn't injectable.
Easiest way to load constants for HTML5 app:
add the following line into head section:
< script src="service/constants.js">
create web api / wcf / java REST service method with signature "service/constants.js"
in that method return "window.constants = {...}".
replace "..." with actual key:value pairs read from database.

Node Webkit / Angular / Change Model

Sorry for a not very specific question by someone new to node webkit, new to Angular, new to about everything in web development:
My app is based on a JSON file that I load at the init of my node webkit app and which is at the center of a bunch of calculations.
In the app, one can open a file dialog to create a new JSON file. Now, of course, I would like the app to recalculate everything based on the new JSON. It works when I press the "refresh" button of node webkit, but I couldn't get it running by using either
require('nw.gui').Window.open('index.html');
nor
require('nw.gui').Window.get().reload(3);.
I am also wondering if handling this on the node level is the good way to do it. Shouldn't it rather be done by Angular? But I couldn't really connect to the content of my controller from an "outside" javascript.
Grateful for any hint...
Having logic on the page loading is always tricky and as you mentioned - requires page reloading what is not very elegant and modern applications avoid this.
In your case, I suggest that if your JSON file is not very big - store it in variable and modify it as needed. The elegant way will be to create Angular service, which can act as a "model".
angular.service('JsonService', function() {
var json = {
// content
};
return {
getJson: function () {
return json;
},
setJson: function (newJson) {
json = newJson;
}
};
});
Then, whenever you need to update JSON invoke setJson(newJson) method and modify your controllers to use the service getJson() method.
You can also add the loading/saving to file functions to this service. The loading function can be invoked in your main controller connected to your dashboard page. Then before the first page will be visible, the JSON file will be already loaded and you preserve desired behavior.

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

Resources