How to create autocomplete box using vue.js? - html

I am new at Vue, now I am creating simple search app (using Vue.js cdn).
I want to append suggestion bar which contains all user id's from fake JSON server, for example if I write into search bar 1, I want to append only user which id is 1, and then I click to that user id I want to send another request to receive only this user info.
I am stuck, how I can solve this?
var app = new Vue({
el: '#app',
data: {
message: '',
searchKey:'',
result:[]
},
methods:{
async getData() {
// GET request using fetch with async/await
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${this.searchKey}`);
const data = await response.json()
this.result = data
},
},
created(){
this.getData()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.js"></script>
<div id="app">
<div class="search-area">
<div class="header-wrapper">
<h1>Tag Search</h1>
</div>
<div class="search-bar-custom">
<input placeholder="Search tags" v-model="searchKey" #keyup="getData" />
<div class="suggetions">
<ul class="suggestions" id="suggestions">
<li><h1>suggetion id</h1></li>
</ul>
</div>
</div>
</div>
</div>

You are on the right way, but, there are some issues about your logic here, for json-server you need to use the Operator _like (https://github.com/typicode/json-server#operators) to retrieve or filter data depending the column or property, so, your getData method must be like this:
async getData() {
// GET request using fetch with async/await
const response = await fetch(
`https://jsonplaceholder.typicode.com/users?name_like=${this.searchKey}`
);
const data = await response.json();
this.result = data;
},
You can change the property or column, in example username_like or id_like.
Finally, you need to show the results, so, change your template:
<ul class="suggestions" id="suggestions">
<h1 v-for="item in result" #mousedown="show(item)">
{{ item.id }} | {{ item.name }}
</h1>
</ul>
Pay attention on #mousedown="show(item)", when user click on some result, this action will display the data about user, for that, we need to create a new method called show and pass the item:
show(item) {
alert(JSON.stringify(item, null, 2));
}
You can look how it works here: https://codepen.io/riateam/pen/ExNrGOE?editors=1010

Related

Next.js use form inputs to render new page

I'm trying to set up a really simple page to render documents based on inputs. I have a form with some inputs on one page, and I want the data appear on the next page when submitted.
I'm still learning Next and I'm trying to use the pages/api to do this but I am not sure how exactly it works.
As of now when I submit the form it is redirecting me to a JSON page with the data, how can I make is so when I hit submit, it uses the JSON data to redirect to another page displaying it?
Here is the code:
index.js
export default function Home() {
return (
<>
<h1 Name Generator</h1>
<form action="/api/form" method="post">
<div>
<label for="name">Name</label>{" "}
<input
type="text"
id="name"
name="name"
placeholder="Enter name."
required
/>
</div>
<div>
<button style={{ display: "flex" }} type="submit">
Submit
</button>
</div>
</form>
</>
);
}
pages/api/form.js
export default function handler(req, res) {
// Get data submitted in request's body.
const body = req.body;
// Optional logging to see the responses
// in the command line where next.js app is running.
console.log("body: ", body);
// Guard clause checks for name,
// and returns early if they are not found
if (!body.subject || !body.teachers) {
// Sends a HTTP bad request error code
return res.status(400).json({ data: "name not found" });
}
// Found the name.
// Sends a HTTP success code
res.status(200).json({
data: {
name: `${body.name}`,
},
});
}
here is the result page I want it to render the data into and what I tried
result.js
import { data } from "../pages/api/form";
export default function Result() {
console.log(data);
return (
<>
<>
<h1>Name Generator</h1>
<hr></hr>
<div>
<h4>Name</h4>
<hr></hr>
{data.name}
</div>
</>
))
</>
);
}
When I submit I get JSON at
host/api/form
data: {
name: "name"
}
I am not sure what to try as I believe there is a simple way to do this that I am just missing

How can i access to 'download_url' in json using vuejs

i want to get 'tags' and 'download url' inside meta, i cannot access to 'download_url' in json how i can do it, as a backend i am using wagtail cms, and is it good to use vue js with wagtail cms (headless cms)
<template>
<div>
<div>
<b-card-group deck v-for="item in results" :key="item.id">
<b-card
>
<b-card-text>
<div v-for="block in item.body" :key="block.id">
<div v-if="block.type == 'heading'">
<h2>{{block.value}}</h2>
</div>
<div v-if="block.type == 'image'">
<img :src="'http://127.0.0.1:8000/api/v2/images/' + block.value">
</div>
<div v-if="block.type == 'paragraph'">
<h2 v-html="block.value">{{block.value}}</h2>
</div>
</div>
</b-card-text>
>
</b-card>
</b-card-group>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Home',
data () {
return {
results: null
}
},
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
))
}
}
</script>
here is json api. i accessed till id of image and do not know what to do next
{
"id": 3,
"meta": {
"type": "wagtailimages.Image",
"detail_url": "http://localhost/api/v2/images/3/",
"tags": [
"gadget",
"phone",
"samsung"
],
"download_url": "/media/original_images/affordable_new_9-7-inch_ipad_group_fan2_1_1.png"
},
"title": "affordable_new_9-7-inch_ipad_group_fan2 1 (1).png",
"width": 528,
"height": 357
}
Assuming that response.data.items is the json you showed above, you can extract just what you want from there. Right now, your component has the whole response.data.items in this.response. So if you want all of it there, keep that.
You can store the 'tags' and 'download url' to your component by adding two new pieces of data ie add them to your data:
data () {
return {
results: null,
tags: null,
downloadUrl: null
}
... and then set those in your response block from the request's response like this.
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
this.tags = response.data.items.meta.tags
this.downloadUrl = response.data.items.download_url
))
}
When dealing with nested response objects like this, it can help to make a local variable with the data and add it that way. Also, if you dont need the whole response, you can avoid storing it and just store the data from the response that you want to your component here. If you wanted to get something from your this.response, you're going to have to go deep into it. It would be cleaner to just pull out only what you need, and then use it in your code with just one {{myStuffIPulledOut}} vs {{response.thingIWantBefore.actualThing.}}

Vue.js edit post by id

I'm starting with vue.js and I was reading this question to help me loading some posts from DB with v-for.
Below each post there are Edit and Delete buttons. I can delete each post by its ID correctly. And I can open the input to edit post title correctly too.
But I cannot save input changes when I click on save button. It returns to the initial text.
And when I click to edit it opens all the inputs titles.
Is there a way to open the specific post title and keep the changes after save it?
<div id="app" class="row mb-50">
<div v-for="(item, index) in tours" v-bind:key="item.id" id="tours" class="col-md-12 mb-30">
<div class="tour-list">
<div class="tour-list-title">
<p>
<input type="text" ref="item.id" :value="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
</p>
</div>
<div class="tour-list-description">
<p>
{{ item.description }}
</p>
</div>
<div class="tour-list-options">
<div class="row">
<div class="col-md-6">
<span>
<button #click="editingTour = !editingTour" v-if="!editingTour"
class="btn border btn-circle tour-list-edit-btn">Edit</button>
</span>
<span>
<button #click="save" v-if="editingTour"
class="btn border btn-circle tour-list-edit-btn">Save</button>
</span>
<span>
<button #click="editingTour = false" v-if="editingTour"
class="btn border btn-circle tour-list-delete-btn">Cancel</button>
</span>
<span>
<button #click="deleteTour(item.id, index)" v-if="!editingTour"
class="btn border btn-circle tour-list-delete-btn">Delete</buton>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
vue.js:
let app = new Vue({
el: '#app',
data: {
editingTour: false,
tours: null,
errored: false,
edited: false,
deleted: false,
item: {
title: null,
description: null
}
},
created: function () {
this.searchTour()
},
methods: {
searchTour: function () {
axios.post('getPosts.php', { "token": param }).then((response) => {
this.tours = response.data;
}).catch((error) => {
this.errored = error;
});
},
editTour: function (id) {
axios.post('editPosts.php', { "token": token, "tourID": id }).then((response) => {
this.edited = response.data;
}).catch((error) => {
this.errored = error;
});
},
deleteTour: function (id) {
if (confirm('Are You sure?')) {
const index = this.tours.findIndex(item => item.id === id);
if (~index) {
axios.post('deletePosts.php', { "token": token, "tourID": id }).then((response) => {
this.deleted = response;
this.tours.splice(index, 1);
}).catch((error) => {
this.errored = error;
});
}
}
},
save: function () {
this.item.title = this.$refs['item.id'].value;
this.editingTour = !this.editingTour;
console.log(this.item.title);
}
}
});
In console.log(this.item.title); is returning undefined.
I have changed ref="item.id" to ref="title" and this.item.title = this.$refs['item.id'].value; to this.item.title = this.$refs['title'].value; but it did not work.
You should use in your input v-model instead of ref it will bind your model with the value you are editing, in general in vue we avoid direct DOM manipulation when possible, like so:
<input type="text" ref="item.id" v-model="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
Where calling your function e.g. editTour you can pass it the item (if it's in the template to save the updated version like so:
#click="editTour(item)"
You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
Source : https://v2.vuejs.org/v2/guide/forms.html
Example:
<input v-model="description" placeholder="my description">
The above input value will then be binded to the description element of your data object and vice-versa - if one changes, the other is updated to the same value:
data:{
description: "default value"
}
So, when you DB request is ready you can update the value of the description within the DB method:
this.description=db.result.description
and the value of the input will also update.
Likewise, if the user changes the value of the input field, the value bound to the data element will be updated also. So, when saving back to DB:
db.update({description:this.description})
(note: the db methods here are for example purposes only. Replace with the relevant DB methods for your backend service.)

Angular *ngFor doesnt update list on add/delete

I have an app where I have a list of vehicles. I have a local .json file where I get my data. This data is updated with a web-api. Whenever I add a vehicle to the list it is updated in the .json file, but I have to refresh the web browser to see the updated result. It works in the same way when I am trying to delete a vehicle from the list. I use one local list to get quick returns and then I use a second list to make sure that the changes are saved to the .json file. See code below.
Typescript
// component
vehicle: VehicleDetail;
favVehicles: VehicleDetail[] = [];
favVehiclesLocal: VehicleDetail[] = [];
ngOnInit() {
this.vehicleService.getFavourite().subscribe(data => {
this.favVehicles = data;
this.favVehiclesLocal = [...data];
});
}
}
// Button function which adds the selected vehicle to your favourites
addFav(event: VehicleDetail): VehicleDetail[] {
this.favVehiclesLocal = [this.vehicle, ...this.favVehiclesLocal];
console.log(this.favVehiclesLocal);
this.vehicleService.addVehicle(event).subscribe(data => {
event = data;
});
return this.favVehiclesLocal;
}
// Button function which deletes the selected vehicle from your favourites
deleteFav(event: VehicleDetail): VehicleDetail[] {
this.favVehiclesLocal = this.favVehiclesLocal.filter(h => h !== event);
this.vehicleService.deleteVehicle(event).subscribe(data => {
this.favVehicles = this.favVehicles.filter(h => h !== event);
event = data;
});
return this.favVehiclesLocal;
}
console.log(this.favVehiclesLocal);
}
The data is coming from a database and I use the following services to call for the data.
// Service for "add to favourite" button
addVehicle(vehicle: VehicleDetail): Observable<VehicleDetail> {
const url = `${this.API_URL}/favourites`;
const service = this.http
.post<VehicleDetail>(url, vehicle, this.httpOptions)
.pipe(
tap(_ => this.log(`adding vehicle id=${vehicle.id}`)),
catchError(this.handleError<VehicleDetail>('addVehicle'))
);
console.log(service);
return service;
}
// Service for "delete from favourite" button
deleteVehicle(vehicle: VehicleDetail): Observable<VehicleDetail> {
const url = `${this.API_URL}/favourites`;
const service = this.http
.put<VehicleDetail>(url, vehicle, this.httpOptions)
.pipe(
tap(_ => this.log(`deleted vehicle id=${vehicle.id}`)),
catchError(this.handleError<VehicleDetail>('deleteVehicle'))
);
console.log(service);
return service;
}
Html
<!-- list of vehicles -->
<aside *ngIf="favVehiclesLocal" class="vehiclelist">
<mat-nav-list matSort (matSortChange)="sortData($event)">
<th mat-sort-header="timestamp">Time of alarms</th>
<th mat-sort-header="status">Severity of status</th>
<mat-list-item *ngFor="let stuff of favVehiclesLocal" class="vehicles">
<span [ngClass]="getColors(stuff)"></span>
<p matLine (click)="updateInfo(stuff.id)"> {{ stuff.name }} </p>
<button mat-icon-button id="btn" *ngIf='check(stuff.alarm)' matTooltip="{{stuff.alarm[tooltipIndex(stuff)]?.timestamp}} - {{stuff.alarm[tooltipIndex(stuff)]?.description}}">
<mat-icon>info</mat-icon>
</button>
</mat-list-item>
</mat-nav-list>
</aside>
// add and delete buttons
<div class="details">
<button mat-raised-button #add (click)="addFav(vehicle)">Add to favourite</button>
<button mat-raised-button #delete (click)="deleteFav(vehicle)">Remove from favourite</button>
</div>
What is going wrong here? I have been checking out the Tour of Heroes on Angulario ( https://stackblitz.com/angular/akeyovpqapx?file=src%2Fapp%2Fheroes%2Fheroes.component.ts ) at .src/app/heroes/ and I havent been able to see a difference in their code and my code.
If you want me to clearify something or if you would like additional information please let me know.
Update
It should be mentioned that I have two views. These views are either the full list or the "my favourite" list. The lists are displayed depending on the value of a slide-toggle.
In my code above I wrote *ngFor="let stuff of favVehiclesLocal" to hide unknown parts of my code since I thought I had the problem narrowed down. The complete app uses a slightly different approach.
//app.component.html
<!-- list of vehicles -->
<aside *ngIf=".........
<mat-list-item *ngFor="let stuff of sortedVehicles" class="vehicles">
.........
</aside>
The sortedVehicles is assigned in the following way:
//app.component.html
<mat-slide-toggle (change)="myFavourite(favVehiclesLocal)">Show favourites</mat-slide-toggle>
// app.component.ts
myFavourite(vehicles: VehicleDetail[]): VehicleDetail[] {
this.toggleChecked = !this.toggleChecked;
console.log(this.toggleChecked);
if (this.toggleChecked) {
this.sortedVehicles = vehicles.slice();
} else {
this.sortedVehicles = this.vehicleDetails.slice();
}
console.log(this.sortedVehicles);
return this.sortedVehicles;
}
I start to think that this line of code start to complicate things? Is there any way that I can register the change? Is there any more effective approaches to it?

filter results by month on button click with Angujar

I am trying to wrap my head around angular still, I started learning a few days ago, and I am still getting to know Angular. I've been trying to experiment with a simple json http request. So far, I have succesfully grabbed and displayed the data, I have implemented a directive inside my controller with your basic search input and a few select options for more filtering based on their value.
However, I want to filter the results even more, by simply clicking on buttons with Month names, therefore filtering the results by the month in their date
My index.html:
<div ng-app="myNetworkEvents">
<div ng-controller="networkEventsCtrl">
<networkevent-directive></networkevent-directive>
</div>
</div>
My template:
<ul>
<li ng-repeat="event in events | filter:search | myFilter:monthlyFilter">
<p>{{event.title}}</p>
<p>{{event.date}}</p>
<p>{{event.tickets}}</p>
<p>{{event.body}}</p>
</li>
<li ng-show="(events | filter:search).length==0">Couldn't find "{{search}}"</li>
</ul>
<!-- my search box -->
<input ng-model="search" type="search">
<!-- filter by month -->
<ul ng-model="monthlyFilter">
<li><a>January</a></li>
<li><a>February</a></li>
<li><a>March</a></li>
</ul>
My app.js:
angular.module('myNetworkEvents', ['ui.utils'])
.service('networkEvents', function ($http, $q) {
var deferred = $q.defer();
$http.get('url to api').then(function (data){
deferred.resolve(data);
});
this.getNetworkEvents = function (){
return deferred.promise;
}
})
.controller('networkEventsCtrl', function ($scope, networkEvents) {
var getNetworkEvents = networkEvents.getNetworkEvents();
getNetworkEvents.then(function (eventsData){
$scope.events = eventsData.data;
});
})
.directive('networkeventDirective', function() {
return {
restrict: "E",
templateUrl: "networkevents.html"
}
})
//this is where i have no idea what to do besides this
.filter('myFilter',['$filter', function($filter){
return function(list){
return list;
}
}])
Sample of what my json looks like:
{
body: "Some awesomesauce here"
date: "4/3/2015 9:48:04 AM"
id: "207"
isactive: "Yes"
thumbnailurl: ""
tickets: "100"
title: "A nice event"
}