So I am working on the direction service using Google Map API. The site works fine with the autocomplete service and I am trying to add the text panel telling users how to get to the destination using 'transit', 'walking', and 'driving' mode.
https://jsfiddle.net/QPX321/yo4tbh58/
what to do is to have the text giving routing information based on the travel mode users select. say if I select 'transit' it shows me the bus that I have to take.
so I know something is wrong with my code in the function below.
function calculateAndDisplayRoute(directionsService,
directionsDisplay) {
var start = document.getElementById('origin-input').value;
var end = document.getElementById('destination-input').value;
var modeSelector = document.getElementById('mode-selector');
directionsService.route({
origin: start,
destination: end,
travelMode: modeSelector,
}, function(response, status) {
if (status === 'OK') {
directionsDisplay.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
the code works fine when I change
travelMode: modeSelector,
to
travelMode: 'WALKING', or
travelMode: 'DRIVING', or
travelMode: 'DRIVING',
but this will only give one option and it forcibly change my travel mode to one specific mode. How should I code it so the text panel is able to detect my travel mode, hence giving my the appropriate suggestion?
i think the problem is that I did not link my travel mode to the function? I use the code below to set my travel mode,
var modeSelector = document.getElementById('mode-selector');
and thought the code below would be able to detect my travel mode
travelMode: modeSelector,
but it did not work when I use these code.
I would expect the text panel to show direction in the travel mode that I click. If I click driving mode, it should give me direction on how to drive from point A to point B. The code does not give me any text unless I change the travelMode: to a specific mode ('driving', 'transit', or 'walking').
Thank you in advance for you help and precious time.
Regards,
First you need to add value attributes to your radio input tags in your HTML:
<input type="radio" name="type" id="changemode-walking" checked="checked" value='WALKING'>
<label for="changemode-walking">Walking</label>
<input type="radio" name="type" id="changemode-transit" value='TRANSIT'>
<label for="changemode-transit">Transit</label>
<input type="radio" name="type" id="changemode-driving" value='DRIVING'>
<label for="changemode-driving">Driving</label>
Then you need to check which of the radio inputs is checked and get the value for it. You can do that in a for loop (as per this answer):
var radios = document.getElementsByName('type');
for (var i = 0; i < radios.length; i++) {
if (radios[i].checked) {
var modeSelector = radios[i].value;
break;
}
}
Now modeSelector contains the text from the value attribute of the checked radio button.
Here is a link to a working version of your JSfiddle.
Related
I'm using the amazing flatpickr on a project and need the calendar date to be mandatory.
I'm trying to have all the validation in native HTML, so I was naively trying with just adding the required attribute to the input tag, but that doesn't appear to be working.
Is there a way of natively making a date mandatory with flatpickr or do I need to write some custom checks?
You can easily achieve this by:
Passing allowInput:true in flatpickr config.
As example:
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
};
From the documentation:
Allows the user to enter a date directly into the input field. By
default, direct entry is disabled.
The downside of this solution is that you should enable the direct entry (but ideally form validation should occur whether or not direct entry is enabled).
But if you don't want to enable the direct entry to solve this problem, you can use the code below as a workaround:
flatpickrConfig = {
allowInput:true,
onOpen: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', true);
},
onClose: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', false);
$(instance.altInput).blur();
}
};
This code remove the readonly property when it is not in focus so that html validation can occur and add back the readonly prop when it is in focus to prevent manual input. More details about it here.
This is what I came up with to make as complete of a solution as possible. It prevents form submission (when no date selected and input is required), ensures browser native "field required" message pops up and prevents the user typing in the value directly.
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
onReady: function(selectedDates, dateStr, instance) {
let el = instance.element;
function preventInput(event) {
event.preventDefault();
return false;
};
el.onkeypress = el.onkeydown = el.onkeyup = preventInput; // disable key events
el.onpaste = preventInput; // disable pasting using mouse context menu
el.style.caretColor = 'transparent'; // hide blinking cursor
el.style.cursor = 'pointer'; // override cursor hover type text
el.style.color = '#585858'; // prevent text color change on focus
el.style.backgroundColor = '#f7f7f7'; // prevent bg color change on focus
},
};
There is one disadvantage to this: Keyboard shortcuts are disabled when the flatpickr is open (when the input has focus). This includes F5, Ctrl + r, Ctrl + v, etc. but excludes Ctrl + w in Chromium 88 on Linux for some reason. I developed this using a rather old flatpickr version 3.1.5, but I think it should work on more recent ones too.
In case you want to use altFormat (display one date format to user, send other date format to server), which also implies setting altInput: true, you have to also change the onReady function to use instance.altInput instead of instance.element.
The onReady event listener can probably be attached to the instance after initializing it. However, my intention of using flatpickr with vue-flatpickr-component where you cannot elegantly access the individual flatpickr instances, made me use the config field instead.
I haven't tested it on mobile devices.
After digging a bit into the GitHub repo, I found a closed issue that points out that the issue will not be addressed.
In the same Issue page there is a workaround that seems to do the trick:
$('.flatpickr-input:visible').on('focus', function () {
$(this).blur()
})
$('.flatpickr-input:visible').prop('readonly', false)
copy attr name from prior input type hidden to rendered flatpickr input
just do this
$('[name=date_open]').next('input').attr("name","date_open");
$('[name=date_close]').next('input').attr("name","date_close");
Have been working on this for a couple of days now, finally getting the result I was after.
NOTE: I am using flatpickr with jQuery validation
As you would know flatpickr uses an alternative field for the date input, the actual field where the date is stored is hidden, and this is the key.
jQuery validation has a set of defaults, and by default hidden fields are not subject to validation, which normally makes perfect sense. So we just have to turn on the validation of hidden fields to make this work.
$.validator.setDefaults({
ignore: []
});
So my validator rules are then fairly normal:
var valid = {
rules: { dateyearlevel: {required: true} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
That should allow you to ensure the date is required.
In my situation I wanted my date to only be required is a checkbox was checked. To do this we changed the rule above:
var valid = {
rules: { dateyearlevel: {
required: function() { return $("#mycheckbox").is(":checked") }
} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
In case this helps someone, I'm using parsley.js for frontend validation and it works good with flatpickr
enter image description here
Just to expand a bit more on this, I found the ignore value set as an empty array did the trick for me also. You can just add this to your validate call back. Also displaying was a bit of an issue so I updated the errorPlacement to allow for flatpickr inputs like so.
$('#my-form').validate({
errorPlacement: function (error, element) {
if (element.hasClass('js-flatpickr') && element.next('.js-flatpickr').length) {
error.insertAfter(element.next('.js-flatpickr'));
} else if (element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
ignore: [],
rules: {
'startdate': { required: true }
},
messages: {
'startdate': {required: "Start Date is required"}
},
submitHandler: function(form) {
// ajax form post
}
});
in my case vue ( dunno why ) , i would like to comment for comment by #mik13ST
fyi: the default allowInput i think is true, no need to define, i didnt set the properties and my flat-pickr also work on testing.
i use
// this work in flat-pickr || #code_01
<small class="text-danger">
{{ validationContext.errors[0] }}
</small>
instead of
// work for all element except <flat-pickr #code_02 , dunno why not work
<b-form-invalid-feedback>
{{ validationContext.errors[0] }}
</b-form-invalid-feedback>
full code
<validation-provider
#default="validationContext"
name="Waktu Selesai Berkegiatan *"
vid="Waktu Selesai Berkegiatan *"
rules="required"
>
<flat-pickr
id="Waktu Selesai Berkegiatan *"
v-model="item.pip_time_end_rl"
placeholder="Waktu Selesai Berkegiatan *"
class="form-control"
static="true"
:config="dpconfig"
:state="getValidationState(validationContext)"
/>
// put here the message of error ( required ) #code_01 instead of #code_02
</validation-provider>
if younot use composite,
just use
#default="{ errors }" // in validation provider
:state="errors.length > 0 ? false : null" // in element for example flat-pickr
{{ errors[0] }} // to print out the message
I have a master and a child component. The child component persists the data for the create mode as well as the edit mode. The child has a data section as follows which is being used when the component is in create mode
data() {
return {
title: '',
description: '',
organizer: '',
startdate: '',
enddate: '',
email: '',
phone: ''
}
},
and my inputs in create mode are as follows
<input type="text" placeholder="enter event title here" class="form-control" v-model="title">
In the edit mode, I am updating a prop value on the client as follows, which is
props:['currentevent']
The value of the currentevent is being passed from the master component to the child component and is also the value that is currently being edited.
so, the complete code for handling an input value looks like as follows
<input type="text" placeholder="enter event title here" class="form-control" v-if="currentevent" :value="currentevent.title">
<input type="text" placeholder="enter event title here" class="form-control" v-else v-model="title">
and in my save method (in the child component), I am checking if currentevent is empty or not. If it is empty then I trigger the add code otherwise, I trigger the update code.
Question : This works , but I have a large form and having to do this for each and every component is not a clean design . Can you please let me know what should I be doing ?
I totally appreciate your predicament. The best way to handle form data is to make it create/update agnostic. Here's what I'd recommend you try:
Instead of maintaining all the data fields as disparate properties, contain them in a single object, in this case I'm calling it eventObj for clarity:
data () {
return {
eventObj: {}
}
}
Then in your markup you'd reference them via the object:
<input type="text" placeholder="..." class="form-control" v-model="eventObj.title">
You'd then need to define a prop for passing in the data (as an object) from the parent component if you are editing:
props: {
currentevent: Object
}
And then you'd just need to map the incoming prop to the child component's data:
created() {
Object.assign(this.eventObj, this.currentevent || {})
}
Now when your inputs like <input v-model="eventObj.title"> are processed, if there is a saved title (that was passed in with currentevent) the field will be prepopulated with it, otherwise it will be blank.
I think this should help you in the right direction toward solving the complexity you're trying to figure out. There are other logistical issues involved with this kind of stuff in general, but I won't drone on. :)
The issue I see is you want to remove the v-if/else in the form. I will recommend here is keep your local data of child to be in sync with the props passed and only use local variable in the form.
One way to do this can be put a watcher on props and whenever props changes, update local variables and only use those variables in form.
watch: {
currentevent: function(newVal){
title = newVal.title,\
description = newVal.description
...
}
}
I'm trying to implement the "reset password" functionality in my Meteor app. I have a very simple implementation of it based on this tutorial: Julien's tutorial on gentlenode
There are several examples floating around that use this same basic approach. I did mine almost exactly like Julien's but I used only one template; I use an {{#if}} in my template that displays the 'reset password' form, if my session variable sResetPassword is not falsey. (I don't know how the correct template is supposed to get displayed in Julien's example and it doesn't work for me as it is written -- the template doesn't change.)
Here's the critical piece of code. Two different methods that both work on my local app, but neither one works on my hosted (modulus) app.
/* method one
if (Accounts._resetPasswordToken) {
Session.set('sResetPassword', Accounts._resetPasswordToken);
}
/* method two
Accounts.onResetPasswordLink( function(token) {
Session.set('sResetPassword', token);
});
On my deployed version (Modulus), the link opens up my app and just goes straight to the start screen. When I check the value of my sResetPassword session var, it's undefined, so somehow the value of the token never gets put into the var.
While we're on the subject, does anyone know how you are supposed to get the correct template to load when you use a separate template for the reset password form?
Here is how it works for us. Code:
var token, done;
Accounts.onResetPasswordLink(function (t, d)
{
token = t;
done = d;
setTimeout(()=>Router.go("reset_password"), 0);
});
Template["reset_password"].events({
"click #resetBtn": function (event:Event, instance:Blaze.TemplateInstance)
{
var password1: string = instance.$("#input_password1").val();
var password2: string = instance.$("#input_password2").val();
console.log(password1, password2);
if (password1 != password2)
{
return;
}
Accounts.resetPassword(token, password1, ()=>
{
done();
Router.go("somewhere");
});
}
});
Template:
<template name="reset_password">
<form data-parsley-validate>
<div class="input-field">
<input id="input_password1" type="password" class="validate" data-parsley-trigger="keyup" data-parsley-minlength="6" data-parsley-minlength-message = "Please provide a password that is at least a 6 characters long." required>
<label for="input_password1">New Password</label>
</div>
<div class="input-field">
<input id="input_password2" type="password" class="validate" data-parsley-trigger="keyup" data-parsley-minlength="6" data-parsley-minlength-message = "Please provide a password that is at least a 6 characters long." required>
<label for="input_password2">Again</label>
</div>
<button id="resetBtn" class="waves-effect btn">Reset Password</button>
</form>
OK, for whatever reason, replacing iron-router with flow-router fixed this issue for me. I created a new app with only the login and reset password functionality and it worked fine. I added iron-router and again it worked, but only dev mode. When I ran it in production mode, the problem returned. Replaced iron-router with flow-router (in both the test app and my full app) and now the problem is gone. The email link works as expected in both modes.
I am trying to use meteor autosubscribe function on the client but sometimes it works and sometimes it doesn't. So here is the case:
Working version: I have dropdown which is populated with channels. When user clicks on the channel I set session variable and start loading threads:
Template.channelDropdown.events({
"click #channelLink": function() {
Session.set("currentChannel", this);
}
});
html
<ul class="dropdown-menu">
{{#each channels}}
<li>
<a id="channelLink" href="#">{{name}}</a>
</li>
{{/each}}
</ul>
and
Tracker.autorun(function() {
Meteor.subscribe("threadsByChannel", Session.get("currentChannel"));
});
Meteor.publish("threadsByChannel", function (channel) {
return threads.find({channel: channel});
});
and loading threads:
"channelThreads": function() {
return threads.find({channel: Session.get("currentChannel")}).fetch();
},
Now this works. However I have other method to open channel which doesn't work. It is possible to enter channel name and if it doesn't exist it is created, otherwise existing one is returned.
Template.channelSearchBar.events({
"submit #joinChannelForm": function() {
event.preventDefault();
var channelName = $("#channelNameField").val();
Meteor.call("getChannelByName", channelName, function(error, result) {
if (error) {
// TODO error handling
} else {
Session.set("currentChannel", result);
}
});
$("#channelNameField").val("");
}
});
server:
'getChannelByName': function (channelName) {
var channel = channels.findOne({name: channelName});
if (channel) {
return channel;
} else {
var newChannel = {
name: channelName
}
return channels.insert(newChannel);
}
}
html
<template name="channelSearchBar">
<form id="joinChannelForm" class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="channelNameField" type="text" class="form-control" placeholder="Enter channel name">
</div>
<button type="submit" class="btn btn-default">Join</button>
</form>
</template>
Now the only difference is that session variable is set in callback. I'm pretty sure this is the problem as it is asynchronious call to the server and somehow threads are not populated in client when requested. When I set breakpoint in loading threads function (threads.find() on client), I see that session variable is correctly set, but it just does not return anything. Also sometimes it is called two times (for example in working first case first call returns nothing and then second call returns real results for some reason. Is this is how it suppose to work?). I am just beginning to learn meteor and trying to understand how it all works. Would be glad if someone could explain or direct me to the right way.
EDIT: Its very strange. I have put breakpoint in publish function and it seems it works fine - exactly like it should. However on the not working case it simply returns nothing right from the server side even though both working and not working situations provides (seemingly) exactly the same channel object. It seems that the problem is related with mongodb query.
Why don't remove the Meteor.call, and do everything on the client side?, the subscription on the Autorun seems to be fine, lets try with this code, just make sure you have the allow/deny permissions in order.
Template.channelSearchBar.events({
"submit #joinChannelForm": function() {
event.preventDefault();
var channel = channels.findOne({name: channelName}),
channelName = $("#channelNameField").val();
if (channel) {
return channel;
} else {
var newChannel = {
name: channelName
}
var chanelCreated = channels.insert(newChannel);
Session.set("currentChannel", chanelCreated);
$("#channelNameField").val("");
}
}
});
OK it seems the real problem was not that of meteor publish/subscribe mistake but because of mongodb query which was not recognizing channel object. Problem was solved by changing this:
threads.find({channel: channel})
to this:
threads.find({"channel.name": channel.name})
I have found that mongo queries cares about order of object parameters, but channel had only one parameter (name) at the moment, so I'm still not sure why they were not considered equal. One channel was returned from findOne query and another from find. One from find was recognized.
I want to include a template (or use a helper, I don't care) that could be clicked to edit in place. This view MUST be reusable, and so can't rely on the Session variable or any other variables that aren't contained by the view instance.
In the display mode it would look like this:
<div class="editable">{{content}}</div>
which would change to the edit mode when you clicked on it, which would look like this:
<input type="text" value="{{content}}" />
and you could revert back to display mode (persisting it's changes appropriately) by either hitting enter or pressing a button.
It seems meteor doesn't make this incredibly easy, since my first attempts with html:
<template name="editable">
{{#if editing}}
<input type="text" value={{this}} />
{{else}}
<div class="edit-thing">{{this}}</div>
{{/if}}
</template>
// In the appropriate display template.
{{> editable stuff}}
and js:
Template.user.stuff = "yo yo yo";
Template.editable.events({
'click .edit-thing': function(e) {
this.isEditing = true;
}
});
Template.editable.helpers({
editing: function() {
return !!this.isEditing;
}
});
have had problems with not being reactive, which using the Deps library didn't solve. (This version just wouldn't change when you clicked it, since this.isEditing isn't reactive and doesn't trigger a change in the editing helper.)
Ask for more information if you like! Thanks!
That's a typical use case for Deps, did you remember to use both depend and changed? The js code may look like this:
Template.editable.created = function() {
this.data.isEditing = false;
this.data.isEditingDep = new Deps.Dependency();
};
Template.editable.events({
'... whatever to start edit mode ...': function(e, t) {
t.data.isEditing = true;
t.data.isEditingDep.changed();
},
'... whatever to close edit mode ...': function(e, t) {
t.data.isEditing = false;
t.data.isEditingDep.changed();
},
});
Template.editable.editing = function() {
this.isEditingDep.depend();
return this.isEditing;
};