$ionicPosition.offset and $ionicPosition.position causing "TypeError: Cannot read property 'getBoundingClientRect' of undefined" - ionic-framework

I'm trying to get the position and offset of a dragged list item on release.
However, both $ionicPosition.offset and $ionicPosition.position both return "TypeError: Cannot read property 'getBoundingClientRect' of undefined.
I've included a rudimentary code snippet. Thanks in advance!
var data = {
"week": [{
"name": "Monday",
"activities": [{
"name": "Go running",
"shortname": "go_running"
}, {
"name": "Wash clothes",
"shortname": "wash_clothes"
}]
}, {
"name": "Tuesday",
"activities": [{
"name": "Holiday shopping",
"shortname": "holiday_shopping"
}, {
"name": "Clean bike",
"shortname": "clean_bike"
}]
}]
}
var app = angular.module('activityplan', ['ionic']);
app.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if (window.cordova && window.cordova.plugins.Keyboard) {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
// Don't remove this line unless you know what you are doing. It stops the viewport
// from snapping when text inputs are focused. Ionic handles this internally for
// a much nicer keyboard experience.
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
});
});
app.controller('PlanController', ['$scope','$ionicPosition', function($scope,$ionicPosition) {
$scope.days = data.week;
$scope.releaseTest = function(a) {
console.log($ionicPosition.offset(a));
console.log($ionicPosition.position(a));
}
$scope.moveActivity = function(activity, day, fromIndex, toIndex) {
day.activities.splice(fromIndex, 1);
day.activities.splice(toIndex, 0, activity);
};
}]);
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>Activity plan</title>
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
</head>
<body ng-app="activityplan">
<ion-content ng-controller="PlanController" style="width:360px;">
<div ng-repeat="day in days">
<div class="item item-divider item-dark">
<b>{{ day.name }}</b>
</div>
<ion-list ng-controller="PlanController" show-reorder="true" can-swipe="true">
<ion-item class="item-icon-right" ng-repeat="activity in day.activities track by $index" on-release="releaseTest(activity)">
{{ activity.name }}
<ion-option-button class="button-assertive icon ion-trash-a" ng-click=""></ion-option-button>
<ion-reorder-button class="ion-navicon" on-reorder="moveActivity(activity,day,$fromIndex,$toIndex)"></ion-reorder>
</ion-item>
</ion-list>
</div>
</ion-content>
</body>
</html>

You are doing it all wrong. Method offset(element) and position(element) of $ionicPosition expects DOM element to be part of parameter.
In your case you are passing the JavaScript object from mark-up thats why its failing.
position(element) :
Get the current coordinates of the element, relative to the offset parent. Read-only equivalent of jQuery’s position function.
Its fact that it is very poorly documented in ionic docs.
What I figured out after digging through source code that these methods are jQuery equivalent and jQuery always wraps the matched element inside array, therefore parameter should be an array with 0th index pointing to actual dom as shown below
var element = document.getElementById(id);
var offset = $ionicPosition.offset([element]);
/*it returns the offset of param[0]
where param is the parameter passed to this function
*/
/*note that we need to wrap the dom element inside array as these methods are inspired from jQuery
and jQuery wraps the matched element inside array
If you are using AngularJS's jqLite, please verify if actual DOM is in array 0th position or not
*/

Related

ArcGIS JavaScript API Popup Not Referencing REST Service Layer

The content in the popup created through the variable "popupCustom" is displaying string instead of referencing the specified field {IN_COUNTRY}. I followed the ArcGIS JS API Popup Tutorials, & can't see what my error is in failing to grab the attributes associated with that field. Here's the code -- any help is greatly appreciated!
*note: feature layer url within "Cyber_Areas" variable points to REST URL for referenced Feature Class.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Search widget with multiple sources - 4.6</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
<script src="https://js.arcgis.com/4.6/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/widgets/BasemapToggle",
"esri/widgets/Legend",
"esri/layers/TileLayer",
"esri/layers/FeatureLayer",
"esri/widgets/Search",
"esri/widgets/LayerList",
"esri/PopupTemplate",
"dojo/on",
"dojo/domReady!"
], function(
Map,
MapView,
BasemapToggle,
Legend,
TileLayer,
FeatureLayer,
Search,
LayerList,
PopupTemplate,
on
) {
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
outFields: ["IN_COUNTRY"],
popupTemplate: popupCustom
});
var map = new Map({
basemap: "osm"
});
map.add(Cyber_Areas);
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.172865, 34.077613], // lon, lat
zoom: 16
});
var searchWidget = new Search({
view: view,
popupOpenOnSelect: false
});
view.ui.add(searchWidget, {
position: "top-left",
index: 0
});
var popupCustom = searchWidget.on('select-result', function(evt){
//console.info(evt);
view.popup.open({
location: evt.result.feature.geometry, // location of the click on the view
title: "Service Availability:", // title displayed in the popup
content: "<p><b>{IN_COUNTRY}"
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
From your code you are mixing the popup template value with when to display it. And those are two different things.
First, you are not setting correctly the popup template of the layer. It should be a PopupTemplate.
It seems to me that in you code the layer definition should be something like this,
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
popupTemplate: {
outFields: ["IN_COUNTRY"],
title: "Service Availability:",
content: "<p><b>{IN_COUNTRY}</b></p>"
}
});
Now if you don't want the default behavior of the popup (left click on a feature), you cant disable it like this,
view.popup.autoOpenEnabled = false; // <- disable view popup auto open
And then you can open it wherever you want like this,
view.popup.open({ // <- open popup
location: evt.result.feature.geometry, // <- use map point of the event result
fetchFeatures: true // <- fetch the selected features (if any)
});
You have to understand that the fields you use in the content of the popup template are related to the layer. That is why i set in the popup of the view to fetch the results.

Vuejs v-model escape automatically html entities

I'm trying to display some html entities in a form text input, but v-model seems escaping them.
Is there something I need to write to make v-model displaying correctly html entities?
my sample code is
<el-input v-model="data" readonly="readonly"></el-input>
I know about v-html but I prefer keep using v-model due the automatic two-way binding.
UPDATE
Maybe I expressed myself wrong, I want to display the character, not the html entity, so instead 49.42₹ i need to display 49.42₹.
If you v-model a computed that interprets HTML entities, I think you get the effect you want. You can type in entity values and it will interpret them correctly. However, it might prematurely turn &#8 into a different character; you have to type #8377; and then go back in and insert the &.
new Vue({
el: '#app',
data: {
a: '49.42₹'
},
computed: {
asText: {
get() {
return this.toText(this.a);
},
set(newValue) {
this.a = newValue;
}
}
},
methods: {
toText(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
}
}
})
<link href="//unpkg.com/element-ui#1.0.0-rc.3/lib/theme-default/index.css" rel="stylesheet"/>
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<el-input v-model="asText"></el-input>
{{a}}
<div v-html="a"></div>
</div>

Resetting forms in Polymer 2.x

I am trying to reset my form. What am I doing wrong? What is best-practice?
Here is my Plunk demo.
My problem on the demo is that connectedCallback() appears to fire continually (not just on initial load), thereby losing the value of savedItem by updating it to newItem on each update.
Here is the same issue on Github.
https://plnkr.co/edit/wRdXXws2UXl3VXrycqua?p=preview
my-demo.html
<base href="https://polygit.org/polymer+v2.0.0/shadycss+webcomponents+1.0.0/components/">
<link rel="import" href="polymer/polymer-element.html">
<link rel="import" href="paper-toggle-button/paper-toggle-button.html">
<dom-module id="my-demo">
<template>
<style>
:host > * {
margin-top: 40px;
font-size: 18px;
}
button.save {
color: white;
background-color: blue;
}
</style>
<paper-toggle-button checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button checked="{{item.dave}}">Dave</paper-toggle-button>
<button>Reset</button>
<button class="save" on-tap="_reset">Save</button>
</template>
<script>
class MyDemo extends Polymer.Element {
static get is() {
return 'my-demo';
}
static get properties() {
return {
item: {
type: Object,
notify: true,
value: () => {
return {
alice: false,
bob: false,
charlie: false,
dave: true,
};
},
},
savedItem: {
type: Object,
notify: true,
},
};
}
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
static get observers() {
return [
'_itemChanged(item.*)',
];
}
_itemChanged(newItem) {
console.log('saved-item', this.savedItem);
console.log('new-item', newItem);
}
_reset() {
this.set('item', this.savedItem);
}
}
window.customElements.define(MyDemo.is, MyDemo);
</script>
</dom-module>
Edit
Steps to recreate the problem
Open the demo here.
Open your console.
Navigate in the Plunker to my-demo.html
Click one of the toggle switches.
Notice in the console, the savedItem property updates to the current item property.
Notice, this appears to be the result of the following code block.
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
But how can this be? Because I thought connectedCallback() only fired once at initialization time?
tldr; The connectedCallback() isn't actually being called more than once in this case. savedItem and item are always the same object in your code because JavaScript passes objects by reference.
Object references
In the following:
connectedCallback() {
this.set('savedItem', this.item);
}
_reset() {
this.set('item', this.savedItem);
}
savedItem and item are both references to the same object. Calling this.set() does not automatically clone the operand (nor does the = operator).
One solution is to clone the object before assignment (using ES2017 object-spread operator):
connectedCallback() {
this.savedItem = {...this.item};
}
_reset() {
this.item = {...this.savedItem};
}
updated plunker
Best practice (or simpler reset method)
A simpler way to reset the form is to let iron-form handle the form's reset event, where it resets the form's named inputs to their initial values. This saves you from having to declare savedItem and no extra JavaScript to manage it.
To accomplish this, wrap the <paper-toggle-button>'s in an <iron-form>, and add name attributes to them. Then, insert an <input type="reset"> in the form, which serves as the reset button.
<iron-form>
<form>
<paper-toggle-button name="alice" checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button name="bob" checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button name="charlie" checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button name="dave" checked="{{item.dave}}">Dave</paper-toggle-button>
<input type="reset" class="save">
</form>
</iron-form>
demo

How to save select state in url with Ember.js?

I implement content filter with ember.js and I need to save filter state in URL. How can I do this?
I reed this section http://guides.emberjs.com/v1.12.0/routing/query-params/ and try to do that code
http://output.jsbin.com/cixama/4
But choice saved in URL as
http://output.jsbin.com/cixama/4#/?pull=undefined
Why undefined?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dynamic select on Ember.js</title>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://builds.emberjs.com/release/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/release/ember.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.0.0-beta.18/ember-data.prod.js"></script>
</head>
<body>
<script type="text/x-handlebars" id="index">
<form>
{{view "select" content=model
optionValuePath="content.number"
optionLabelPath="content.title"
value=pull
prompt="Choice option"}}
</form>
</script>
<script id="jsbin-javascript">
App = Ember.Application.create({});
// ROUTES
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls');
}
});
// CONTROLLERS
App.IndexController = Ember.Controller.extend({
queryParams: ['pull'],
pull: null,
});
</script>
<script id="jsbin-source-javascript" type="text/javascript">App = Ember.Application.create({});
// ROUTES
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls');
}
});
// CONTROLLERS
App.IndexController = Ember.Controller.extend({
queryParams: ['pull'],
pull: null,
});</script></body>
</html>
Your problem is that the number property of the payload is an integer, while the query param is a string.
When you select an item from the dropdown, a numeric value gets written into the pull property. But the query params mechanism replaces it with a string. The dropdown sees the value changed, looks up a new value and finds nothing. It assumes that no value was chosen and sets pull to undefined.
One solution is to use two properties: one will store the original numeric value, the other will be a getter/setter computed property that would convert between numeric and text.
<form>
{{view "select" content=model
optionValuePath="content.number"
optionLabelPath="content.title"
value=currentPull
prompt="Choice option"}}
</form>
<p>currentPull: {{currentPull}}</p>
App.IndexController = Ember.Controller.extend({
queryParams: ['pull'],
pull: Ember.computed('currentPull', {
get: function() {
return this.get('currentPull');
},
set: function(key, value) {
this.set('currentPull', parseInt(value, 10));
return value;
},
}),
currentPull: null,
});
Demo: http://output.jsbin.com/redefi/2
But a better solution would be to introduce a model layer into your app. You'd have a pull-request entity with its attributes corresponding to properties of the payload. Then you can handle the number↔text conversion in the serializer, and your business logic will stay concise and expressive.

Mapbox non-geographic

I'm trying to mixed up for at least a week , mapbox with leaflet to did a Non geographic map.
My first step was to build it with maptiler.com which generated with the tiled a code based on leaflet. But i want to add to this code a Geojson proprites.
I saw that in Mapbox there is already a geojson popup built-in.
This is why i want to use my leaflet map code + mapbox popup, it's possible ?
Thanks,
Jade
<!DOCTYPE html>
<html>
<head>
<title>map</title>
<meta charset="utf-8"/>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.5/mapbox.js'></script>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.5/mapbox.css' rel='stylesheet' />
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.ie.css" />
<![endif]-->
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiamFkZTIyOTMiLCJhIjoiRDdweEFrZyJ9.Yk4XeNmp3SExkU41Z7BU3w';
function init() {
var mapMinZoom = 3;
var mapMaxZoom = 6;
var map = L.map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: L.CRS.Simple
}).setView([0, 0], mapMaxZoom);
var mapBounds = new L.LatLngBounds(
map.unproject([0, 7680], mapMaxZoom),
map.unproject([10496, 0], mapMaxZoom));
map.fitBounds(mapBounds);
L.tileLayer('{z}/{x}/{y}.png', {
minZoom: mapMinZoom, maxZoom: mapMaxZoom,
bounds: mapBounds,
noWrap: true
}).addTo(map);
// The GeoJSON representing a point feature with a property of 'video' for the Vimeo iframe
var geoJson = {
features: [{
type: 'Feature',
properties: {
'marker-color': '#f00',
'marker-size': 'large',
'marker-symbol': 'rocket',
video: '<iframe src="//player.vimeo.com/video/106112939" width="380" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> <p><h2>How Simplicity Will Save GIS</h2><p>Vladimir Agafonkin from FOSS4G on Vimeo.</p>',
},
geometry: {
type: 'Point',
coordinates: [0,0]
}
}]
};
var myLayer = L.mapbox.featureLayer().addTo(map);
// Add the iframe in a marker tooltip using the custom feature properties
myLayer.on('layeradd', function(e) {
var marker = e.layer,
feature = marker.feature;
// Create custom popup content from the GeoJSON property 'video'
var popupContent = feature.properties.video;
// bind the popup to the marker http://leafletjs.com/reference.html#popup
marker.bindPopup(popupContent,{
closeButton: false,
minWidth: 320
});
});
// Add features to the map
myLayer.setGeoJSON(geoJson);
}
</script>
<style>
html, body, #map { width:100%; height:100%; margin:0; padding:0; }
background-color:white;
</style>
</head>
<body onload="init()">
<div id="map"></div>
</body>
</html>
It seems that you're asking 2 separate questions here. The original question about non-geographic maps and your follow-up question about adding an iframe to a leaflet popup. I'll try to address your follow-up question:
Let's take the Mapbox example you linked (https://www.mapbox.com/mapbox.js/example/v1.0.0/video/) and adapt it to work with the video you would like to display.
If you've already got some GeoJSON data, you can edit it to include a video property. Let's look at the GeoJSON code from the Mapbox example:
var geoJson = {
features: [{
type: 'Feature',
properties: {
'marker-color': '#f00',
'marker-size': 'large',
'marker-symbol': 'rocket',
video: '<iframe src="//player.vimeo.com/video/106112939" width="380" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> <p><h2>How Simplicity Will Save GIS</h2><p>Vladimir Agafonkin from FOSS4G on Vimeo.</p>',
},
geometry: {
type: 'Point',
coordinates: [0,0]
}
}]
};
See that video property? Its value contains the iframe code that will end up inside the popup for the map marker it corresponds to. I went ahead and added the iframe code from your YouTube video to the above example and you can see it in action on jsfiddle here: http://jsfiddle.net/danswick/tcxvpw84/.
Your GeoJSON data probably doesn't have a video property, but you can add it using a text editor or geojson.io.
Further down in our example code, we access that video property, set it to a variable, and bind it to our marker's popup:
// Create custom popup content from the GeoJSON property 'video'
var popupContent = feature.properties.video;
// bind the popup to the marker http://leafletjs.com/reference.html#popup
marker.bindPopup(popupContent,{
closeButton: false,
minWidth: 320
});
Mapbox just uses Leaflet's bindPopup method which comes standard with L.Marker. If you create a L.GeoJSON layer, you can add a popup to each feature using the onEachFeature option of L.GeoJSON which takes a function with two parameters: feature and layer. In there you can bind a popup to your feature:
For example when you have features like this one, with a property called name:
{
"type": "Feature",
"properties": {
"name": "E"
},
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
}
You could then use that name value when binding a popup to your feature like this:
// Create new GeoJSON layer
L.geoJson(data, {
// Define the onEachFeature function which runs on every feature
onEachFeature: function (feature, layer) {
// Bind a popup to the layer using the name property
layer.bindPopup(feature.properties.name);
}
}).addTo(map);
Here's a working example on Plunker: http://plnkr.co/edit/iPLHqi?p=preview
Thanks, to take time to reply.
But actually i wanted to use geojson just to put iframe in a leaflet popup.
like this :
L.marker(map.unproject([452, 410])).addTo(map).bindPopup("<iframe width="560" height="315" src="https://www.youtube.com/embed/zP71_cXfiu0" frameborder="0" allowfullscreen></iframe>");
But it doesn't work but with the same syntax this work : I just saw in this exemple that with geojson it's might work :
https://www.mapbox.com/mapbox.js/example/v1.0.0/video/
L.marker(map.unproject([452, 410])).addTo(map).bindPopup("https://www.youtube.com/embed/zP71_cXfiu0");
Sorry if i'm a little bit confusing, because i'm designer and all this "code thing" it's new for me :)