right now I'm working on an app that relies heavily on user's geolocation. At this moment I only need to get an user's address through this user's latitude and longitude. To do this I use two packages: mdg:geolocation (to get latitude and longitude) and meteor-google-reverse-geocode (to transfrom latitude and longitude into an address).
Right now I have the following code:
import React, { Component, PropTypes } from 'react';
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Geolocation } from 'meteor/mdg:geolocation';
import { reverseGeocode } from 'meteor/jaymc:google-reverse-geocode';
export default class Profile extends Component {
setLocation() {
var latLng = Geolocation.latLng();
var lat = latLng.lat;
var lng = latLng.lng;
reverseGeocode.getSecureLocation(lat, lng, function(location) {
Meteor.users.update(Meteor.userId(), {
$set: {"profile.location": reverseGeocode.getAddrStr()}
});
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col s4">
<User />
</div>
<button onClick={this.setLocation.bind(this)}>Set location</button>
</div>
</div>
)
}
}
I have a User component which displays user's location among other parameters but at first it should be getting this location param from registration and the log in (I'll implement sessions later), so I created a button that does the trick but only when I click it twice. With first click it doesn't get any info rather showing me an error in console but when I hit it second time it somehow works as I want, i.e getting the right address and then displaying it in my User component. Okay, it can grab the data but only if I click it twice which is not acceptable because I want to grab this data after registration or logging in, i.e with very first click, not the second one, but I still don't know how to do it. Any help or thoughts would be appreciated. Thanks for your time you dedicated to described problem!
UPD
After some research, I found that Geolocation function isn't reactive and it could take some time to load data, so I used ReactiveVar to make it reactive and Tracker to manually stop calculation when it's completed. Now my code looks like this:
setLocation() {
var latLng = new ReactiveVar();
Tracker.autorun(function(computation) {
latLng.set(Geolocation.latLng());
if (latLng.get()) {
computation.stop();
console.log(latLng);
var lat = latLng.lat;
var lng = latLng.lng;
reverseGeocode.getSecureLocation(lat, lng, function(location) {
Meteor.users.update(Meteor.userId(), {
$set: {"profile.location": reverseGeocode.getAddrStr()}
});
});
}
})
}
It computes lan and lng properties with first click but here comes another error Uncaught TypeError: Cannot read property 'formatted_address' of undefined in google-reverse-geocode.js file.
Okay guys, I solved this problem. I took a look in ReactiveVar output and realized what was my mistake. See, the output contains and object curValue where lan and lng properties are stored, so to extract these properties I just had to call them explicitly like latLng.curValue.lat. Here's fixed function that works the way I described in my first post:
setLocation() {
var latLng = new ReactiveVar();
Tracker.autorun(function(computation) {
latLng.set(Geolocation.latLng());
if (latLng.get()) {
computation.stop();
console.log(latLng);
var lat = latLng.curValue.lat;
var lng = latLng.curValue.lng;
reverseGeocode.getSecureLocation(lat, lng, function(location) {
Meteor.users.update(Meteor.userId(), {
$set: {"profile.location": reverseGeocode.getAddrStr()}
});
});
}
})
}
Hope it helps someone!
Related
Using Fullcalendar 4, I am trying to show/hide my resources using a select menu. When the user selects one of the providers from a menu, I want to only show that one resourc's events.
Above my fullcalendar I have my select menu:
<select id="toggle_providers_calendar" class="form-control" >
<option value="1" selected>Screech Powers</option>
<option value="2">Slater</option>
</select>
I am gathering the resources I need using an ajax call on my included fullcalendar.php page. I am storing them in an object and then trying to control which resources are shown onscreen:
document.addEventListener('DOMContentLoaded', function() {
var resourceData = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
console.log(resourceData);
});
//below, set the visible resources to whatever is selected in the menu
//using 1 in order for that to show at start
var visibleResourceIds = ["1"];
//below, get the selected id when the the menu is changed and use that in the toggle resource function
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
var calendar_full = document.getElementById('calendar_full');
var calendar = new FullCalendar.Calendar(calendar_full, {
events: {
url: 'ajax_get_json.php?what=location_appointments'
},
height: 700,
resources: function(fetchInfo, successCallback, failureCallback) {
// below, I am trying to filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
...
});
// below, my toggle_providers_calendar will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
To make sure the getJSON is working, I have console.log(resourceData). The information in the console once it's gathered is:
[{id: '1', title: 'Screech Powers'}, {id: '2', title: 'Slater}]
... the above are the correct resources that can be chosen/rendered. So that seems to be okay.
On page load, no resources show at all, when resource id of '1' (Screech Powers) should be shown per my code. Well, at least, that's what I am trying to do right now.
When the menu changes, resources will show/hide, but not based on what's selected; the logic of only showing what is selected in the menu doesn't seem to be working.
I used to use a URL request for my resources: 'ajax_get_json.php?what=schedule_providers_at_location', and it worked fine! All resources show then their events properly. I am just trying to modify it by using a menu to show/hide the resources as needed.
Here's what I'm doing to make it happen so far! In case someone comes across this post ever, this will help.
Here's my code before my fullcalendar code.
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
My select menu with id 'toggle_providers_calendar' is the same as my original post. My fullcalendar resources as a function is the same too.
After the calendar is rendered, here are the changes I made to my toggle resources function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
This causes the resources to show and hide properly. If the user selects "Show All" that works too!
In order to have a default resource show on load, I add this to my fullcalendar script:
loading: function(bool) {
if (bool) {
//insert code if still loading
$('.loader').show();
} else {
$('.loader').hide();
if (initial_load) {
initial_load = false;
//code here once done loading and initial_load = true
var default_resource_to_show = "<?php echo $default_provider; ?>";
if (default_resource_to_show) {
//set the menu to that provider and trigger the change event to toggleresrource()
$('#toggle_providers_calendar').val(default_provider).change();
} else {
//pass in nothing meaning 'select all' providers for scheduler to see
toggleResource();
}
}
}
},
I am using a bool variable of initial_load to see if the page was just loaded (basically not loading data without a page refresh). The bool of initial_load = true is set outside of DOMContentLoaded
<script>
//show selected date in title box
var initial_load = true;
document.addEventListener('DOMContentLoaded', function() {
My only current problem is that when toggleResource function is called, the all day vertical time block boundaries don't line up with the rest of the scheduler. Once I start navigating, they do, but I don't understand why it looks like this on initial load or when toggleResource() is called:
Any thoughts on how to correct the alignment of the allday vertical blocks?
I have an aframe object in main.html:
<a-gltf-model id='player1' foobox playmyclip src="#myMixBun" ></a-gltf-model>
I want to have it perform a gltf clip animation when the eventListener ‘driveplay’ is emitted.
In foofile.js:
AFRAME.registerComponent('playmyclip', {
init: function () {
var el = this.el;
el.addEventListener('driveplay', function () {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
});
}
});
Currently I have it so when the j key is hit ‘driveplay’ is emitted:
AFRAME.registerComponent('foobox', {
init: function() {
var el = this.el
var swingno = 0;
self = this;
document.addEventListener('keydown', (event) => {
const keyName = event.key;
if (keyName === 'j') {
el.emit('driveplay',{},true);
// code to store record of event in Mongo for second player
var playerid = self.el.getAttribute('id');
var playerMid = Games.findOne({name: playerid})._id;
Games.update({_id: playerMid},{$set:{swings : swingno}});
swingno = swingno + 1;
}
})
}
});
But I also need to have the animation to happen at the same time on player2‘s screen. So in the above code I increment a variable in Mongo every time the animation happens. Then in main.js meteor automatically emits an event whenever “swingno” changes in Mongo.
main.js:
import {EventEmitter} from 'meteor/raix:eventemitter';
Event = new EventEmitter();
var swingcnt1 = 0;
Template.hello.helpers({
counter() {
if (Games.findOne()) //mongo is ready to access
{
var plyr1Swing = Games.findOne({name: "player1"}).swings;
if (plyr1Swing !== swingcnt1) {
Event.emit('driveplay',{},true);
swingcnt1 = plyr1Swing;
console.log(“this shows on player2’s console automatically”, swingcnt1);
}
}
else {null}
return { ........};
},
When player1 hits the “j” key the animation happens correctly on his screen, plus the event is recorded in Mongo, and player2 receives the updated Mongo value (since it show in the console.log).
The problem is the
Event.emit('driveplay',{},true);
statement doesn’t trigger the animation in player2‘s screen. This is a little tricky, since I need meteor’s “raix:eventemitter” package to create an event that the aframe event listener can see. It’s possible I’m not actually emitting an event at all, since I don’t know how to test if it’s working. Or possibly aframe can’t see the emitted event.
Possibly there’s an easier way of doing this. Thanks for any help.
SOLVED The solution was to use the listener code from meteor’s raix:eventemitter package inside Aframe’s component.
https://atmospherejs.com/raix/eventemitter
AFRAME.registerComponent('playmyclip', {
init: function ()
var el = this.el;
listener = function() {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
};
Event.on('driveplay', listener);
}
});
Plus the foobox component no longer needs:
if (keyName === 'j') {
el.emit('driveplay',{},true);
Both player1 and player2 get the animation event from
Event.emit('driveplay',{},true);
in main.js when the meteor helper notices a change in the mongodb.
I have the following structure:
{
id: 23423-dsfsdf-32423,
name: Proj1,
services: [
{
id:sdfs-24423-sdf,
name:P1_Service1,
products:[{},{},{}]
},
{
id:sdfs-24jhh-sdf,
name:P1_Service2,
products:[{},{},{}]
},
{
id:sdfs-2jnbn3-sdf,
name:P1_Service3,
products:[{},{},{}]
}
]
},
{
id: 23423-cxcvx-32423,
name: Proj2,
services: [
{
id:sdfs-xvxcv-sdf,
name:P2_Service1,
characteristics:[{},{},{}]
},
{
id:sdfs-xvwqw-sdf,
name:P2_Service2,
characteristics:[{},{},{}]
},
{
id:sdfs-erdfd-sdf,
name:P2_Service3,
characteristics:[{},{},{}]
}
]
}
I have no problem creating a form this schema an insert form with quickForm.
But I cant figure out (and tried to read every tutorial and instruction and nothing worked) how to create an update form with all fields filled and (need to expand and fill the services and the characteristics arrays also:
of course, as i said, in update i need the services and characteristics to expend to the right size with all the fields.
But if i could understand how to fill the form fields i could understand myself how to expend the arrays...
i've tried:
{{> quickForm collection="Projects" id="updateProjectForm" collection="Projects" type="method" class="update-project-form" doc=project }}
with:
import SimpleSchema from 'simpl-schema';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
// Attaching the subscription to the template so we can reuse it
Template.ProjectSingle.onCreated(function(){
var self = this;
self.autorun(function(){
var id = FlowRouter.getParam('id');
self.subscribe('projectSingle', id);
});
});
Template.ProjectSingle.helpers({
project: ()=> {
var id = FlowRouter.getParam('id');
console.log(Projects.findOne({_id: id}));
return Projects.findOne({_id: id});
}
});
I can't even see the console.log() printing.
This solution at list didn't crash the meteor server... everything else i've tried crashed the server on many errors
Maybe i need to mention that i'm using partials so maybe there is a problem with the JS files but i don't think so as the onCreated method is being read.
10x.
EDIT:
I've removed the partial for the update template and it is now in the root Template with its own JS with the method:
projectDoc: ()=> {
var id = FlowRouter.getParam('id');
console.log("Update: " + Projects.findOne({_id: id}));
return Projects.findOne({_id: id});
}
Now i can see this method is being called but for some reason it is being called twice. First with the correct data and then getting undefined so i've still not getting the fields showing anything but if i could find why it is being called twice i will solve the first level form (no services and so on)
Solved it (Not sure this is the best way as i'm still having two calls to the method but this is working for now:
projectDoc: ()=> {
var id = FlowRouter.getParam('id');
if(Projects.findOne({_id: id}) != null){
console.log(Projects.findOne({_id: id}));
thisProject = Projects.findOne({_id: id});
return Projects.findOne({_id: id});
} else {
return thisProject;
}
}
Just as a preface, I am using create-react-app boiler plate.
I am trying to parse out a post from the wordpress API into a react component so I can extract all images and make them their own JSX objects. I need to have each post have its own on click method so I cant just use the dangerouslyaddHTML function in React.
Currently, I search through the string of json that had the rendered HTML from the WP content of each post and find all img tags. I then add then image src to an array and set this in my state.
However I am getting this error :
DOMException: Only secure origins are allowed
The site I have this currently hosted on it http://natalie.madeline-omoore.com/build/
The code for part of my component is below:
`componentDidMount(){
var owner;
for(var i = 0; i < this.props.categoryId.length; i++) {
if (this.props.categoryId[i].num === this.props.content.categories[0]) {
owner = this.props.categoryId[i].category;
this.setState({
catname: owner
})
break;
}
}
var theContent = this.props.content.content.rendered;
var output = $(theContent).find("img").map(function() {
return this.src;
}).get();
this.setState({
postImages: output
});
}
click(){
this.updateActive();
this.props.fix();
}
render() {
if (this.props.className === this.state.catname) {
return (
<div >
{ this.props.className === this.state.catname &&
<article className={`${this.state.catname}`} >
{this.state.postImages.map((image, i) =>
<ImageContain key={image} image={image} fix={this.props.fix} />
)}
</article>
}
</div>
);
} else {
return null;
}
}`
I am new to react so thank you!
This sounds like an issue with the 'service worker' that is default with create-react-app
You can deregister the worker by adding the following to index.js:
import { unregister } from './registerServiceWorker';
// ...
unregister();
Be sure to call unregister() after registerServiceWorker() is called.
You can learn more about this here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app
Good luck, and happy coding!
I'm getting Uncaught TypeError: Cannot read property 'chapter' of null when trying to get a Reactive Var value. I'm doing exactly the same thing with Session and it works. So I'm confused as to what I'm going wrong. Perhaps the value is being set at the wrong time?
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var'
import { Stories } from '/imports/collections/stories.js';
/* Story */
Template.story.onCreated(function(){
// Set the current chapter
this.chapter = new ReactiveVar(0);
this.page = new ReactiveVar(0);
});
Template.story.onRendered(function(){
/* Show the first fragment */
page.addEventListener( 'changed', function( event ) {
console.log( Template.instance().chapter.get() );
....
The event is being fired, but the instance of chapter isn't being set in onCreated.
What am I doing wrong here?
try this:
Template.story.onRendered(function(){
let instance = Template.instance();
/* Show the first fragment */
page.addEventListener( 'changed', function( event ) {
console.log( instance.chapter.get() );