Good afternoon folks,
How can I send a Get request every time with a search button on my app?
I don`t want to download the whole JSON from the API just only the search results?
Many thanks.
That is my search bar:
<div id="app">
<div class="search-wrapper">
<input type="text"
class="search-bar"
v-model="search"
placeholder="Search in the titles"/>
</div>
and this is the axios part I have :
axios
.get(
`https://zbeta2.mykuwaitnet.net/backend/en/api/v2/media-center/press-release/?page_size=61&type=5`
)
.then((response) => {
this.items = response.data.results;
});
Step 1: Create HTML template
<div class="search-wrapper">
<form>
<input
type="search"
#input="searchByTitle($event.target.value)"
class="search-bar"
v-model="search"
placeholder="Search by title"
/>
</form>
</div>
Step 2: Add search by title method
searchByTitle(value) {
clearTimeout(this.debounce);
this.debounce = setTimeout(() => {
if (value.length >= 3) {
this.params = `q=${value}`;
this.loadPressRelease();
}
}, 600);
},
We have to make the REST API call when there is 3 or more than 3 characters and added debounce once we stopped typing then only we are making REST API call
Step 3: Added watch for when clearing search text
watch: {
search(newVal, oldVal) {
if (oldVal && !newVal) {
console.log("morning has broken...");
}
if (!newVal) {
this.clearSearch();
}
},
},
Step 4: Added searchbar css style
.search-bar {
border: 1px solid #ccc;
outline: 0;
border-radius: 10px;
width: 50%;
margin-left: 10px;
padding: 0.5rem;
}
.search-bar:focus {
box-shadow: 0 0 15px 5px #b0e0ee;
border: 2px solid #bebede;
}
.search-box:not(:valid) ~ .close-icon {
display: none;
}
DEMO
Related
I am trying to get a local Vue project working where there is an E-Library Accordion with 3 types of media and a dropdown button to filter between the types of media. I am also trying to get a color coding system going and I attempted this through created a class and trying to bind it (not working). I am trying to filter the media with a method and then applying that method as an #click event to each dropdown button, but I know this is probably poor practice or just not the correct way to do this. Can anyone point me in the right direction as to how to get these two features working correctly? Much appreciated.
This is my page that I am routing to and creating the code on for the Vue project:
<template>
<label for="accordion">Choose type of media:</label>
<select name="accordion-types" id="types">
<option #click="filteredAccordion(index)" value="book">Book</option>
<option #click="filteredAccordion(index)" value="dvd">DVD</option>
<option #click="filteredAccordion(index)" value="stream">Streaming Video</option>
</select>
<div v-for="(accordion, index) in accordions" :key="index">
<button :class="{red: accordions.type === 'Book' }" #click="toggleOpen(index)">
{{ accordion.title }}
</button>
<div class="panel" :class="{open: accordion.isOpen}">
<p>{{ accordion.content }} </p>
<p>{{ accordion.type }}</p>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
accordions: []
};
},
created: function () {
const appData = this;
axios.get("data.json").then(function (response) {
appData.accordions = appData.addIsOpen(response.data.accordions);
});
},
methods: {
addIsOpen: function(items) {
return items.map(function(item) {
item.isOpen = false;
return item;
});
},
toggleOpen: function(index) {
this.accordions[index].isOpen = !this.accordions[index].isOpen;
}
},
computed: {
filteredAccordion: function(items) {
return this.accordions.filter(accordions => !accordions.type.indexOf(this.type));
}
}
};
</script>
<style>
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.active, .accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
background-color: white;
display: none;
overflow: hidden;
}
.panel.open {
display: block;
}
.red {
color: red;
}
</style>
This is an unwanted animation bug that seems to be normal, but I want the element to be animationed which is in <input v-model="todo"/>. The problem is each time I press on add button to add the todo name (<input v-model="todo"/>), the last element 'last' runs the animation.
P.s. I tried to keep the code as simple as possible.
Vue.createApp({
data() {
return {
todo: "",
todo_list: ['last'],
};
},
methods: {
add() {
this.todo_list.unshift(this.todo);
console.log(this.todo_list);
},
remove(index) {
this.todo_list.splice(index, 1);
},
},
}).mount('#app');
.list-enter-active,
.list-leave-active {
transition: all 200ms ease-out;
}
.list-enter-from {
transform: translateY(-20px);
}
.list-leave-to {
opacity: 0;
transform: translateX(20px);
}
article {
width: 50%;
padding: 10px;
background-color: #dddddd;
border: 1px solid #cfcfcf;
border-radius: 5px;
margin-block: 10px
}
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<input type="text" v-model="todo" /> <button #click="add">add</button>
<transition-group name="list" tag="section">
<article v-for="(todo, index) in todo_list" :key="index" #click="remove(index)">
<span>{{ todo }}</span>
</article>
</transition-group>
</div>
The reason you're experiencing this issue, is because you're using the index as a key.
If you added the elements to the end of the list, this would be fine.
But since you're adding it to the start, it will cause the issue you're seeing.
I'd suggest you make each todo an object, and add a unique identifier to each object. This can be a simple integer that you increment. Then you can use that property as the key.
Example
let id = 1;
Vue.createApp({
data() {
return {
todo: "",
todo_list: [{
id: id++,
value: 'last'
}],
};
},
methods: {
add() {
const todo = {
id: id++,
value: this.todo
}
this.todo_list.unshift(todo);
},
remove(index) {
this.todo_list.splice(index, 1);
},
},
}).mount('#app');
.list-enter-active,
.list-leave-active {
transition: all 200ms ease-out;
}
.list-enter-from {
transform: translateY(-20px);
}
.list-leave-to {
opacity: 0;
transform: translateX(20px);
}
article {
width: 50%;
padding: 10px;
background-color: #dddddd;
border: 1px solid #cfcfcf;
border-radius: 5px;
margin-block: 10px
}
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<input type="text" v-model="todo" /> <button #click="add">add</button>
<transition-group name="list" tag="section">
<article v-for="(todo, index) in todo_list" :key="todo.id" #click="remove(index)">
<span>{{ todo.value }}</span>
</article>
</transition-group>
</div>
currently drag and drop feature is working with vue2, i want to achieve same feature using vue3 composition api.
vue2 code:
<div id="app">
<div id="box-droppable1" #drop="drop" #dragover="allowDrop">
<h3>Draggaable area 1:</h3>
<hr>
<div class="" draggable="true" #dragstart="onDragging" id="123">
<h2>Drag mee</h2>
<p>this is a text</p>
</div>
<img id="img-draggable" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" draggable="true" #dragstart="drag" width="336">
</div>
<div id="box-droppable2" #drop="drop" #dragover="allowDrop">
<h3>Droppable area 2:</h3>
<hr>
</div>
</div>
Here is vuejs code done using vuejs options API.
JS:
new Vue({
el: '#app',
data(){
return {
};
},
methods : {
onDragging(ev){
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
//this.$store.commit('module/namespace', status);
},
allowDrop(ev) {
ev.preventDefault();
},
drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
},
drop(ev) {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
},
})
css:
#app{
width: 100%;
display: flex;
#box-droppable1 {
width: 50%;
background-color: coral;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
#box-droppable2 {
width: 50%;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
}
---------------------#---------------------------------------------------------------------------------------#------------------
codepen
As the comments already mention, this is nothing that would be different in the composition API, which is just another way to define a component.
All the methods you have in the options API, you can just have them in the setup method and return them:
setup() {
const onDragging = (ev) => {
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
};
const allowDrop = (ev) => {
ev.preventDefault();
};
const drag = (ev) => {
ev.dataTransfer.setData("text", ev.target.id);
};
const drop = (ev) => {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
return {
onDragging,
allowDrop,
drag,
drop,
}
}
I would probably not directly append a child with vanila js but also do it the Vue way, but that's just a side note.
Being new to Vue, came across the following use-case. I have an accordion component that is reused across the application. The accordion simply hides and reveals anything wrapped inside it.
How can I enable closing any other open instance when I open an instance? e.g if prototype accordion is open, then functions and objects accordion is clicked, prototype accordion should close.
const vue = Vue.createApp({});
vue.component('accordion', {
data() {
return {
open: false,
}
},
methods: {
toggle() {
this.open = !this.open;
},
},
template: `
<div class="accord">
<p #click="toggle" class="accord-header">Click me to open and close!</p>
<div v-if="open" class="accord-body">
<slot></slot>
</div>
</div>
`,
})
vue.mount('#main-app');
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main-app {
width: 50%;
margin: 0 auto;
}
.accord {
padding: 4px;
cursor: pointer;
}
.accord-header {
text-align: center;
padding: 4px 0;
}
.accord-body {
background-color: #eee;
padding: 8px;
}
<script src="https://unpkg.com/vue#next"></script>
<div id="main-app">
<accordion>
<p class="accord-text">Javascript Proptotypes and inheritance</p>
</accordion>
<accordion>
<p class="accord-text">Scopes and Closures</p>
</accordion>
<accordion>
<p class="accord-text">Functions and Objects</p>
</accordion>
</div>
You can change open to be a prop, then you can do something like activeAccordianId: 1 in the parent and have the prop be
<accordian :open="activeAccordianId === 1" />
<accordian :open="activeAccordianId === 2" />
...
In Laravel 5.8 / vuejs 2.6 / vuex / mysql app I need to add stripe elements from https://stripe.com/docs/stripe-js,
http://prntscr.com/phflkd
and for this in resources/views/index.blade.php I added line:
#include('footer')
<script src="{{ asset('js/jquery.min.js') }}"></script>
<script src="{{ asset('js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('js/waves.min.js') }}"></script>
<script src="{{ asset('js/jquery.slimscroll.min.js') }}"></script>
<script src="{{ asset('js/powerange.js') }}"></script>
<script src="{{ asset('js/appInit.js') }}"></script>
<script src="{{ asset('js/app.js') }}{{ "?dt=".time() }}"></script>
<script src="https://js.stripe.com/v3/"></script>
</html>
and in my vue form I init stripe in initStripe() method, which is called in mount event :
<template>
<div class="page-content col-md-offset-2">
<div class="sign-up container-fluid justify-content-center" style="max-width: 460px;">
<hr>
<hr>
<form action="/charge" method="post" id="payment-form">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
<button type="button"
class="btn btn-outline-pink btn-round waves-effect waves-light cancel-btn mr-5" #click.prevent="cancelSelectedSubscription()">
<i :class="getHeaderIcon('cancel')"></i> Cancel
</button>
</div>
</div>
</template>
<script>
import {bus} from '../../../../app';
import appMixin from '../../../../appMixin';
import Vue from 'vue';
export default {
data: function () {
return {
is_page_loaded: false,
}
},
name: 'selectedSubscription',
created() {
if ( typeof this.currentLoggedUser.id != 'number' ) {
this.showPopupMessage("Access", 'Your session is expired !', 'error');
this.$store.commit('logout');
}
this.message = '';
}, // created) {
mounted() {
this.is_page_loaded = true
this.setAppTitle("Selected Subscription", 'Selected Subscription Details', bus);
this.initStripe();
}, // mounted() {
mixins: [appMixin],
methods: {
cancelSelectedSubscription() {
this.$router.push({path: '/personal-details'});
},
initStripe()
{
console.log("Stripe -1::")
// Create a Stripe client.
var stripe = Stripe('pk_test_NNNN');
console.log("Stripe -2::")
// Create an instance of Elements.
var elements = stripe.elements();
console.log("Stripe -3::")
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', {style: style});
console.log("Stripe -4::")
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
console.log("Stripe -5::")
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
this.stripeTokenHandler(result.token);
}
});
});
}, // initStripe() {
// Submit the form with the token ID.
stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
alert( "stripeTokenHandler form::"+var_dump(form) )
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
},
}, // methods: {
computed: {
currentLoggedUser() {
return this.$store.getters.currentLoggedUser;
},
...
} //computed: {
}
</script>
<style lang="css">
/**
* The CSS shown here will not be introduced in the Quickstart guide, but shows
* how you can use CSS to style your Element's container.
*/
.StripeElement {
box-sizing: border-box;
height: 40px;
padding: 10px 12px;
border: 1px solid transparent;
border-radius: 4px;
background-color: white;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
</style>
As result in browser's console I see console messages, “Credit or debit card” label and
uncolored “Submit Payment” button : https://imgur.com/a/TRWc23I
If to click on “Submit Payment” button I see :https://imgur.com/a/CdBSfMC
Is way I added Stripe to my vue form invalid? Which is valid way?
I suppose that I do not have to insert any additive elements/code in this block :
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
and they must be uploaded automatically on my form upload ?
and which method have I to use as in my app I save my data with axios?
I made all correctly, it was styles issue.
I replaced css from the example with lines :
#card-element {
line-height: 1.5rem;
margin: 10px;
padding: 10px;
}
.__PrivateStripeElement {
min-width: 300px !important;
min-height: 40px !important;
color: $text_color;
}
and my form was ready for payment!