b-form-datepicker show only month and year - javascript

I have a b-form-datepicker. I want this b-form-datepicker to only select month and year. Is this possible ?
I couldn't find an example in bootstrap-vue.

After spend some time on this requirement, I came up with this work around solution. Please have a look and see if it can help you.
new Vue({
el: '#app',
data() {
return {
value: '',
displayValue: '',
selected: '',
formattedStr: ''
}
},
methods: {
onContext(ctx) {
// The date formatted in the locale, or the `label-no-date-selected` string
let formattedStr = ctx.selectedFormatted;
const modifyFormattedStr = ctx.selectedFormatted.split(',').slice(1);
const month = modifyFormattedStr[0].trim().split(' ').shift();
const year = modifyFormattedStr[1];
this.formattedStr = `${month}, ${year}`
// The following will be an empty string until a valid date is entered
this.selected = ctx.selectedYMD.split('-').slice(0, -1).join('-');
this.displayValue = this.selected
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/vue#2.6.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<label for="example-input">Choose a date</label>
<b-input-group>
<b-form-input
id="example-input"
v-model="displayValue"
type="text"
placeholder="YYYY-MM"
autocomplete="off"
></b-form-input>
<b-input-group-append>
<b-form-datepicker
v-model="value"
button-only
right
locale="en-US"
aria-controls="example-input"
#context="onContext"
></b-form-datepicker>
</b-input-group-append>
</b-input-group>
<p class="mb-1">Selected: '{{ selected }}'</p>
<p class="mb-1">Formatted String: '{{ formattedStr }}'</p>
</div>

Related

How to render an array inside another object array with Vue Js

I am working with Laravel Vuejs, and from my database I obtain an object array called arrayService, through axios I perform a get where the array I obtain can be seen represented.
var app = new Vue({
el: '#app',
mounted() {
//this.getService()
},
data() {
return {
arrayService: [
{ service: '2', format: [".mp3",".mp4"] },
{ service: '3', format: [".jpg",".png"] },
],
arrayFormat: [".mp3",".mp4",".jpg",".png"]
}
},
methods:
{
getService() {
axios.get('/').then(function(response){
this.arrayService = response.data
/*I GET FROM THE DATABASE
arrayService: [
{ service: '2', format: [".mp3",".mp4"] },
{ service: '3', format: [".jpg",".png"] },
],
*/
$.each(response.data, function (key,value) {
$.each(JSON.parse( value.format ), (key,element) => {
this.arrayFormat.push(element)
/*RESULT OF PARSE:
arrayFormat: [
{ [".mp3",".mp4",".jpg",".png"] }
]
*/
})
})
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div id="app">
<div>
<div class="row">
<div class="col-sm-6">
<h5>Wrong result :</h5>
<div v-for="service in arrayService" :key="service.id">
<strong>Id Service:</strong> {{service.service}}
<br>
<strong>Format:</strong>
<div v-for="format in arrayFormat" :key="format.id">
{{format}}
</div>
</div>
</div>
<div class="col-sm-6">
<h5>Correct result:</h5>
<strong>Id Service:</strong> 2
<br>
<strong>Format:</strong>
<br>
.mp3
<br>
.mp4
<br>
<strong>Id Service:</strong> 3
<br>
<strong>Format:</strong>
<br>
.jpg
<br>
.png
<br>
</div>
</div>
<br>
<br>
<br>
<br><br>
<br>
<br>
<br>
<br>
<br>
</div>
</div>
When I store the array arrayService, what I do is a Parse, inside the format attribute, since there is another array with the formats of each service. (see comments).
By doing this Parse, I finally do a push to store all these elements (formats) in an array called arrayFormat.
The problem I am having is that when doing that push, it stores everything together, and it is not what I am looking for.
What I am looking for is to store each format, regarding its service.
In the HTML view I tried to show the correct result, but the idea is to put all this together with VueJS.
Any idea?
You don't need the arrayFormat array at all, since the data structure you need is already in the API response.
You can iterate the nested array (service.format) directly:
<div v-for="service in arrayService" :key="service.service">
... 👇
<div v-for="format in service.format" :key="format">
{{format}}
</div>
</div>
new Vue({
el: '#app',
data() {
return {
arrayService: [{
service: '2',
format: [".mp3", ".mp4"]
},
{
service: '3',
format: [".jpg", ".png"]
},
],
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div id="app">
<div v-for="service in arrayService" :key="service.service">
<strong>Id Service:</strong> {{service.service}}
<br>
<strong>Format:</strong>
<div v-for="format in service.format" :key="format">
{{format}}
</div>
</div>
</div>

BootstrapVue form tags input v-model

I have a b-form-tag like this:
<b-form-group label="Search" label-for="search">
<b-form-tags
id="search"
v-model="search"
separator=","
remove-on-delete
tag-variant="primary"
tag-pills
placeholder="Search here..."
></b-form-tags>
</b-form-group>
And in the data section:
data() {
return {
search: []
}
}
In the search variable only tags will be stored, buy I also need to access to the current typing text of the input and bind it to one variable in data. I know it must be done using inputAttrs or inputHandlers but I don't know how?
You can use custom inputs. This will force you to recreate some functionality for clearing the input and adding tags. Here is a demo where I've simplified the docs' advanced example. It initializes the tag value, reimplements adding tags with Enter, and shows setting v-model programatically:
new Vue({
el: "#app",
data() {
return {
newTag: 'starting text',
value: []
}
},
methods: {
resetInputValue() {
this.newTag = ''
},
setTag(text) {
this.newTag = text;
}
}
});
<div id="app">
<b-form-tags
v-model="value"
#input="resetInputValue()"
tag-variant="success"
class="mb-2 mt-2"
placeholder="Enter a new tag value and click Add"
>
<template v-slot="{tags, inputId, placeholder, addTag, removeTag }">
<b-input-group>
<!-- Always bind the id to the input so that it can be focused when needed -->
<b-form-input
v-model="newTag"
:id="inputId"
:placeholder="placeholder"
#keydown.enter="addTag(newTag)"
></b-form-input>
<b-input-group-append>
<b-button #click="addTag(newTag)" variant="primary">Add</b-button>
</b-input-group-append>
</b-input-group>
<div v-if="tags.length" class="d-inline-block" style="font-size: 1.5rem;">
<b-form-tag
v-for="tag in tags"
#remove="removeTag(tag)"
:key="tag"
:title="tag"
class="mr-1"
>{{ tag }}</b-form-tag>
</div>
<b-form-text v-else>
There are no tags specified. Add a new tag above.
</b-form-text>
</template>
</b-form-tags>
<div>Text from `v-model`: {{ newTag }}</div>
<div><button #click="setTag('programatically')">Set v-model programatically</button></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue#2.6.12/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.19.0/bootstrap-vue.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.19.0/bootstrap-vue.min.css" rel="stylesheet" />

How to dynamically add data in data() from a v-for

I'm collecting questions from my database. I post these questions with v-for. I also create an input for users to answer these questions.
<el-form v-model="answer" class="form-bilan" label-position="top">
<el-form-item label="test" :label="question.question_without_html"
class="form-bilan__label-input"
:for="'question_' + question.id"
:key="question.id"
v-for="question in questions.questions"
v-if="question.type == 'label-input'">
<el-input :id="'question_' + question.id"></el-input>
</el-form-item>
<div style="clear: both;"></div>
</el-form>
I would like to link these input (answers) to an object in data() to be able to send them with axios on my server
Do you have an idea of how?
data() {
return {
answer: {
},
}
}
Thanks !
You need to use v-model and bind the answers to a property in data, here a functional example:
var demo = new Vue({
el: '#demo',
data: {
answer: null,
questions: {
questions: [
{ id: 1, type: 'label-input'},
{ id: 2, type: 'label-input'},
]
},
answers: {}
},
methods: {
doClickAnswers () {
console.clear();
console.log(this.answers)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<div id="demo">
<el-form
v-model="answer"
class="form-bilan"
label-position="top">
<el-form-item
label="test"
style="max-width: 200px;display: inline-block;margin: 0;"
:label="question.question_without_html"
:for="'question_' + question.id"
:key="question.id"
v-for="question in questions.questions"
v-if="question.type == 'label-input'">
<el-input :id="'question_' + question.id" v-model="answers[question.id]"></el-input>
</el-form-item>
</el-form>
<button type="button" #click="doClickAnswers">answers</button>
</div>

Animation of each element separately in the v -for loop (Vue.JS)

I made a simple todo app using VueJS.
I also added vue2-animate (A Vue.js 2.0 port of Animate.css. For use with Vue's built-in transitions.)
Animation of adding one element works correctly.
But there were two problems that I would like to solve without unnecessary coding:
Animation display for the list of downloaded from local storage
works for all items simultaneously. I need the animation to work
sequentially for each item separately.
Animation of deleting an item does not work correct - the last
item is always removed, and then a shift follows.
P.S.: Look demo in JSFiddle, because localstorage don't work in SO snippets.
Vue.component("adder", {
data: function() {
return {
task: ""
};
},
template: `
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="New task..." aria-label="New task..." aria-describedby="" v-model="task" v-on:keyup.enter="add">
<div class="input-group-append">
<button class="btn btn-primary" id="" v-on:click="add" >+</button>
</div>
</div>
`,
methods: {
add: function() {
this.$emit("addtodo", {
title: this.task,
done: false
});
this.task = "";
}
}
});
Vue.component("todo", {
props: ["item"],
template: `
<a href="#" class="list-group-item list-group-item-action task" v-bind:class="{'disabled done' : item.done==true}">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="" id="" value="checkedValue" v-model="item.done"> {{item.title}}
</label>
<button type="button" class="close" aria-label="Close" v-on:click="del">
<span aria-hidden="true">×</span>
</button>
</a>
`,
methods: {
del: function() {
this.$emit("deletetodo");
}
}
});
Vue.component("todos", {
props: ["items"],
template: `
<div class="list-group">
<transition-group name="bounceLeft" tag="a">
<todo v-for="(item, index) in items" :key="index" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>
</transition-group>
</div>
`,
methods: {
delTodo: function(i) {
this.$emit("deletetodo", i);
}
}
});
Vue.config.devtools = true;
let app = new Vue({
el: ".todoapp",
data: {
title: "Todo App",
items: []
},
methods: {
addTodo: function(e) {
this.items.push(e);
},
delTodo: function(i) {
this.items = this.items.filter(e => e != i);
}
},
mounted() {
if (localStorage.items) {
this.items = JSON.parse(localStorage.getItem("items"));
}
},
watch: {
items: {
handler(val) {
localStorage.setItem("items", JSON.stringify(this.items));
},
deep: true
}
}
});
.done>label {
text-decoration: line-through;
}
.task {
padding-left: 36px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Todo App</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<link rel="stylesheet" href="https://unpkg.com/vue2-animate/dist/vue2-animate.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container todoapp">
<div class="row">
<br />
</div>
<div class="card">
<div class="card-header">
{{ title }}
</div>
<div class="card-body">
<adder v-on:addtodo="addTodo"></adder>
<todos :items.sync="items" v-on:deletetodo="delTodo"></todos>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="script.js"></script>
</body>
</html>
JSFiddle demo
Ok taking this one at a time:
Deleting a task
The reason it always seems to be the last task being removed is because you are keying your list items by index. When you replace the whole items array in your delTodo method that in turn gives you a new array with new keys for each item in the list. Key by item and you'll get the right result:
<todo v-for="(item, index) in items" :key="item" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>
Showing tasks one at a time on load
My advice would be to approach the showing/hiding of tasks with a computed property:
computed: {
tasks: function(){
return this.items.filter(item => item.isVisible);
}
}
Here we'll show/hide by toggling isVisible on each task.
This means when you initially load the tasks from local storage you could set them all to isVisible: false and then use a setTimeout in a for loop to display them all one at a time:
mounted() {
// Get your items and set all to hidden
if (localStorage.items) {
this.items = JSON.parse(localStorage.getItem("items"))
.map(item => item.isVisible = false);
}
// Loop through and show the tasks
for(let i=1; i<=this.items.length; i++){
// Where 300 is milliseconds to delay
let delay = i * 300;
setTimeout(function(){
this.items[i].isVisible = true;
}.bind(this), delay);
}
},
Best of all, phased addition to the items array worked:
mounted() {
let items = [];
if (localStorage.items) {
items = JSON.parse(localStorage.getItem("items"))
}
for (let i = 0; i < items.length; i++) {
let delay = i * 1000;
setTimeout(
function() {
this.items.push(items[i])
}.bind(this),
delay
)
}
}
Just to add to the conversation, the following achieves the staggering within a Vuex's Action and using fat arrow syntax:
async fetchRepositories( {commit} ){
const response = await gitHubApi.get<Repository[]>('/users/rodolphocastro/repos') // Calling API with Axios
const staggered: Repository[] = []
response.data.forEach((r, i) => {
const delay = i * 300 // 300m -> Time to wait for each item in the array
setTimeout(() => {
staggered.push(r)
commit('setRepositories', staggered)
}, delay)
})
}

Vue bootstrap b-form-select sets vuelidate's $anyDirty to true on load

The issue I'm having is due to the input event that's being run when I first set a value to the v-model, this data is being loaded in via an API; I understand why the form is being set to dirty (as this it is being changed) but this causes problems in another component I have which checks if the $v.$anyDirty flag is sets to true and if it is, creates a pop up to say "are you sure you want to navigate away" but calling $v.reset() after the data is loaded doesn't work.
Vue.use(vuelidate.default);
const { required } = window.validators;
new Vue({
el: "#app",
data: {
todos: [],
todo: ""
},
async created() {
var data = await axios.get("https://jsonplaceholder.typicode.com/todos");
this.todos = data.data.map(d => d.id);
this.todo = this.todos[0];
this.$v.$reset()
},
validations() {
return {
todo: { required }
};
}
});
<link href="https://unpkg.com/bootstrap-vue#2.0.0-rc.11/dist/bootstrap-vue.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap#4.1.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.0.0-rc.11/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.7.4/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.7.4/dist/vuelidate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
<div id='app'>
<div class="row">
<div class="col-md-4">
<b-form-select v-model="$v.todo.$model" :options="todos"></b-form-select>
</div>
<div class="col-md-8">
<code>
$anyDirty: {{$v.$anyDirty}}
</code>
</div>
</div>
</div>
The problem is that $v.reset() is being run before vue renders so the input events happen after, so the stack trace would look like this
load > set values > reset validation > render > input event
You need to put the reset inside Vue.nextTick and then it'll work as it'll change the execution to be
load > set values > render > input event > reset validation
Vue.use(vuelidate.default);
const {
required
} = window.validators;
new Vue({
el: "#app",
data: {
todos: [],
todo: ""
},
async created() {
var data = await axios.get("https://jsonplaceholder.typicode.com/todos");
this.todos = data.data.map(d => d.id);
this.todo = this.todos[0];
Vue.nextTick(() => {
this.$v.$reset()
})
},
validations() {
return {
todo: {
required
}
};
}
});
<link href="https://unpkg.com/bootstrap-vue#2.0.0-rc.11/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap#4.1.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.0.0-rc.11/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.7.4/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.7.4/dist/vuelidate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
<div id='app'>
<div class="row">
<div class="col-md-4">
<b-form-select v-model="$v.todo.$model" :options="todos"></b-form-select>
</div>
<div class="col-md-8">
<code>
$anyDirty: {{$v.$anyDirty}}
</code>
</div>
</div>
</div>
As a note, you can also call Vue.nextTick with this.$nextTick

Categories