I am experiencing some issues with implementing the angular-google-maps plugin (https://angular-ui.github.io/angular-google-maps/#!/) for the Ionic Framework software in that I cannot get the map to render at all. Crazy thing is that the GoogleMapAPI promise is being triggered (as per alerts that I am placing within there for testing purposes) but no map is rendered to the screen.
My index.html file (in my iOS directory) uses the following file calls:
_assets/_js/_plugins/lodash-2.4.1.min.js"
lib/ionic/js/ionic.bundle.js"
lib/ngCordova/ng-cordova.min.js"
cordova.js"
_assets/_js/_plugins/angular-google-maps-2.0.7.min.js"
_assets/_js/app.js"
_assets/_js/_custom/factories.js"
_assets/_js/_custom/controllers.js"
I have double checked that these files are all present in the locations I have listed in the script src attributes so no problems there.
The Google Map plugin is being loaded/initialised within my controller.js file via the following:
angular.module('sampleAppNameHere.controllers', ["google-maps".ns()])
.config(['GoogleMapApiProvider'.ns(), function (GoogleMapApi) {
GoogleMapApi.configure({
key: 'MY-KEY-HERE',
v: '3.17',
libraries: 'weather,geometry,visualization'
});
}])
Further down in the controller where I want the Google Map to be loaded from I have the following set-up:
.controller('LocationController', ['$scope', '$http', '$stateParams', '$sce', '$ionicLoading', '$ionicPopup', '$timeout', 'Logger'.ns(), 'GoogleMapApi'.ns(), 'RetrieveAppContent', function($scope, $http, $stateParams, $sce, $ionicLoading, $ionicPopup, $timeout, $log, GoogleMapApi, RetrieveAppContent)
{
function parseLocations(locationID, locationName, regionName)
{
// Retrieve factory function to return AJAX loaded data
RetrieveAppContent.retrieveRemoteContentForApp().then(function(locationObj)
{
// Loop through associative array formed from parsed AJAX data into factory method
angular.forEach(locationObj.locations, function(v, k)
{
if(locationID === v.recordID)
{
// Other content in here bonded to scope
$scope.mapLongitude = v.mapLongitude;
$scope.mapLatitude = v.mapLatitude;
$scope.mapZoom = v.mapZoom;
}
});
GoogleMapApi.then(function(maps)
{
// Alerts placed in here are triggered when the template loads
maps.visualRefresh = true;
$scope.map = { center: {latitude: $scope.mapLatitude, longitude: $scope.mapLongitude }, zoom: $scope.mapZoom };
$scope.options = { scrollwheel: false };
});
});
}
}
The parseLocations function is called shortly afterwards in the above controller and the content is all loaded into the template exactly as intended so no problems there BUT the angular map will not render.
The following segment is from the view where the content for the controller is loaded/displayed and where the map is located:
<ion-view title="{{ locationName }}">
<ion-content class="location-panel" ng-controller="LocationController">
<!-- Render Google Map -->
<div id="map-canvas">
<ui-gmap-google-map center="map.center" zoom="map.zoom"></ui-gmap-google-map>
</div>
I have set the following class within the CSS that the app uses:
.map-canvas,
.angular-google-maps-container {
height: 400px;
}
But I see NO rendering of the map only a white space fixed to the above height.
Can anyone offer any suggestions/heads-up on what might or could be causing the non-rendering of the Map and what steps I might be able to take to rectify this?
My thanks in advance for any help that folk might be able to provide.....and my apologies if the code formatting is a bit wonky! :-/
Check your CSS class
.angular-google-maps-container
should be
.angular-google-map-container.
I believe you have it as maps not map.
First of all thanks to Brad for his answer to my question (the heads up on the CSS class name WAS useful for my most recent attempt in using this package!).
I originally solved this, back in November 2014, by writing my own custom directive which provided the Google Map functionality that I needed.
Since then I've had occasion to use the latest version of the AngularJS Google Maps package (angular-google-maps 2.0.12 2015-01-29) and have implemented this without problem (noting Brad's tip on the class name).
Thanks once again.
Related
I have an angular app which has a ng-view which (like any good MVC should) manipulates how the model is shown. The data (model) comes from a database, and I call it into the app's component. From there I want to propagate (if that's the right word) the model into ng-view, which loads a template to display the data based on the route. I also want to be able to filter the data/model that goes into the view with a "top-bar"
I.e:
INDEX.HTML:
<html ng-app="app">
<head>...</head>
<body ng-controller="appController">
<top-bar></top-bar>
<div ng-view></div>
</body>
</html>
APP.JS:
angular.module('app', ['top-bar','view-one','view-two', 'ngRoute']);
angular.module('app').controller('appController', function() {
var self = this;
this.myData = [];
$http.get('theQuery').then(res => self.myData = res.data);
});
angular.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/view-one', {template:'<view-one></view-one>'})
.when('/view-two', {template:'<view-two></view-two>'});
});
angular.module('top-bar', ['ngRoute']);
angular.module('top-bar').component('top-bar', {
templateUrl: './app/top-bar/top-bar.template.html',
controller: function(filterFilter) {
this.filters = filterFilter(...);
}
});
angular.module('view-one', ['ngRoute']);
angular.module('view-one').component('view-one', {
templateUrl: './app/view-one/view-one.template.html',
controller: function(filterFilter) {
// appController.data and topBar.filters would somehow
// need to be gotten from those respective modules.
this.data = appController.data;
this.filter = topBar.filters;
}
});
What I am trying to figure out is how to get the data from the main app's controller (appController) and the top-bar component, and send it to whatever view is currently loaded into ng-view.
I've been searching the web, I cannot find if the better way to do this would be to use binding (i.e. binding: {data:'<'})in the view-one controller/component, a system of $scopes, a custom service or something else. I also can't find out I would accomplish using either one to get the data in there. Thus any answers that also include a) code samples and b) links to further documentation I could read up on would be would be much appreciated.
The recommended way for doing this is to create a service, and let the different controllers work with the reference to the objects provided by the service.
Possible duplicate of enter link description here
I am using goldenlayout with angualrJS. I am facing below exception:
Error: ng:btstrpd App Already Bootstrapped with this Element
on execution of this line of code
myGoldenLayout.on('initialised', function () {
angular.bootstrap(angular.element('#layoutContainer')[0], ['app']);
});
The reason is, I have already ng-app in my HTML so how can I register golden layout when I already have ng-app?
https://github.com/codecapers/golden-layout-simple-angular-example/issues/1
Well, the official Golden Layout docs recommend using manual bootstrap, but if you want to keep using ng-app, then you have to make sure that your components (templates) are compiled by Angular (via $compile). Here's an example of how to do that:
angular.module('someApp') // your main module name here
.run(function ($compile, $rootScope) {
myLayout.registerComponent('template', function( container, state ){
var templateHtml = $('#' + state.templateId).html();
var compiledHtml = $compile(templateHtml)($rootScope);
container.getElement().html(compiledHtml);
});
myLayout.on( 'initialised', function() {
$rootScope.$digest(); // Golden Layout is done, let Angular know about it
});
});
// somewhere...
myLayout.init();
Basically, the main difference from the example in the repository you provided is that instead of just appending raw HTML, we $compile it with Angular, so now it knows to set up bindings and keep the html updated.
This should allow you to keep using ng-app instead of manual bootstrap.
TL;DR;
I've written a program that uses DOM-manipulation and jQuery to respond to the user inputting a comma-separated list of values in a hash-URL and wish to do it in Angular, instead.
The long version
I have been writing a program, on and off, in my spare time, that draws fractal images, such as the famous Mandelbrot fractal. Here's the URL: http://danielsadventure.info/html5fractal/docs/intro.html. I did this as an exercise to flex my HTML5 muscles with features like the canvas element and web workers. The program works great. Here is a rendered image of the "Negabrot" fractal from my program:
Recently, I've been teaching myself Angular, and I decided to rewrite the Javascript using Angular instead of jQuery. Once again, I'm doing this as an exercise.
Angular is, indeed, a very suitable tool because there are lots of forms that the user may use to describe the fractal image to be drawn. I was able to use Angular to bind a model to the forms and get rid of the DOM-manipulation code that I was previously using. Yay! Angular is awesome!
There is another feature of my program that it is not entirely clear how I should convert it to work with Angular. My program is a Single Page Application. There is only one thing it does: draw fractal images. However, I use hash-URLs to keep track of what fractal is being drawn and what configuration is used to draw it. For example, you can follow the URL below to see a zoomed-in section of the Multibrot-5 fractal:
http://danielsadventure.info/html5fractal/index.html#103,0.41000000000000014,1.0299999999999998,0.41999999999999993,1.04,2,1261,true,z%20^%205%20%2B%20c,-2,2,-2,2
As you can see, the URL consists of a list of comma-separated values that describe different aspects of the programs configuration. If you draw something beautiful with it, you can simply send someone else the URL and they can draw the same thing; easy as pie!
In order to accomplish this, I listen for an event that indicates that the hash-URL has changed and respond to it by updating the configuration on the form, once again using old-fashioned DOM-maniputation.
I previously asked on StackOverflow how to respond to hash-URLs, and I was directed to ngRoute. ngRoute looks very useful, but it looks like it is associated primarily with templates and controllers.
In my program, I need not load any additional templates. All I need is to respond to a new hash-URL by updating the configuration and drawing a new fractal. I also want to update the hash-URL with the same when the user manually updates the configuration and draws a new fractal.
In short, what I want to happen is this:
When the user enters a new hash-URL, the program should respond by updating the model that is bound to the inputs so that the form values change.
When the user manually changes the inputs and clicks a button to draw again, the hash-URL should be updated with the new configuration.
With angular ui-router you could do it like this:
angular.module('demoApp', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
//
// For any unmatched url, redirect to /fractal
$urlRouterProvider.otherwise("/fractal?minr=-0.29&maxr=-0.27&mini=-0.64");
//
// Now set up the states
$stateProvider
.state('fractal', {
url: '/fractal?minr&maxr&mini',
templateUrl: 'app/partials/fract.html',
controllerAs: 'fract',
controller: function($stateParams) {
console.log($stateParams);
var fract = new Fractal($stateParams);
this.getSettings = fract.getSettings;
}
});
});
In the url property you can specify your params. I've picked just some of your params.
$stateParams service will inject all the params that are passed in the url.
The following is just to show how I've implemented the Fractal class:
function Fractal(settings) {
var that = this;
this.settings = settings;
this.getSettings = function() {
return that.settings;
}
}
And the partial fract.html looks like this (it only outputs the settings):
<h1>Drawing fractal in this state</h1>
<hr/>
{{fract.getSettings()|json}}
In your app you'll probably create a directive for your fractal class because you're doing DOM stuff. I'm just adding everything in the controller of the state to keep the demo simple.
You can add the directive to the fractal.html partial.
Please have a look at the demo below or in this jsfiddle. Please notice that you're not seeing the url parameters in jsfiddle.
In your app they will be present like in the following screenshot:
angular.module('demoApp', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
//
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/fractal?minr=-0.29&maxr=-0.27&mini=-0.64");
//
// Now set up the states
$stateProvider
.state('fractal', {
url: '/fractal?minr&maxr&mini',
templateUrl: 'app/partials/fract.html',
controllerAs: 'fract',
controller: function($stateParams) {
console.log($stateParams);
var fract = new Fractal($stateParams);
this.getSettings = fract.getSettings;
}
});
});
// here comes your fractal class
function Fractal(settings) {
var that = this;
this.settings = settings;
this.getSettings = function() {
return that.settings;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<div ng-app="demoApp">
<script type="text/ng-template" id="app/partials/fract.html">
<h1>Drawing fractal in this state</h1>
<hr/>
{{fract.getSettings()|json}}
</script>
<div ui-view></div>
<!-- We'll also add some navigation: -->
<a ui-sref="fractal({minr:-0.3, maxr:-0.2, mini: -0.64})">Draw fractal</a>
</div>
ok I am not quite sure what JS code you already have, so I am going to show some snippets you may find helpful!
This is a watch on the URL - so everytime the hash changes this function will be called:
$scope.$on('$locationChangeSuccess', function(event, newState, oldState) {
var values = $location.hash().split(','); // => [103,true,...]
var desc = ['point1', 'boolean']; // I don't know wich values you have..
$scope.values = {}; // values model
angular.forEach(values, function(value, index) {
$scope.values[desc[index]] = value; // {'point1': 103} => nested attribute
});
});
Then you can bind this to a form:
// HTML
<form ng-submit="updateHash()">
<input ng-model="values.point1" />
</form>
// JS
$scope.updateHash = function() {
var updatedValues = [];
angular.forEach($scope.values, function(value) {
updatedValues.push(value);
});
$location.hash(updatedValues); // update URL
};
I am working on an angular application that relies heavily on the client-side and renders most of the app's components through a RESTful API. One of which, is a navigation component which is stored in a JSON file, and is comprised of a list of links that use the ui-router syntax to navigate between states.
So far, I've managed to write a service that builds the nav from a json. Then, using a controller, I'm rendering it to the view using angular's sanitize service. The part I'm stuck at is, the result links appear as hard-coded strings that aren't clickable.
From different threads I read, I assume it's because they aren't compiled and just thrown to the view, but I'm unable to get them to compile / work.
I've seen similar threads such as here and here but they all relay on creating a custom directive for it. I need to render and compile dynamic html (ui-sref links in specific) that are returned from an $http service.
JSFiddle
<div ng-controller="TopNavController as TopNavCtrl">
Output Navigation :
<div ng-bind-html="topnavCtrl.navbar"></div>
</div>
<!-- json file contains something similar to this :
<ul>
<li><a ui-sref="catpage({category: 'products', subcategory: 'whale toys'})" </a>Whale Toys</li>
<li><a ui-sref="catpage({category: 'products', subcategory: 'sharks toys'})"">Shark Toys</a></li>
</ul>
-->
var myApp = angular.module('myApp',[]);
myApp.controller('TopNavController', ['NavbarFactory', '$sce', function(NavbarFactory, $sce) {
var self = this;
self.navbar ="";
NavbarFactory.getnav().then(function(data) {
self.navbar = $sce.trustAsHtml(data);
});
}])
.factory('NavbarFactory', ['$http', function($http) {
return {
getnav: function() {
return $http.get('/path/myjson').then(function(answer) {
var result = answer.data;
return result
},
function(error) {
console.log('Failed');
});
}
}
}]);
I would like to use AngularJS for a single page webapp.
I am concerned if there is an elegant way to "send" different templates based on whether the client is a mobile or desktop.
Is there any way to do it ? Is it recommended that web server "understand" what the browser is and send the view accordingly so the browser always asks for template.html OR you write javascript so the browser will tell webserver to get the mobile/template.html ?
if you wanted to use the same URL but serve two different sets of HTML (say swap out large images and inpage videos for something else) I would do something like this
'use strict';
angular.module('MyApp', []).config(function ($routeProvider) {
// Magic sauce, imediate so the value is stored and we don't need to lookup every check
var _isNotMobile = (function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
return !check;
})();
// Swap out different HTML because you want to say, hide a video etc.
$routeProvider
.when('/', {
templateUrl: (_isNotMobile )? 'views/MY_DESKTOP_VIEW.html':'views/m/MY_MOBILE_VIEW.html',
controller: (_isNotMobile )?'MyHomeCtrl':'MyMobileCtrl'
})
.otherwise({
redirectTo: '/'
});
});
How I would go about with this is to display one template to the user and make the template Responsive. Just because you are using AngularJS templates, I do not see a reason why you would not want to make the template responsive. I would not go for the solution that involves displaying a different template to the user based on the device browser.
That said, one way that I would do is:
To have a simple script for the home / landing page of the web application that determines the browser / device. This can be found here.
Next, depending on the browser / device, you redirect the user to a different route
Have different routes based on the browser / device type - display a different template based on the route and thus identify if it s a mobile device or not based on the route.
The last step would be something like:
angular.module('myApp', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
//Display desktop version
when('/desktop/homePage', {
//Template for Desktop based browsers
templateUrl: 'partials/desktop/home-page.html'
}).
//Display mobile version
when('/mobile/homePage', {
//Template for Mobile based browsers
templateUrl: 'partials/mobile/home-page.html'
}).
otherwise({redirectTo: '/desktop/homePage'});
}]);
Responsive design will work well for smaller apps but gets rather messy when you move to bigger applications.
I'd personally suggest detecting the user agent on page load, and redirecting him to a separate mobile app if needed. You can still use most of your code base for both apps (simply import individual modules).
If your interested in detecting the user agent using javascript I suggest this solution (simply select javascript): http://detectmobilebrowsers.com/ the most extensive solution I've found so far
use boostrap. and it could be easily done.
<!-- Display Only Screen > Big -->
<div class="hidden-xs">
<div ng-view class="section" ng-class="animate"></div>
</div>
<!-- Display Only Screen < Small -->
<div class="visible-xs">
<div ng-swipe-right="openSlide()">
<div ng-view class="section" ng-class="animate"></div>
</div>
</div>
and in landing.html
<div class="visible-xs" ng-include="'{{template path}}/desktop.html'" ></div>
<div class="hidden-xs" ng-include="'{{template path}}/mobile.html'" ></div>
ANd in config
$routeProvider
.when(/,{
templateUrl : "landing.html"
controller : "landingCtrl"
});
it works floawlessley for me. Its not of the way. maybe there could be another using pure JS. This just happened pout of the box since i am using boostrap and leverage it to my advantage.
A bit late, but in something like your header, or nav controller, you could set the initial width:
angular
.module('myApp')
.controller('navCtrl', ['$rootScope', '$window',
function($rootScope, $window) {
$rootScope.is_mobile = ($window.innerWidth < 480);
And if you want checking on resize, go ahead and bind it:
angular.element($window).bind('resize', function() {
$scope.$apply();
});
Then watch it:
$scope.$watch(function () {
return $window.innerWidth;
}, function (innerWidth) {
$rootScope.safeApply(function () {
$rootScope.is_mobile = innerWidth < 480 // went with max device width
});
});
Then in your HTML:
<div ng-if="$root.is_mobile">Show me only in mobile</div>
BOOSTRAP + ANGULARJS solution to this problem:
You can check out the angular-match-media library. It is extremely small in size but very helpful and elegant.
https://github.com/jacopotarantino/angular-match-media
**Watch out: This is installed with bower install angular-media-queries; however, the path to the js file is /path/to/library/angular-media-queries/match-media