I have a problem with a dialog modal who don,t want to close even with a function
I have this code to reset and close the modal, the resetting is ok but nothing close with cancel, I use the resetForm() function
I have a form with this structure ( form and rules )
<template>
<div class="text-center">
<v-dialog
v-model="dialog"
>
<template v-slot:activator="{ on }">
<v-btn class="addGroup" v-on="on">Add a group</v-btn>
</template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-card>
<v-card-title
class="headline grey lighten-2"
primary-title
>
Add a group
</v-card-title>
<v-stepper v-model="e1">
<v-stepper-header class="stepHeader">
<v-stepper-step :complete="e1 > 1" step="1">Choose a name</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 2" step="2">Select fixtures</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 3" step="3">Apply a recipe</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 4" step="4" >Select dates</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="5">Summary</v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1" class="mt-6">
<v-card
>
<div class="step chooseName">
<h3>Create a group</h3>
<p class="mt-12">Choose a name for your group</p>
<v-text-field
:rules="rules.name"
#keyup.enter="e1 = 2"
v-model="form.groupName"
></v-text-field>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 2"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="2" class="mt-6">
<v-card
>
<div class="step selectFixtures">
<h3><v-icon #click="e1 = 1">mdi-chevron-left</v-icon>Select fixtures</h3>
<p class="mt-12 mb-6"
>Choose fixtures you want associate with {{form.groupName}}.</p>
<v-select
:rules="rules.fixture"
v-model="form.selectedFixture"
:items="fixtures"
item-text="name"
></v-select>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 3"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="3" class="mt-6">
<v-card
flat
height="68vh"
rounded
>
<div class="step applyRecipe">
<h3><v-icon #click="e1 = 2">mdi-chevron-left</v-icon>Apply a recipe to your group</h3>
<p class="mt-12 mb-6">Select a recipe for the group {{groupName}}.</p>
<div class="selectRecipe">
<v-select
class="mr-6"
v-model="form.selectedRecipe"
:items="recipe"
item-text="name"
item-value="duration"
return-object>
</v-select>
<p>or</p>
<create_recipe></create_recipe>
</div>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 4"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="4" class="mt-6">
<v-card
>
<div class="selectDates">
<h3><v-icon #click="e1 = 3">mdi-chevron-left</v-icon>Select start date</h3>
<p class="mt-6">Select the launch date of the Tuscan sun recipe for the {{groupName}} group</p>
<p>The end date will be set automatically according to the parameters of your recipe</p>
<v-date-picker
class="mt-6 ml-6"
v-model="selectedDate"
no-title
:date-format="date => new Date(date).toDateString()"
>
</v-date-picker>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 5"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="5" class="mt-6">
<v-card
>
<div class="stepChooseName">
<h3><v-icon #click="e1 = 4">mdi-chevron-left</v-icon>Summary</h3>
<p class="answer">Group name :</p>
<p class="subtitle">{{form.groupName}}</p>
<v-divider></v-divider>
</div>
</v-card>
<v-btn
#click="submit()"
>
Add group
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</v-card>
</v-form>
</v-dialog>
</div>
</template>
I have this code to reset and close the modal, the resetting is ok but nothing close with cancel, I use the resetForm() function
I have a form with this structure ( form and rules )
<script>
import Create_recipe from "./create_recipe";
export default {
components: {Create_recipe},
props: ['groups', 'fixtures'],
groups: [],
data() {
const defaultForm = Object.freeze({
groupName: '',
selectedFixture: [],
selectedRecipe: '',
selectedScenario: '',
});
return {
form: Object.assign({}, defaultForm),
valid: true,
rules: {
name: [val => (val || '').length > 0 || 'This field is required'],
fixture: [val => (val || '').length > 0 || 'fixture is required'],
recipe: [val => (val || '').length > 0 || 'recipe is required'],
},
dialog: null,
defaultForm,
e1:0,
counterOfUnnamed:'',
checkbox: true,
fixtures: this.fixtures,
selectedDate: new Date().toISOString().substr(0, 10),
recipe: [{
id:1,
name: 'Tuscan sun',
duration: '5',
},
{ id:2,
name: 'Montreal summer',
duration: '10',
},
{ id:3,
name: 'French spring',
duration: '365',
}
],
}
},
methods: {
numberOfFixture() {
return this.form.selectedFixture.length;
},
resetForm () {
this.$refs.form.reset();
this.dialog = false
},
submit() {
if (!this.form.groupName) {
this.form.groupName = "Unnamed" + this.counterOfUnnamed;
this.counterOfUnnamed = this.counterOfUnnamed + 1;
this.counterOfUnnamed = parseInt(this.counterOfUnnamed);
}
var self = this;
http.post('group/create', {
name: self.form.groupName,
}).then((result) => {
self.groups.push(result);
self.resetForm();
})
},
},
computed: {
displayFixturesName() {
return this.form.selectedFixture.join(', ');
},
formIsValid() {
return (
this.form.selectedFixture &&
this.form.selectedRecipe
)
}
}
}
</script>
```
But when I launch the reset function, the form is reset but the validation appears.
How can I solve it ?
There is a bug in above code, check in console, "this" keyword in Vue js should not use inside fat array functions. By default the current context points to main Vue instance in any Vue components, methods and computed properties. Using this will change the reference. In this code, it gets struck when you are calling this.groups.push(result)
Better preserve the this reference and try to achieve the same by putting resetForm inside then block
submit() {
var self = this;
http.post('group/create', {
name: self.form.groupName,
}).then((result) => {
self.groups.push(result)
self.resetForm();
// still you face issue add this line here "self.dialog = false;"
})
.catch(err => {
// handle error
});
},
Your updated script:
<script>
import Create_recipe from "./create_recipe";
const defaultForm = Object.freeze({
groupName: '',
selectedFixture: [],
selectedRecipe: '',
selectedScenario: '',
});
export default {
components: {Create_recipe},
props: ['groups', 'fixtures'],
groups: [],
data() {
return {
form: Object.assign({}, defaultForm),
valid: true,
rules: {
name: [val => (val || '').length > 0 || 'This field is required'],
fixture: [val => (val || '').length > 0 || 'fixture is required'],
recipe: [val => (val || '').length > 0 || 'recipe is required'],
},
dialog: null,
defaultForm,
e1:0,
counterOfUnnamed:'',
checkbox: true,
fixtures: this.fixtures,
selectedDate: new Date().toISOString().substr(0, 10),
recipe: [{
id:1,
name: 'Tuscan sun',
duration: '5',
},
{ id:2,
name: 'Montreal summer',
duration: '10',
},
{ id:3,
name: 'French spring',
duration: '365',
}
],
}
},
methods: {
numberOfFixture() {
return this.form.selectedFixture.length;
},
resetForm () {
this.$refs.form.reset();
this.dialog = false
},
submit() {
if (!this.form.groupName) {
this.form.groupName = "Unnamed" + this.counterOfUnnamed;
this.counterOfUnnamed = this.counterOfUnnamed + 1;
this.counterOfUnnamed = parseInt(this.counterOfUnnamed);
}
var self = this;
http.post('group/create', {
name: self.form.groupName,
}).then((result) => {
self.groups.push(result);
self.form = Object.assign({}, defaultForm);
self.dialog = false;
})
},
},
computed: {
displayFixturesName() {
return this.form.selectedFixture.join(', ');
},
formIsValid() {
return (
this.form.selectedFixture &&
this.form.selectedRecipe
)
}
}
}
</script>
Related
here's the example of my simple form which throws an error when the text field is empty. On the first submit everything is ok, but after next you see the error before submitting second message. Here's code:
<div id="app">
<v-app >
<v-dialog v-model="dialog">
<template v-slot:activator="{ on }">
<v-btn v-on="on">Open</v-btn>
</template>
<v-card>
<v-text-field
v-model="note"
:rules="noteRules"
></v-text-field>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn #click="submit()">save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
</div>
and app.js
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
dialog: false,
note: '',
noteRules: [
v => !!v || 'error'
],
}),
methods: {
clear() {
this.dialog = false;
//something here?
}
}
})
how should I reset the validation? here's codepen https://codepen.io/tutu-kaen/pen/oNjPPKe
Wrap your content of the card in a v-form, add a v-model and a ref to that v-form.
You can then access that v-form in your clear() by using this.$refs..
The formValid will be a boolean so you can easily check if a form is valid by using if (this.formValid) anywhere in your code, for example to disable the send button.
Example:
<div id="app">
<v-app>
<v-dialog v-model="dialog">
<template v-slot:activator="{ on }">
<v-btn v-on="on">Open</v-btn>
</template>
<v-card>
<v-form v-model="formValid" ref="myForm">
<v-text-field v-model="note" :rules="noteRules"></v-text-field>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn #click="submit()">save</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
dialog: false,
note: '',
formValid: false,
noteRules: [
v => !!v || 'error'
],
}),
methods: {
submit() {
this.note = '';
this.dialog = false;
this.$refs.myForm.reset();
}
}
})
Codepen example
I am trying to add some custom logic to my semantic ui validation but can't figure out what I am doing wrong.
Basically, when the user selects "Yes" from the drop-down, I would like to make the "input_field" mandatory. If the user selects "No", the "input_field" becomes optional and the form can be submitted.
I tried searching for examples and followed some code from the Semantic Ui website but can't figure out what I am missing. Any advice would be appreciated as I am on a deadline for a project I am working on.
Form:
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
Validation:
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
},
input_field: {
identifier: 'input_field',
depends: 'select',
rules: [
{
type: 'empty',
prompt: function() {
$('.select').on('change', function() {
if( this.value == 'Yes') {
return "Custom Validation";
}
return false;
}).trigger("change");
}
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
});
</script>
Figured out a solution for this! It might not be the best answer but it works and does what I am looking for.
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
$('.selectOption').on('change', function() {
if ( this.value == 'Yes' ) {
$('.ui.form').form('add rule', 'input_field', ['empty', 'integer']);
$('.ui.form').form('add prompt', 'input_field', 'Enter an integer');
}
if ( this.value == 'No' ) {
$('.ui.form').form('remove prompt', 'input_field');
$('.ui.form').form('remove rule', 'input_field');
}
}).trigger("change");
});
</script>
I was able to implement the validation rule by extending Semantic UI setting rules.
See below example:
$.fn.form.settings.rules.dependsOnFieldValue = function (value, dependFieldValue) {
var identifier = dependFieldValue.split('[')[0];
var dependValue = dependFieldValue.match(/\[(.*)\]/i)[1];
if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
matchingValue = $('[data-validate="'+ identifier +'"]').val();
}
else if($('#' + identifier).length > 0) {
matchingValue = $('#' + identifier).val();
}
else if($('[name="' + identifier +'"]').length > 0) {
matchingValue = $('[name="' + identifier + '"]').val();
}
else if( $('[name="' + identifier +'[]"]').length > 0 ) {
matchingValue = $('[name="' + identifier +'[]"]');
}
return (matchingValue !== undefined)
? !( dependValue.toString() === matchingValue.toString() && value === '')
: false
;
};
Then in the form validation initializer you will pass the desired values as below:
$(".ui.form").form({
fields: {
select: {
identifier: 'select',
rules : [
{
type : 'empty'
}
]
},
input_field: {
identifier : 'input_field',
rules : [
{
type : 'dependsOnFieldValue[select[Yes]]',
}
]
},
...
}
});
Notice that we pass the <select> identifier (in this case also called select) within the first [] and then the value that we want to see to make the input_field mandatory ("Yes" in this case).
I have a problem with a dialog modal who don,t want to close even with a function
I have this code to reset and close the modal, the resetting is ok but nothing close with cancel, I use the resetForm() function
I have a form with this structure ( form and rules )
<template>
<div class="text-center">
<v-dialog
v-model="dialog"
>
<template v-slot:activator="{ on }">
<v-btn class="addGroup" v-on="on">Add a group</v-btn>
</template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-card>
<v-card-title
class="headline grey lighten-2"
primary-title
>
Add a group
</v-card-title>
<v-stepper v-model="e1">
<v-stepper-header class="stepHeader">
<v-stepper-step :complete="e1 > 1" step="1">Choose a name</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 2" step="2">Select fixtures</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 3" step="3">Apply a recipe</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 4" step="4" >Select dates</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="5">Summary</v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1" class="mt-6">
<v-card
>
<div class="step chooseName">
<h3>Create a group</h3>
<p class="mt-12">Choose a name for your group</p>
<v-text-field
:rules="rules.name"
#keyup.enter="e1 = 2"
v-model="form.groupName"
></v-text-field>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 2"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="2" class="mt-6">
<v-card
>
<div class="step selectFixtures">
<h3><v-icon #click="e1 = 1">mdi-chevron-left</v-icon>Select fixtures</h3>
<p class="mt-12 mb-6"
>Choose fixtures you want associate with {{form.groupName}}.</p>
<v-select
:rules="rules.fixture"
v-model="form.selectedFixture"
:items="fixtures"
item-text="name"
></v-select>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 3"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="3" class="mt-6">
<v-card
flat
height="68vh"
rounded
>
<div class="step applyRecipe">
<h3><v-icon #click="e1 = 2">mdi-chevron-left</v-icon>Apply a recipe to your group</h3>
<p class="mt-12 mb-6">Select a recipe for the group {{groupName}}.</p>
<div class="selectRecipe">
<v-select
class="mr-6"
v-model="form.selectedRecipe"
:items="recipe"
item-text="name"
item-value="duration"
return-object>
</v-select>
<p>or</p>
<create_recipe></create_recipe>
</div>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 4"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="4" class="mt-6">
<v-card
>
<div class="selectDates">
<h3><v-icon #click="e1 = 3">mdi-chevron-left</v-icon>Select start date</h3>
<p class="mt-6">Select the launch date of the Tuscan sun recipe for the {{groupName}} group</p>
<p>The end date will be set automatically according to the parameters of your recipe</p>
<v-date-picker
class="mt-6 ml-6"
v-model="selectedDate"
no-title
:date-format="date => new Date(date).toDateString()"
>
</v-date-picker>
</div>
</v-card>
<v-btn
color="primary"
#click="e1 = 5"
>
Continue
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="5" class="mt-6">
<v-card
>
<div class="stepChooseName">
<h3><v-icon #click="e1 = 4">mdi-chevron-left</v-icon>Summary</h3>
<p class="answer">Group name :</p>
<p class="subtitle">{{form.groupName}}</p>
<v-divider></v-divider>
</div>
</v-card>
<v-btn
#click="submit()"
>
Add group
</v-btn>
<v-btn
text
#click="dialog = !dialog"
>
Cancel</v-btn>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</v-card>
</v-form>
</v-dialog>
</div>
</template>
I have this code to reset and close the modal, the resetting is ok but nothing close with cancel, I use the resetForm() function
I have a form with this structure ( form and rules )
<script>
import Create_recipe from "./create_recipe";
export default {
components: {Create_recipe},
props: ['groups', 'fixtures'],
groups: [],
data() {
const defaultForm = Object.freeze({
groupName: '',
selectedFixture: [],
selectedRecipe: '',
selectedScenario: '',
});
return {
form: Object.assign({}, defaultForm),
valid: true,
rules: {
name: [val => (val || '').length > 0 || 'This field is required'],
fixture: [val => (val || '').length > 0 || 'fixture is required'],
recipe: [val => (val || '').length > 0 || 'recipe is required'],
},
dialog: null,
defaultForm,
e1:0,
counterOfUnnamed:'',
checkbox: true,
fixtures: this.fixtures,
selectedDate: new Date().toISOString().substr(0, 10),
recipe: [{
id:1,
name: 'Tuscan sun',
duration: '5',
},
{ id:2,
name: 'Montreal summer',
duration: '10',
},
{ id:3,
name: 'French spring',
duration: '365',
}
],
}
},
methods: {
numberOfFixture() {
return this.form.selectedFixture.length;
},
resetForm () {
this.$refs.form.reset();
this.dialog = false
},
submit() {
if (!this.form.groupName) {
this.form.groupName = "Unnamed" + this.counterOfUnnamed;
this.counterOfUnnamed = this.counterOfUnnamed + 1;
this.counterOfUnnamed = parseInt(this.counterOfUnnamed);
}
var self = this;
http.post('group/create', {
name: self.form.groupName,
}).then((result) => {
self.groups.push(result);
self.resetForm();
})
},
},
computed: {
displayFixturesName() {
return this.form.selectedFixture.join(', ');
},
formIsValid() {
return (
this.form.selectedFixture &&
this.form.selectedRecipe
)
}
}
}
</script>
```
But when I launch the reset function, the form is reset but the validation appears.
How can I solve it ?
In your case use this.$refs.form.resetValidation() for reset the validation after reset
resetForm () {
this.$refs.form.reset()
this.$refs.form.resetValidation()
},
Here is the working code
Codepen link: https://codepen.io/chansv/pen/QWWExBz?editors=1010
<div id="app">
<v-app id="inspire">
<v-form
ref="form"
v-model="valid"
>
<v-text-field
v-model="formObj.name"
:counter="10"
:rules="nameRules"
label="Name"
required
></v-text-field>
<v-text-field
v-model="formObj.email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
<v-btn
:disabled="!valid"
color="success"
class="mr-4"
#click="submitForm"
>
Submit
</v-btn>
<v-btn
color="error"
class="mr-4"
#click="reset"
>
Reset Form
</v-btn>
</v-form>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
const defaultForm = Object.freeze({
name: '',
email: '',
})
return {
formObj: Object.assign({}, defaultForm),
valid: true,
nameRules: [
v => !!v || 'Name is required',
v => (v && v.length <= 10) || 'Name must be less than 10 characters',
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+\..+/.test(v) || 'E-mail must be valid',
],
};
},
methods: {
submitForm () {
console.log(this.formObj);
},
reset () {
this.$refs.form.reset();
console.log(this.formObj);
},
},
})
If you are reseting the form , no need to set default value this.form = Object.assign({}, this.defaultForm)
This reset function automatically resets all the value in the form this.$refs.form.reset()
I would like to implement a date timepicker with Algolia. If I choose a date, all elements should be displayed from this date.
Unfortunately, I have no idea how I can make this with Agolia.
I hope you can help me.
const datePicker = instantsearch.connectors.connectRange(
(options, isFirstRendering) => {
if (!isFirstRendering) return;
new Calendar({
element: $('.daterange--single'),
current_date: new Date(),
format: {input: 'DD.MM.YYYY'},
required: false,
callback: function() {
const start = new Date().getTime();
refine([start]);
},
});
}
);
search.addWidget(
datePicker({
attributeName: 'date',
})
);
<div class="daterange daterange--single"></div>
now i have a working code. Now a have the problem.. how i can change in my custom widget the searchParameters?
const search = instantsearch({
appId: '0000000',
apiKey: '000000000000',
indexName: 'Events',
routing: true,
searchParameters:{
filters: 'dateNumeric >= 1531591200'
}
});
var customWidget = {
init: function(options) {
$( "#datetimepickerNotime" ).focusout(function() {
var date = $('#datefromdatetimepicker').val();
var date = new Date (date);
alert("dateNumeric >= 1512752400");
});
}
};
search.addWidget(customWidget);
<div class='input-group date' id="datetimepickerNotime">
<input type='text' id="datefromdatetimepicker" name="date" class="form-control" autocomplete="off" value="<f:format.date date='now' format='d.m.Y' />"/>
<span class="input-group-addon">
<span class="fa fa-calendar"></span>
</span>
</div>
import { Formik, Field } from 'formik';
import { connectRange } from 'react-instantsearch-dom';
import * as Yup from 'yup';
const RangeFilter = ({ currentRefinement, min, max, refine }) => {
const validationSchema = Yup.object().shape({
minValue: Yup.number().min(min, `Minimum value must be at least ${min}`),
maxValue: Yup.number().max(max, `Maximum value must be at most ${max}`),
});
const onSubmit = (values) => {
refine({ min: values.minValue, max: values.maxValue });
};
return (
<Formik
onSubmit={onSubmit}
validationSchema={validationSchema}
initialValues={{
minValue: currentRefinement?.min,
maxValue: currentRefinement?.max,
}}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="minValue">
{({ field, form: { errors, touched } }) => (
<div>
<label htmlFor="price-min">To price:</label>
<input
className="input is-shadowless"
{...field}
id="price-min"
type="number"
placeholder="Min price"
/>
{errors.minValue && touched.minValue && (
<p className="help is-danger">{errors.minValue}</p>
)}
</div>
)}
</Field>
<Field name="maxValue">
{({ field, form: { errors, touched } }) => (
<div className="mt-2">
<label htmlFor="price-max">From price:</label>
<input
{...field}
id="price-max"
type="number"
className="input is-shadowless"
placeholder="Max price"
/>
{errors.maxValue && touched.maxValue && (
<p className="help is-danger">{errors.maxValue}</p>
)}
</div>
)}
</Field>
<button type="submit" className="ais-RefinementList-showMore mt-2">
Get Result
</button>
</form>
)}
</Formik>
);
};
export default connectRange(RangeFilter);
I'm facing the problem that adding data to a subscribed Collection doesn't automatically refresh the shown elements of a collection. If I add a new element the element show's up for a second and then disappears! Refreshing the browser (F5) and the new element shows up.
I put the subscription into Meteor.autorun but things kept beeing the same.
lists.html (client):
<<template name="lists">
<div class="lists col-md-12" {{!style="border:1px red solid"}}>
<!-- Checklist Adder -->
<form id="list-add-form" class="form-inline" role="form" action="action">
<div class="col-md-6">
<input class="form-control" id="list-name" placeholder="Neue Liste" required="required"/>
</div>
<button type="submit" class="btn btn-primary" id="submit-add">
<span class="glyphicon glyphicon-plus-sign"></span>
Neue Liste
</button>
</form>
<!-- Checklist Ausgabe -->
<ul>
<br/>
{{#each lists}}
<li style="position: relative;" id="{{this._id}}" data-id="{{_id}}" class="clickOnList">
<!--<input type="button" class="deleteLists" id="{{this._id}}" value="-" style="z-index: 999;"/> -->
<span id="{{this._id}}" data-id="{{_id}}" style="padding-left: 10px; vertical-align:middle;">{{this.name}}</span>
<form id="changerForm_{{_id}}" class="changeList-name-form" data-id="{{_id}}" style="visibility: hidden; position: absolute; top:0;">
<input id="changerText_{{_id}}" type="text" class="list_name" data-id="{{_id}}" value="{{this.name}}" />
</form>
{{#if ownerOfList this._id}}
<a data-toggle="modal" class="userForListModal" id="{{this.name}}" data-id="{{this._id}}" data-target="#userForListModal">
<span class="glyphicon glyphicon-user" id="{{this.name}}" style="color:black;"data-id="{{this._id}}"></span><span style="color:black;" id="{{this.name}}" data-id="{{this._id}}" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>
<div class="deleteLists" id="dLBtn_{{_id}}" data-id="{{this._id}}" style="float: right; padding-right: 5px; padding-top: 1px; visibility: hidden;">
<span class="deleteLists glyphicon glyphicon-minus-sign" data-id="{{this._id}}"></span>
</div>
{{else}}
<a class="userForListModal">
<span class="glyphicon glyphicon-user" style="color:black;"></span><span style="color:black;" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>
{{/if}}
<!-- <button type="submit" class="deleteLists btn btn-default btn-xs" id="dLBtn_{{_id}}" data-id="{{this._id}}" style="float: right;" > -->
</button>
</li>
{{/each}}
</ul>
</div>
<div class="modal fade" id="userForListModal" >
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="userForListModalLabel"></h4>
</div></template>
<div class="modal-body col-md-12">
<div id="userForListModalUsers">
</div>
<p>Neuen Benutzer zur Checkliste hinzufügen:</p>
<form id="list-addUser-form" class="form-inline" role="form" action="action">
<div class="col-md-12">
<div class="col-md-6">
{{inputAutocomplete settings id="name-list-addUser" class="input-xlarge" placeholder="Benutzer Name" required="required"}}
</div>
<div class="col-md-6">
<button type="submit" class="btn btn-secondary" id="submit-list-addUser">
<span class="glyphicon glyphicon-plus-sign"></span>
Benutzer hinzufügen
</button>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<div id="userForListModalerrorMessage" style="color:red; display: none; text-align:left"></div><button type="button" class="btn btn-default" data-dismiss="modal">Schließen</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
<template name="userPill">
<span class="label" style="color:black">{{username}}</span>
lists.js (client):
Template.lists.lists = function(){ return Lists.find(); }
Lists = new Meteor.Collection("lists");
Deps.autorun(function() {
Meteor.subscribe('lists');
})
lists.js
var activeListName = "";
var activeListID = "";
Template.lists.lists = function()
{
return Lists.find();
}
Template.lists.memberCount = function(id)
{
var count = "";
Meteor.call("listMemberCount", id, function(error,result)
{
if (error) {
console.log("List not initialized:" + error.reason);
}
else
{
Session.set("countMember_"+id,result);
}
});
return Session.get("countMember_"+id);
}
Template.lists.ownerOfList = function(id)
{
return ( Meteor.userId() == Lists.findOne({_id : id}).owner);
}
Template.lists.settings = function()
{
return {
position: "top",
limit: 5,
rules: [
{
token: '',
collection: Meteor.users,
field: "username",
template: Template.userPill
}]
}
}
Template.lists.events({
'submit #list-add-form' : function(e, t) {
/* Checklisten ausgeben */
e.preventDefault();
var name = t.find('#list-name').value;
var id = new Meteor.Collection.ObjectID().valueOf();
var id_block = new Meteor.Collection.ObjectID().valueOf();
Lists.insert({_id : id, name : name, owner : Meteor.userId()});
Meteor.users.update({_id : Meteor.userId()}, {$addToSet :{lists : id}});
Listitems.insert({_id : id_block, name : "", items: []});
Lists.update(id, {$addToSet : {items : id_block}});
},
'click .clickOnList' : function(e)
{
/* Eventhandler fuer klick auf Checkliste */
Session.set("activeListId", e.target.id);
$("#"+e.target.id).siblings('li').removeClass("active");
$("#"+e.target.id).addClass("active");
},
'mouseover .clickOnList' : function (e,t) {
$( ".deleteLists" ).each(function( index, item ) {
if ( item.getAttribute("data-id") == e.target.getAttribute("data-id")) {
item.style.visibility = 'visible';
} else {
item.style.visibility = 'hidden';
}
});
},
'mouseleave .clickOnList' : function (e,t) {
$( ".deleteLists" ).each(function( index, item ) {
item.style.visibility = 'hidden';
});
},
'click .deleteLists' : function(e, t)
{
/* Eventhandler zum loeschen einer Checkliste */
var id = e.target.getAttribute("data-id");
Meteor.call("removeList", id);
console.log("test");
},
'click .changeListnameButton' : function(e,t) {
var id = e.target.getAttribute("data-id");
document.getElementById("changerForm_" + id).style.visibility = 'visible';
document.getElementById(id).style.visibility = 'hidden';
document.getElementById("changerText_" + id).focus();
},
'dblclick .clickOnList' : function(e,t){
var id = e.target.getAttribute("data-id");
document.getElementById("changerForm_" + id).style.visibility = 'visible';
document.getElementById(id).style.visibility = 'hidden';
document.getElementById("changerText_" + id).focus();
},
'submit .changeList-name-form' : function(e,t) {
e.preventDefault();
var id = e.target.getAttribute("data-id");
var text = document.getElementById("changerText_" + id).value;
if(text != '') {
Meteor.call("changeListName", id, text);
}
if (Session.get("activeListId", e.target.id) == id ) {
Session.set("activeListName", text);
}
document.getElementById("changerForm_" + id).style.visibility = 'hidden';
document.getElementById(id).style.visibility = 'visible';
},
'blur .list_name' : function(e,t) {
e.preventDefault();
var id = e.target.getAttribute("data-id");
var text = document.getElementById("changerText_" + id).value;
if((text != '') && (document.getElementById(id).style.visibility == 'hidden')) {
Meteor.call("changeListName", id, text);
}
if (Session.get("activeListId", e.target.id) == id ) {
Session.set("activeListName", text);
}
document.getElementById("changerForm_" + id).style.visibility = 'hidden';
document.getElementById(id).style.visibility = 'visible';
},
'click .userForListModal' : function(e,t) {
e.preventDefault();
activeListName = e.target.id;
activeListID = e.target.getAttribute("data-id");
//console.log(activeListID + " " + activeListName);
//console.log("New user for Liste" + Lists.findOne({_id : activeListID}).name);
userForList(activeListID);
$("#userForListModalLabel").html("Benutzer der Liste '"+ activeListName+ "'");
},
'submit #list-addUser-form' : function(e,t) {
e.preventDefault();
var newUser = $('#name-list-addUser').val();
Meteor.call("addUserToList", newUser, activeListID, function(error,result)
{
if (error) {
console.log(error.reason);
}
else
{
if (result == 1) {
$('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
$('#userForListModalerrorMessage').html("<div class=\"alert alert-danger\">Benutzer wurde nicht gefunden...</div>");
}
else if (result == 2) {
$('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
$('#userForListModalerrorMessage').html("<div class=\"alert alert-warning\">Benutzer ist Besitzer der Liste...</div>");
}
}
});
}
});
function userForList(id)
{
try
{
var owner = Lists.findOne({_id : id}).owner;
var members = Lists.findOne({_id : id}).member;
}
catch(e){
}
output = "<ul>";
output += "<li> Besitzer der Liste: <ul><li>" + owner + "</li></ul></li>";
output += "<li>Mitarbeiter der Liste: <ul>"
if (members != undefined) {
for(i=0; i<members.length; i++)
{
output+= "<li>" + members[i] + "</li>";
}
}
output += "</ul></li></ul>";
$('#userForListModalUsers').html(output);
}
main.js (server):
Lists = new Meteor.Collection("lists");
Meteor.publish("lists", function(){
var ListsOfUser = Meteor.users.findOne({_id : this.userId}).lists;
return Lists.find({_id :{ $in : ListsOfUser}});
});
Lists.allow({
insert : function(userID, list)
{
return (userID && (list.owner === userID));
},
//todo
update : function(userID)
{
return true;
},
//todo
remove : function(userID)
{
return true;
}
});
Thanks in advance!
I believe this is happening because the ListsOfUser variable in your Meteor.publish "lists" function is not a reactive data source. ListsOfUser is an array drawn from your result set, not a reactive cursor. Therefore it is not being invalidated server side when a user adds a new list on the client. From the Meteor docs (note the last sentence especially):
If you call Meteor.subscribe within a reactive computation, for example using
Deps.autorun,the subscription will automatically be cancelled when the computation
is invalidated or stopped; it's not necessary to call stop on subscriptions made
from inside autorun. However, if the next iteration of your run function subscribes
to the same record set (same name and parameters), Meteor is smart enough to skip a
wasteful unsubscribe/resubscribe.
ListsOfUser is not changing when a user adds a new list, so you are not being unsubscribed and resubscribed to the lists publication. (Note also that Meteor.users.findOne() is also not a reactive data source - you might want to switch it to Meteor.users.find() depending on how you go about making ListsOfUser reactive).
There are a few ways you could go about making the user lists reactive.
First, you could publish both the user cursor and the lists cursor, either separately or as an array in the same publish function, place both subscriptions in your Deps.autorun, and then fish out the user lists client side in a helper.
Meteor.publish("userWithLists", function(){
return Meteor.users.find(
{_id: this.userId},
{fields: {'lists': 1}}
);
});
Second, you could publish the static array of user lists as its own Collection and then use cursor.observe or cursor.observeChanges to track when it changes. While my understanding is that this is closest to the "correct" or "Meteor" way of doing it, it is also apparently quite verbose and I have not tried it. This tutorial goes into great detail about how you would tackle something like this: https://www.eventedmind.com/feed/aGHZygsphtTWELpKZ
Third, you could simply stick the user lists into your Session object, which is already reactive, and then publish your Lists.find() based on the Session, i.e.:
Meteor.publish("lists", function(lists){/* find code goes here */});
and
Deps.autorun(function(){
Meteor.subscribe("lists", Session.get("listsOfUser"));
});
This last one is probably an overuse / abuse of the Session object, particularly if your listsOfUser grows large, but should work as a hack.
I know this question is old, but still someone might be seeking for the answer. And the answer is Meteor.publishComposite available with a publish-composite package - https://atmospherejs.com/reywood/publish-composite
And there's an example there
Meteor.publishComposite('topTenPosts', {
find: function() {
// Find top ten highest scoring posts
return Posts.find({}, { sort: { score: -1 }, limit: 10 });
},
children: [
{
find: function(post) {
// Find post author. Even though we only want to return
// one record here, we use "find" instead of "findOne"
// since this function should return a cursor.
return Meteor.users.find(
{ _id: post.authorId },
{ limit: 1, fields: { profile: 1 } });
}
},
{
find: function(post) {
// Find top two comments on post
return Comments.find(
{ postId: post._id },
{ sort: { score: -1 }, limit: 2 });
},
children: [
{
find: function(comment, post) {
// Find user that authored comment.
return Meteor.users.find(
{ _id: comment.authorId },
{ limit: 1, fields: { profile: 1 } });
}
}
]
}
]
});
I am rather new to meteor but, in your server code, should it not be:
var ListsOfUser = Meteor.users.findOne({_id : Meteor.userId}).lists;
rather than:
var ListsOfUser = Meteor.users.findOne({_id : Meteor.userId}).lists;