Multiple axios requests with vue.js and Airtable - vue.js

I build a working code to retrieve data from fake Airtable database with axios. You can see the code below perfectly working. No problem to retrieve data when is on the main table. Yet as you can assume some fields are connected to other tables (named in Airtable: parent child data or fields). Is the case in my table when you have the field "ORGANIZZAZIONE" (Organization) that is connected to another table having the organization datails as for example: NOME (Name) and TIPOLOGIA (Activity).
As you can see in the code when I try to access to this data (ORGANIZZAZIONE) I have bad results. Probably I have to build an "axios.all" request but I can't find the right way.
enter code here
new Vue({
el: '#app',
data: {
saluti:'Saluti da Mauro',
items: []
},
mounted: function(){
this.loadItems();
},
methods: {
loadItems: function(){
// Init variables
var self = this
var app_id = "apppCqmyJHPmfDTLr"; <!-- base -->
var app_key = "key8S2tx7NINXaZzZ";
this.items = []
axios.get(
"https://api.airtable.com/v0/"+app_id+"/DASHBOARD?api_key="+app_key,
{
headers: { Authorization: "Bearer "+app_key }
}
).then(function(response){
self.items = response.data.records
}).catch(function(error){
console.log(error)
})
}
}
})
<head>
<title>Airtable example</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<h1>AIT</h1>
<p>All work from <strong>Ryan Hayden</strong> | A basic introduction to the Airtable API.</p>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<div id="app">
<h2>{{saluti}}</h2>
<div class="container-fluid">
<div class="card-body" v-for="item, index in items" :key:="items">
<img src="https://www.artemedialab.it/wp-content/uploads/2019/04/immagini-sfondo-1-700x400.jpg" class="card-img-top" class="img-fluid" alt="MM" style="width: 18rem;">
<div class="card text-BLACK bg-primary mb-3 text-right" style="width: 18rem;">
<div class="card-header">
{{ index+1 }}
</div>
<div class="card text-black bg-success mb-3" style="max-width: 18rem;">
<div class="card border-primary mb-1" style="max-width: 18rem;">
<!-- lavorare su questo per approfondire la struttura su JSON
this.posts = response.data.data.map((post) => {
return {id: post.id, title: post.attributes.title, body: post.attributes.body};
}) -->
<p class="price"><strong>DATA INIZIO: </strong>{{ item['fields']['DATA INIZIO'] }}</p>
<p class="category"><strong>DETTAGLI ATTIVITA': </strong>{{ item['fields']["Attivita'"] }}</p>
<p class="category"><strong>PRIORITA': </strong>{{ item['fields']["PRIORITA'"] }}</p>
<p class="category"><strong>ORGANIZZAZIONE: </strong>{{ item['fields']['ORGANIZZAZIONE']}}</p>
<p class="category"><strong>ORGANIZZAZIONE / NOME: </strong>{{ item['fields']['ORGANIZZAZIONE']['Nome_Organizzazione']}}</p>
<p class="category"><strong>ORGANIZZAZIONE / TIPOLOGIA: </strong>{{ item['fields']['ORGANIZZAZIONE']['TIPOLOGIA']}}</p>
<img :src="item['fields']['Photo'][0]['thumbnails']['large']['url']" alt="" v-if="item['fields']['Photo']" width="150">
</div>
</div>
</div>
</div>
<!--app-->
<!-- Include Dependancy Scripts -->
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
</body>

In your case, you need to call the api for each ORGANIZZAZIONE on your response, then append it. Here is an example with the first ORGANIZZAZIONE:
var app_id = "apppCqmyJHPmfDTLr";
var app_key = "key8S2tx7NINXaZzZ";
this.items = []
axios.get(
"https://api.airtable.com/v0/" + app_id + "/DASHBOARD?api_key=" + app_key, {
headers: {
Authorization: "Bearer " + app_key
}
}
).then(function(response) {
// here get ORGANIZZAZIONE for each record
for (let record of response.data.records) {
// here the first ORGANIZZAZIONE on array
let ORGANIZZAZIONE = record.fields.ORGANIZZAZIONE[0];
//call Airtable api again
axios.get(
"https://api.airtable.com/v0/" + app_id + "/DASHBOARD/" + ORGANIZZAZIONE + "?api_key=" + app_key, {
headers: {
Authorization: "Bearer " + app_key
}
}
).then(function(response2) {
// Replace ORGANIZZAZIONE on first response to the the data in response2
record.fields.ORGANIZZAZIONE = response2.data.fields;
this.items = response.data.records; // You can set each time is getted
}).catch(function(error) {
console.log(error)
})
}
}).catch(function(error) {
console.log(error)
})
codepen: https://codepen.io/hans-felix/pen/MWaNraL

I got one blog regarding this issue. please check.
https://www.storyblok.com/tp/how-to-send-multiple-requests-using-axios

Related

controller not sending to view .net core

controller not sending to view . I m trying to send request from controller to view , but its not redirecting . controller always redirect to index page. when i summit the form . its always redirecting same index page ,
controller not sending to view .controller not sending to view
My controller is sending to another view. but its not working .
public IActionResult userLogin([FromBody] Users user)
{
string apiUrl = "https://localhost:44331/api/ProcessAPI";
var input = new
{
email = user.email,
password = user.password
};
string inputJson = (new JavaScriptSerializer()).Serialize(input);
WebClient client = new WebClient();
client.Headers["Content-type"] = "application/json";
// client.Encoding = Encoding.UTF8;
string json = client.UploadString(apiUrl + "/userLogin", inputJson);
// List<Users> customers = (new JavaScriptSerializer()).Deserialize<List<Users>>(json);
user = JsonConvert.DeserializeObject<Users>(json);
return View();
}
and the view page is
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model myproject.Models.Users
#{
Layout = null;
}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Inventory Management System</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
#* <link rel="stylesheet" type="text/css" href="./includes/style.css">*#
#*<script type="text/javascript" rel="stylesheet" src="~/js/main.js"></script>*#
</head>
<body>
<div class="overlay"><div class="loader"></div></div>
<!-- Navbar -->
<br /><br />
<div class="container">
<div class="alert alert-success alert-dismissible fade show" role="alert">
#*<?php echo $_GET["msg"]; ?>*#
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
#*<?php
}
?>*#
<div class="card mx-auto" style="width: 20rem;">
<img class="card-img-top mx-auto" style="width:60%;" src="./images/login.png" alt="Login Icon">
<div class="card-body">
<form id="form_login" >
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
#*<input asp-for="Name" type="text" class="form-control" id="name" required />*#
<input asp-for="email" type="email" class="form-control" id="log_email" placeholder="Enter email">
<small id="e_error" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" name="log_password" asp-for="password" id="log_password" placeholder="Password">
<small id="p_error" class="form-text text-muted"></small>
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"> </i>Login</button>
<span>Register</span>
</form>
</div>
<div class="card-footer">Forget Password ?</div>
</div>
</div>
<input type="text" id="txtName" />
<input type="button" id="btnGet" value="Get Current Time" />
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
$("#form_login").on("submit", function () {
var data = {
email: $("#log_email").val(),
password: $("#log_password").val(),
// Phone: $("#phone").val()
}
// data: $("#form_login").serialize(),
// var data = $("#form_login").serialize();
console.log(data);
$.ajax({
type: 'POST',
url: '/Process/userLogin',
// window.location.href = '#Url.Action("Process", "Dashboard")';
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
success: function (result) {
alert('Successfully received Data ');
console.log(result);
window.location.href = "Process/Dashboard";
// window.location.href = '#Url.Content("~/User/Home")';
// window.location.href = '#Url.Action("Process", "Dashboard")';
// window.location.href = DOMAIN + "/dashboard.php";
},
error: function () {
alert('Failed to receive the Data');
console.log('Failed ');
}
})
})
});
</script>
</body>
</html>
From your code, since you want to use JQuery Ajax to submit the form data to the action method, in the form submit event, you should use the event.preventDefault() to prevent the form submit action, then you can use JQuery Ajax to submit the form.
Second, does the Index page is the Process/Dashboard page? From your code, we can see in the Ajax success function, you will use the window.location.href to change the request URL, you can change the redirect page from here.

How can I add Vue.js search filter

I try to learn Vue.js and have now tried to solve a filter search of my array savedMembers. But I can not make it work.
Array name: savedMembers
variables in array, firstName, lastName and email
search field: search
How can I add responsive search?
How can I update view with saved members?
I would like a filter function to filter all values of array. But it´s okey if I only can filter by firstname.
This is my index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Team Page</title>
</head>
<body>
<div id="app">
<team-page></team-page>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
<script src="main.js"></script>
</body>
</html>
This is my main.js
template: `
<div class="team_members">
<h3>Team</h3>
<div class="create_member">
<form action="" #submit.prevent="onSubmit">
<label for="firstName">First name</label><br>
<input type="text" v-model="firstName" name="firstName"><br>
<label for="lasttName">Last name</label><br>
<input type="text" v-model="lastName" name="lastName"><br>
<label for="email">Email</label><br>
<input type="text" v-model="email" name="email"><br>
<input class="addMemberBtn" type="submit" value="Submit">
</form>
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
</div>
<div class="teamContainer">
<h3 class="team_header">Team</h3>
<div class="team_content">
<div class="container">
<div class="searchbox">
<input class="search" type="text" placeholder="Search" v-model="search">
</div>
<div class="createMemberBtn">
<button>Add New Team Member</button>
</div>
</div>
<div class="view-members">
<table>
<tr>
<th>Name</th>
<th colspan="2">Status</th>
</tr>
<tr v-for="member in savedMembers">
<td>{{ member.firstName }} {{ member.lastName }}</td>
<td>{{ member.email }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
`,
data() {
return {
search: null,
firstName: null,
lastName: null,
email: null,
errors: [],
savedMembers: [],
};
},
methods: {
addNewTeamMember(newTeamMember) {
this.savedMembers.push(newTeamMember);
},
onSubmit() {
if (this.firstName && this.lastName && this.email) {
let newTeamMember = {
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
};
this.addNewTeamMember(newTeamMember);
//this.$emit('members-submitted', newTeamMember)
this.firstName = null;
this.lastName = null;
this.email = null;
} else {
if (!this.firstName) this.errors.push("First name required");
if (!this.lastName) this.errors.push("Last name required");
if (!this.email) this.errors.push("Email required");
}
},
},
computed: {
/*filteredList() {
return this.savedMembers.filter((member) => {
return member.firstName
.toLowerCase()
.includes(this.search.toLowerCase());
});
},*/
},
});
var app = new Vue({
el: "#app",
data: {},
});
According to this part of the documentation:
"Computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed."
Which means that, in your case, the filteredList would only change if the this.savedMembers array change.
So, the correct way would be that:
Create another array, let's assume filteredSavedMembers
You create a method to react whenever the search changes ( i.e <input #change="onSearchChange".../>
In this method, you will assign your filtering to the filteredSavedMembers
And that's it, now you only have to show your filterd list in the template.
*Obs: to filter by multiple values, you can use the or (||) operator, like this:
const search = this.search.toLowerCase()
this.filteredSavedMembers = this.savedMembers
.filter(({ firstName, lastName, email }) =>
firstName.toLowerCase().includes(search) ||
lastName.toLowerCase().includes(search) ||
email.toLowerCase().includes(search)
)

Files From Upload Modal Not Being Passed

I have a BootStrap Modal Popup that I want to use for selecting and uploading a file. The pop-up works in all respects EXCEPT it is not passing the selected file to the underlying controller. Here is the form:
<!--Modal Body Start-->
<div class="modal-content">
<!--Modal Header Start-->
<div class="modal-header">
<h4 class="modal-title">Upload File</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<!--Modal Header End-->
<form asp-action="FileUpload" asp-controller="Attachment" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="modal-body form-horizontal">
<div>
<p>Upload a file using this form:</p>
<input type="file" name="file" />
</div>
<!--Modal Footer Start-->
<div class="modal-footer">
<button data-dismiss="modal" id="cancel" class="btn btn-default" type="button">Cancel</button>
<input type="submit" class="btn btn-success relative" id="btnSubmit" data-save="modal" value="Upload">
</div>
<div class="row">
</div>
</div> <!--Modal Footer End-->
</form>
</div>
<script type="text/javascript">
$(function () {
});
</script>
<!--Modal Body End-->
Here is the action in the controller:
[HttpPost]
public IActionResult FileUpload(IFormFile file)
{
//DO something with the file
return View();
}
[HttpGet]
public ActionResult UploadFile(string issueid)
{
ViewBag.id = issueid;
return PartialView("_UploadFile");
}
The action gets called but the "file" variable is NULL.
I have the following markup & script on the MAIN page the pop-up originates from:
<div id="modal-container" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
</div>
</div>
</div>
Upload Files
<script>
$('body').on('click', '.modal-link', function () {
var actionUrl = $(this).attr('href');
$.get(actionUrl).done(function (data) {
$('body').find('.modal-content').html(data);
});
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
});
})
$('body').on('click', '.close', function () {
$('body').find('#modal-container').modal('hide');
});
$('#CancelModal').on('click', function () {
return false;
});
$("form").submit(function () {
if ($('form').valid()) {
$("input").removeAttr("disabled");
}
});
</script>
To upload form data with a file you have to use a FormData object.
Also, you have to use $.ajax, as $.past cannot handle the FormData object
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = new FormData(form[0]);
$.ajax({
url: actionUrl,
type: 'POST',
data: dataToSend,
processData: false, //prevent jQuery from trying to serialize the FormData object
contentType: false, // prevents jQuery from setting the default content type
success: function(data){
$('body').find('.modal-content').html(data);
}
});
})

bootstrap-vue formatter is changing input position

I would like to live replace whitespaces with underscore _ in file name input in bootstrap-vue. But if I add a white space not at the end of input then formatter will move the cursor position to end of input. How to replace whitespaces without changing cursor position?
I tried:
<div class="form-group row">
<label class="col-6 col-md-4 col-lg-3 col-xl-3">File name</label>
<div class="col-6 col-md-8 col-lg-9 col-xl-9">
<b-input-group v-if="viewModel.offer">
<b-form-input ref="fileName" type="text" :formatter="formatter"></b-form-input>
<b-input-group-append>
<b-button variant="dark" v-b-popover.hover.top="'Download'" v-on:click=".."><i class="fas fa-arrow-circle-down"></i></b-button>
</b-input-group-append>
</b-input-group>
</div>
</div>
with:
formatter(value, event) {
const pos = event.target.selectionStart - 1
const nfileName = value.replace(/\s+/g, '_');
if (nfileName !== value) {
this.$nextTick(() => {
event.target.selectionEnd = pos
})
}
return nfileName;
}
but there the selectionStart value is equal selectionEnd, so that actual position is unknown.
Try using setTimeout instead of $nextTick.
setTimeout, should be run after the input has been allowed to render the new value (and moving the cursor).
I personally found this explaning pretty good, about how the logic functions.
http://www.hesselinkwebdesign.nl/2019/nexttick-vs-settimeout-in-vue/
new Vue({
el: '#app',
data() {
return {
text: ''
}
},
methods: {
formatter(value, event) {
const {
target: {
selectionEnd,
selectionStart
}
} = event
setTimeout(() => {
event.target.selectionStart = selectionStart;
event.target.selectionEnd = selectionEnd;
})
return value.replace(/\s+/g, '_');
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.2/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.16.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.16.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-input v-model="text" :formatter="formatter"></b-input>
</div>

Vue add a component on button click

I have three templates. AddCard.vue , ImageCard.vue and VideoCard.vue
AddCard.vue has two buttons on it one is to add the Image Card and Other To Add the video Card.. I need to add components based on the button click. Here are three templates and my index.html file.
index.html
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<div id="app">
<div class="container">
<div id="dynamic_components">
<!-- add components here -->
</div>
<addcard></addcard>
</div>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
AddCard.vue
<template>
<div class="buttons">
ul class="no-margin-list">
<li #click="imagecard">
<span class="card_icon">
<img :src="'img/image.jpg'" >
</span>
<p>Add Image Card</p>
</a>
</li>
<li #click="videocard">
<span class="card_icon">
<img :src="'img/video.jpg'" >
</span>
<p>Add Video Card</p>
</a>
</li>
</div>
</template>
<script>
export default {
computed: {
},
methods: {
imagecard(val) {
//how to add image card
},
videocard() {
//how to add video card
}
},
}
</script>
ImageCard.vue
<template>
<h1> I am a image Card </h1>
</template>
<script>
</script>
VideoCard.vue
<template>
<h1> I am a Video Card </h1>
</template>
<script>
</script>
I need to add components dynamically one after another in the <div id="dynamic_components"> . User can add as many as cards they want.
How do I add the components dynamically. Please point me to a tutorial.
Uses v-for + dynamic component.
Vue.config.productionTip = false
Vue.component('card1', {
template: '<div>Card:<span style="background-color:green">{{title}}</span></div>',
props: ['title']
})
Vue.component('card2', {
template: '<div>Card:<span style="background-color:blue">{{title}}</span></div>',
props: ['title']
})
Vue.component('card3', {
template: '<div>Card:<span style="background-color:yellow">{{title}}</span></div>',
props: ['title']
})
new Vue({
el: '#app',
data() {
return {
cards: [
{'card': {'title': 'I am one card1'}, 'card-type':'card1'},
{'card': {'title': 'I am one card2'}, 'card-type':'card2'}
]
}
},
computed: {
computedNoCard1: function () {
let availableCards = new Set(['card2', 'card3'])
return this.cards.filter((item) => {
return availableCards.has(item['card-type'])
})
}
},
methods: {
addCard: function () {
let supportedCards = ['card1', 'card2', 'card3']
let seed = Math.floor(Math.random()*supportedCards.length)
this.cards.push({'card': {'title': 'I am new card for ' + supportedCards[seed]}, 'card-type': supportedCards[seed]})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<button #click="addCard()">Add Card</button>
<table>
<tr><th>Data Property</th><th>Computed Property</th></tr>
<tr>
<td>
<div v-for="(card, index) in cards" :key="index">
<component :is="card['card-type']" :title="card.card.title">
</component>
</div>
</td>
<td>
<div v-for="(card, index) in computedNoCard1" :key="index">
<component :is="card['card-type']" :title="card.card.title">
</component>
</div>
</td>
</tr>
</table>
</div>