Ionic (or AngularJS) blocks google maps autocomplete - angularjs

I'm just trying to add a simple autocomplete form to my Ionic app.
So first I tried this, it works fine.
I then tried in my app (in the browser first).
I put this in my controller, and called it with ng-init="initMaps()"
$scope.initMaps = function() {
var center = new google.maps.LatLng(51.514032, -0.128383);
var circle = new google.maps.Circle({
center: center,
radius: 50
});
var options = {componentRestrictions: {country: 'uk'}, types: ['geocode']}
var input = document.getElementById('autocomplete');
var autocomplete = new google.maps.places.Autocomplete(input, options);
autocomplete.setBounds(circle.getBounds());
console.log("maps loaded")
}
When I look at my console, I can see "maps loaded", and google warnings like:
Google Maps API warning: SensorNotRequired https://developers.google.com/maps/documentation/javascript/error-messages#sensor-not-required
I just added this line in my template form:
<input id="autocomplete" type="text" style="width: 200px;">
So, I think on a non-ionic (or angular) project it would work fine. But here, when I enter some letters, I don't get any result
Does Angular/Ionic create some interference here ? Am I forced to install other modules ?
Thanks !

Since Place Autocomplete is a part of Google Maps Places Library you probably forgot to include the loading of this library via libraries parameter, for example:
<script src="//maps.googleapis.com/maps/api/js?key={KEY}&libraries=places"></script>
Example
angular.module('ionic.example', ['ionic'])
.controller('MapCtrl', function ($scope) {
$scope.initMap = function () {
var center = new google.maps.LatLng(51.514032, -0.128383);
var circle = new google.maps.Circle({
center: center,
radius: 50
});
var options = { componentRestrictions: { country: 'uk' }, types: ['geocode'] }
var input = document.getElementById('autocomplete');
var autocomplete = new google.maps.places.Autocomplete(input, options);
autocomplete.setBounds(circle.getBounds());
}
google.maps.event.addDomListener(window, 'load', $scope.initMap);
});
.controls {
margin-top: 10px;
border: 1px solid transparent;
border-radius: 2px 0 0 2px;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 32px;
outline: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
<script src="//maps.googleapis.com/maps/api/js?libraries=places"></script>
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
<div ng-app="ionic.example" ng-controller="MapCtrl">
<ion-header-bar class="bar-dark" >
<h1 class="title">Map</h1>
</ion-header-bar>
<ion-content>
<div id="map" data-tap-disabled="true"></div>
<input id="autocomplete" placeholder="Enter your address" class="controls" type="text"></input>
</ion-content>
</div>
Codepen

Related

ngRepeat strange behaviour when its elements order change

I am learning angularjs and I came across some behaviour that I can't understand. I render a list of items in ng-repeat, each has a button to remove itself from the $scope's array - this works as expected but once I added a sortable to the list strange things started to happen.
Here is the running example showing the issue:
https://embed.plnkr.co/eQWcxZ7p8CcACfk6Z53X/
In the example - if I move [^] item 4 (Rocket Launcher) to position 1 and use Rocket Launcher's delete [X] button the list gets updated (i.e. item 4 - Rocket Launcher - that was on position 1 is removed) but the other items delete buttons stop working. Basically moving items and deleting them somehow break the bindings (?).
The code:
(function() {
'use strict';
var app = angular.module('myApp', []);
app.controller('myAppController', function($scope) {
$scope.boxes = [];
$scope.removeBoxItem = function(box, item) {
console.log('Removing "' + item.name + '" form box "' + box.name + '"...');
var index = box.items.indexOf(item);
box.items.splice(index, 1);
console.log(box);
};
this.init = function() {
var e;
e = new Box({
name: 'Red box'
});
$scope.boxes.push(e);
e.items.push(new Item({
index: 1,
name: 'Rock'
}));
e.items.push(new Item({
index: 2,
name: 'Scissors'
}));
e.items.push(new Item({
index: 3,
name: 'Paper'
}));
e.items.push(new Item({
index: 4,
name: 'Rocket launcher'
}));
e = new Box({
name: 'Green box'
});
e.items.push(new Item({
index: 1,
name: 'Chuck the Plant'
}));
e.items.push(new Item({
index: 2,
name: 'Hamster'
}));
e.items.push(new Item({
index: 3,
name: 'Tentacle Chow'
}));
$scope.boxes.push(e);
};
this.init();
});
app.directive("sortable", ["$timeout", function($timeout) {
return {
template: '<div class="sortable" ng-transclude></div>',
transclude: true,
scope: {
'handle': '#'
},
link: function(scope, element, attrs) {
$timeout(function() {
//console.log(element)
var sortable = element.find('> div');
console.log(sortable[0]);
scope.sortable = Sortable.create(sortable[0], {
handle: scope.handle || null
});
});
}
};
}]);
}());
function Box(args) {
this.name = args.name || null;
this.items = [];
}
function Item(args) {
this.index = args.index || null;
this.name = args.name || null;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css" />
<style>
.container {
border: 1px solid #929292;
padding: 5px;
}
.header {
font-size: 1.2rem;
padding: 10px 15px;
background-color: #F5F5F5;
border-bottom: 2px solid #E2E2E2;
}
.body {
background-color: #F2F2F2;
padding: 10px 10px;
margin-bottom: 10px;
}
.body .item {
border: 1px solid #D2D2D2;
padding: 5px;
margin-bottom: 5px;
}
.body .options {
float: right;
}
.body .options .delete {
cursor: pointer;
}
.body .options .handle {
cursor: move;
}
.debug {
margin-top: 20px;
border-top: 1px dotted #929292;
}
</style>
</head>
<body>
<div ng-app="myApp" ng-controller="myAppController as appCtrl">
<div class="container">
<div ng-repeat="box in boxes">
<div class="header">{{ box.name }}</div>
<div class="body">
<div data-sortable="" data-handle=".handle">
<div class="item" ng-repeat="item in box.items">
{{item.index }}) {{ item.name }}
<div class="options">
<span ng-click="removeBoxItem(box, item)" class="delete">[X]</span>
<span class="handle">[^]</span>
</div>
</div>
</div>
</div>
</div>
<div class="debug">
<pre>{{ boxes | json }}
</pre>
</div>
</div>
</div>
<script data-require="jquery#3.1.1" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script data-require="Sortable.js#1.6.0" data-semver="1.6.0" src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.6.0/Sortable.js"></script>
<script data-require="angular.js#1.6.6" data-semver="1.6.6" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.js"></script>
<script src="script.js"></script>
</body>
</html>
I am confused by this, in my real code things get even more broken - I made a collapsible directive that surrounds the "Boxes" so I can open and manipulate one at a time - in that case moving item 4 to position 1 and deleting it removes all items from the view (but not from the $scope). Than adding new item to the $scope's array causes items to correctly reappear. For now I created simpler version as I guess this is all somehow connected.
I am aware the sortable should set the objects indexes etc but for now I'd like to understand what is happening. I suspect I have some issues with understanding what is going on with scopes (?). Would be grateful for any help.

object-fit fallback solution? (AngularJS&ng-repeat)

I've got an interesting compatability issue. Of course, object-fit: cover will not work on MS Edge and IE, so I've been trying to find a workaround for it. However, I can't seem to access an image created using ng-repeat via javascript.
I want to be able to edit the image styles from the controller, but querySelector('img') always returns null.
Heres the HTML:
<div class="js-image" id="danCarousel" style="height: 600px; width: 100%; background-color: grey;">
<!-- carousel image -->
<img ng-show="loaded" class="cimg {{fadeAnimation}}" ng-repeat="slide in slides"
ng-if="isCurrentSlideIndex($index)" ng-src="{{slide.src}}" style="position: absolute;
height: 100%; width: 100%; object-fit: cover; float: left; "/>
And here's the Javascript:
danApp.controller('indexController', function($scope, $document, $timeout, QueueService) {
$scope.state='index';
if('objectFit' in document.documentElement.style === false) {
console.log("This browser does not support object-fit");
//get the carousel image container
var carouselImages = document.getElementsByClassName('js-image');
for(var i=0; i < carouselImages.length; i++) {
console.log(i);
//get the image source url
var imageSrc = carouselImages[i].querySelector('img').getAttribute("ng-src");
console.log(carouselImages[i].querySelector('img'));
//hide the image
carouselImages[i].querySelector('img').style.display = "none";
//add background-size: cover
carouselImages[i].style.backgroundSize = 'cover';
//add in the background image src here
carouselImages[i].style.backgroundImage = 'url(' + imageSrc + ')';
//Add background-position: center center
carouselImages[i].style.backgroundPosition = 'center center';
}
console.log("Compatability settings added.");
}
else {
console.log('This browser supports object-fit');
}
Anyone know of a solution for this?
Use ng-class and either call a function in your controller to return the appropriate class name or add a class property to slide and reference it via ng-class.

Google Polymer - Iron Pages/Selector not updating main field

I'm cutting my teeth with Google Polymer by trying to put together a control panel style layout but unfortunately when clicking on items in my app-drawer the content doesn't load in the main field. The url updates in the bar and the CSS highlights the selection but nothing else happens.
I started with a blank app template using polymer init, tried to flesh out the structure by referencing this answer, and then referenced the starter-kit as a guide for the rest. If anyone could give me a nudge in the right direction it would be greatly appreciated as i'm still new to the concepts of routing.
<dom-module id="Panel-app">
<template>
<style>
:host {
display: block;
font-family: Fira Sans Condensed;
--app-primary-color: #1A3663;
}
app-header {
background-color: var(--app-primary-color);
color: white;
}
app-drawer {
top: 64px;
--app-drawer-content-container: {
padding: 0px;
background-color: #325999;
};
}
.drawer-list {
margin: 0 20px;
}
.drawer-list a {
display: block;
padding: 0 16px;
text-decoration: none;
color: var(--app-secondary-color);
line-height: 40px;
}
.drawer-list a.iron-selected {
color: black;
font-weight: bold;
}
</style>
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"></app-route>
<app-header-layout fullbleed>
<app-header shadow>
<app-toolbar id="toolbar">
<paper-icon-button icon="menu" onclick="drawer.toggle()"></paper-icon-button>
<div main-title>Panel</div>
<paper-menu-button>
<paper-icon-button icon="communication:live-help" class="dropdown-trigger"></paper-icon-button>
<paper-menu class="dropdown-content">
<paper-item>Live Chat</paper-item>
<paper-item>Contact List</paper-item>
<paper-item>Leave Feedback</paper-item>
</paper-menu>
</paper-menu-button>
<paper-icon-button icon="supervisor-account" class="dropdown-trigger"></paper-icon-button>
<paper-icon-button icon="settings" class="dropdown-trigger"></paper-icon-button>
</app-toolbar>
</app-header>
<app-drawer-layout>
<app-drawer id="drawer">
<iron-selector selected="[[page]]" attr-for-selected="name" class="drawer-list" role="navigation">
<a name="view1" href="/view1">One</a>
<a name="view2" href="/view2">Two</a>
<a name="view3" href="/view3">Three</a>
</iron-selector>
</app-drawer>
<iron-pages
selected="[[page]]"
attr-for-selected="name"
fallback-selection="view404"
role="main">
<my-view1 name="view1"></my-view1>
<my-view2 name="view2"></my-view2>
<my-view3 name="view3"></my-view3>
</iron-pages>
</app-drawer-layout>
</app-header-layout>
</template>
<script>
Polymer({
is: 'Panel-app',
properties: {
page: {
type: String,
reflectToAttribute: true,
observer: '_pageChanged',
},
},
observers: [
'_routePageChanged(routeData.page)',
],
_routePageChanged: function(page) {
this.page = page || 'view1';
if (!this.$.drawer.persistent) {
this.$.drawer.close();
}
},
_pageChanged: function(page) {
// Load page import on demand. Show 404 page if fails
var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');
this.importHref(resolvedPageUrl, null, this._showPage404, true);
},
_showPage404: function() {
this.page = 'view404';
},
});
</script>
</dom-module>
You're missing a curly bracket here or else the binding fails:
route="{{route}"
It must be
route="{{route}}"
Then the _routePageChanged observer works

How to detect multi finger touch in ionic app

I am developing an Ionic app.In that I want to apply 1 finger swipe and 2 finger swipe and 3 finger swipe ( If it is possible ).
In a div if user swipes with single finger, it should scroll and
If user swipes with multi finger , it should select the content and select,copy options should be shown and 3 finger swipe for one more action.
edit:
I checked the question before posting this question.I am able to detect multi touch but not 2finger / 3 finger swipe. I am looking for any plugins for this actions.
Help me in this issue.
Look how 4-touch for reload is implemented in the Phonegap Developer App:
var currentTouches = {},
eventName = { touchstart: 'touchstart', touchend: 'touchend' };
if (window.navigator.msPointerEnabled) {
eventName = { touchstart: 'MSPointerDown', touchend: 'MSPointerUp' };
}
document.addEventListener(eventName.touchstart, function(evt) {
var touches = evt.touches || [evt],
touch;
for(var i = 0, l = touches.length; i < l; i++) {
touch = touches[i];
currentTouches[touch.identifier || touch.pointerId] = touch;
}
});
document.addEventListener(eventName.touchend, function(evt) {
var touchCount = Object.keys(currentTouches).length;
currentTouches = {};
if (touchCount === 4) {
evt.preventDefault();
window.location.reload(true);
}
}, false);
Hope this helps.
I am not quite sure but you might try adding a jQuery plugin
jGestures that enables you to add gesture events such as 'pinch', 'rotate', 'swipe', 'tap' and 'orientationchange' just like native jQuery events. Includes event substitution: a "tapone" event can be triggered by "clicking", a "swipe" by performing a swipe-ish mousegesture.
try this one for more https://codepen.io/edisonpappi/pen/LyqrXw
angular.module('ionicApp', ['ionic'])
.controller('MainCtrl', function($scope, $ionicGesture) {
$scope.messages = [];
var touchpad = angular.element(document.querySelector('#touchpad'));
var maxFingers = 10;
var fingers = [];
for(var i=0; i<maxFingers; i++) fingers.push(angular.element(document.querySelector('#t'+i)))
$scope.touches = new Array;
var resetTransformations = function(){
for(var i=0;i<maxFingers;i++)
fingers[i].css({"transform": "translate3D(0px, 0px, 0px)"});
}
$ionicGesture.on('dragstart',
function (event) {
$scope.messages.push({txt: "dragstart"});
}, touchpad);
$ionicGesture.on('dragend',
function (event) {
$scope.messages.push({txt: "dragend"});
}, touchpad);
$ionicGesture.on('transformstart',
function (event) {
$scope.messages.push({txt: "transformstart"});
}, touchpad);
$ionicGesture.on('transformend',
function (event) {
$scope.messages.push({txt: "transformend"});
}, touchpad);
// keep track if a finger is released:
var fingersDown = 0;
// drag = 1 finger, transform = more
$ionicGesture.on('transform drag',
function (event) {
if(fingersDown > event.gesture.touches.length)
resetTransformations();
fingersDown = event.gesture.touches.length;
$scope.touches = [];
var identifier = 0;
for(var i=0; i<event.gesture.touches.length; i++){
identifier = parseInt(event.gesture.touches[i].identifier);
$scope.touches[i] = {pageX: parseInt(event.gesture.touches[i].pageX), pageY: parseInt(event.gesture.touches[i].pageY), id: identifier};
fingers[identifier].css({"transform": "translate3D("+event.gesture.touches[i].pageX+"px, "+event.gesture.touches[i].pageY+"px, 0px)"});
}
$scope.$apply();
}, touchpad);
$ionicGesture.on('release',
function (event) {
$scope.touches = [];
$scope.$apply();
$scope.messages.push({txt: "release"});
resetTransformations();
}, touchpad);
$ionicGesture.on('touch',
function (event) {
$scope.messages.push({txt: "touch"});
}, touchpad);
});
body {
cursor: url('http://ionicframework.com/img/finger.png'), auto;
}
.full{
width: 100%;
height: 100%;
}
h4.full{
color: #FFF;
position: absolute;
top: 0px;
left: 0px;
z-index: 10;
}
.debug{
color: #fff;
padding-left: 10px;
font-size: 12px;
line-height: 12px;
}
.circle{
position: absolute;
color: #fff;
top: -44px;
left: 0px;
margin-left: -30px;
margin-top: -30px;
width: 60px;
height: 60px;
border-radius: 30px;
background: rgba(255,255,255,0.62);
}
#container{
padding-top: 38%;
z-index: 1;
position: absolute;
background: url("https://unsplash.imgix.net/46/yzS7SdLJRRWKRPmVD0La_creditcard.jpg?q=75&fm=jpg&s=7cc01cbdd45363cc0e2e8340b185bca2");
background-size: cover;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
}
<html ng-app="ionicApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>VELOCITY + IONIC</title>
<!-- add touch emulation hold SHIFT-key while using the mouse -->
<script src="http://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script>
<script>TouchEmulator();</script>
<link href="//code.ionicframework.com/1.0.0-beta.13/css/ionic.css" rel="stylesheet">
<script src="//code.ionicframework.com/1.0.0-beta.13/js/ionic.bundle.js"></script>
</head>
<body ng-controller="MainCtrl">
<ion-header-bar class="bar-positive">
<h1 class="title">TRACKING MANY FINGERS</h1>
</ion-header-bar>
<ion-content class="full" scroll="false">
<h4 id="touchpad" class="full padding">Drag below with > 1 fingers.<br />Press and hold Shift + mouse key to emulate multitouch on a computer.</h4>
<div class="full world padding">
<div id="container">
<div class="debug" ng-repeat="touch in touches">
touches[{{$index}}] = FINGER <strong>{{touch.id}}</strong>: ({{touch.pageX}}, {{touch.pageY}})
</div>
<div class="debug" ng-repeat="msg in messages">{{msg.txt}}</div>
<div id="t0" class="circle">0</div>
<div id="t1" class="circle">1</div>
<div id="t2" class="circle">2</div>
<div id="t3" class="circle">3</div>
<div id="t4" class="circle">4</div>
<div id="t5" class="circle">5</div>
<div id="t6" class="circle">5</div>
<div id="t7" class="circle">7</div>
<div id="t8" class="circle">8</div>
<div id="t9" class="circle">9</div>
</div>
</div>
</ion-content>
</body>
</html>

AngularJS directives with HTML5 drag and drop -- issue with scope object

I'm fairly new to angular and I'm having a hard time wrapping my head around where the items are being pushed to. I am not sure if I am correctly setting up the functions to be used with drag/drop and if its getting bound to an older scope object and the ng-repeat isn't being updated properly. I'm thinking there is some slight issue with the way I have this setup. Any pointers or help would be much appreciated.
What should happen is when you drag a color from the Draggable container into the Droppable container it should update the text which is linked to the scope object items. I am successfully pushing an item onto the scope object but ng-repeat isn't picking it up. I am not sure if I need a watch or what to do to get it to pay attention to the newly added items.
JS Fiddle Here: http://jsfiddle.net/RV23R/
HTML CODE:
<div ng-app="my-app" ng-controller="MainController">
<div class="container">
<header><h1>Draggables</h1></header>
<section>
<div draggable="true" ng-repeat="drag_type in drag_types">{{drag_type.name}}</div>
</section>
</div>
<div class="container">
<header><h1>Drop Schtuff Here</h1></header>
<section droppable="true">
<div><span>You dragged in: </span><span ng-repeat="items in items">{{item.name}},</span></div>
</section>
</div>
ANGULAR CODE:
var module = angular.module('my-app', []);
module.directive('draggable', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element[0].addEventListener('dragstart', scope.handleDragStart, false);
element[0].addEventListener('dragend', scope.handleDragEnd, false);
}
}
});
module.directive('droppable', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element[0].addEventListener('drop', scope.handleDrop, false);
element[0].addEventListener('dragover', scope.handleDragOver, false);
}
}
});
function MainController($scope)
{
$scope.drag_types = [
{name: "Blue"},
{name: "Red"},
{name: "Green"},
];
$scope.items = [];
$scope.handleDragStart = function(e){
this.style.opacity = '0.4';
e.dataTransfer.setData('text/plain', this.innerHTML);
};
$scope.handleDragEnd = function(e){
this.style.opacity = '1.0';
};
$scope.handleDrop = function(e){
e.preventDefault();
e.stopPropagation();
var dataText = e.dataTransfer.getData('text/plain');
$scope.items.push(dataText);
console.log($scope.items);
};
$scope.handleDragOver = function (e) {
e.preventDefault(); // Necessary. Allows us to drop.
e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object.
return false;
};
}
CSS (if anyone cares)
.container {
width: 600px;
border: 1px solid #CCC;
box-shadow: 0 1px 5px #CCC;
border-radius: 5px;
font-family: verdana;
margin: 25px auto;
}
.container header {
background: #f1f1f1;
background-image: -webkit-linear-gradient( top, #f1f1f1, #CCC );
background-image: -ms-linear-gradient( top, #f1f1f1, #CCC );
background-image: -moz-linear-gradient( top, #f1f1f1, #CCC );
background-image: -o-linear-gradient( top, #f1f1f1, #CCC );
box-shadow: 0 1px 2px #888;
padding: 10px;
}
.container h1 {
padding: 0;
margin: 0;
font-size: 16px;
font-weight: normal;
text-shadow: 0 1px 2px white;
color: #888;
text-align: center;
}
.container section {
padding: 10px 30px;
font-size: 12px;
line-height: 175%;
color: #333;
}
There are a couple of typos in the fiddle, but the basic problem is that your drag events are outside an angular digest cycle. You should wrap your changes in $scope.$apply (code sample coming). This forked and bugfixed (FIDDLE) shows that when you click the button, angular shows the changes and refreshes the display with new values.
Fix: (FIDDLE)
$scope.$apply(function() {
$scope.items.push(dataText);
});
A bug you had is in this code:
<span ng-repeat="items in items">{{item.name}},</span>
This should probably be ng-repeat="item in items", also items only contains the dropped text so it is an array of strings and not the original item objects.

Resources