laravel vuejs/axios put request Formdata is empty - laravel

I have a few application, when I am sending formdata using axios post data is successfully sent to API. but when i am using put request its not working with formData.
<template>
<div class="container">
<div class="container-fluid" style="background:#fff">
<div class="page-header">
<h4 class="page-title">
<i class="flaticon-users"></i> Leads
</h4>
<ul class="breadcrumbs">
<li class="nav-home">
<a href="/">
<i class="flaticon-home"></i>
</a>
</li>
<li class="separator">
<i class="flaticon-right-arrow"></i>
</li>
<li class="nav-item">
<router-link to="/leads">Leads</router-link>
</li>
<li class="separator">
<i class="flaticon-right-arrow"></i>
</li>
</ul>
</div>
<template>
<div class="btn-wrapper">
<button v-on:click="seen = !seen" class="btn btn-primary btn-md">
<i class="flaticon-interface-1"></i>Add New Lead
</button>
</div>
<p></p>
</template>
<div class="row">
<div class="col-md-12" v-if="!seen">
<div class="card">
<div class="card-header">
<h4 class="card-title">
<i class="flaticon-interface-1"></i> New Leads
</h4>
</div>
<div class="card-body">
<form
#submit.prevent="addLeads"
id="leadform"
class="mb-3"
enctype="multipart/form-data"
>
<div class="col-md-12">
<div class="row">
<div class="col-md-6">
<label>Lead</label>
<div class="form-group">
<input
type="text"
id="name"
name="lead_name"
class="form-control"
placeholder="Lead Name"
v-model="form.name"
>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'name'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
<div class="col-md-6">
<label>Source</label>
<div class="form-group">
<textarea
type="text"
id="source"
name="source"
class="form-control"
placeholder="lead Souve"
v-model="form.source"
></textarea>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'source'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label>Value</label>
<div class="form-group">
<input
type="text"
id="value"
name="value"
class="form-control"
placeholder="lead Value"
v-model="form.value"
>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'value'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
<div class="col-md-6">
<label>Notes</label>
<div class="form-group">
<textarea
type="text"
id="notes"
name="notes"
class="form-control"
placeholder="lead Notes"
v-model="form.notes"
></textarea>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'notes'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="exampleFormControlSelect1">Assigned To</label>
<template v-if="!userlist">
<select class="form-control" id="assigned_to">
<option value>No User Found</option>
</select>
</template>
<template v-else>
<select
v-model="form.assigned_to"
name="assigned_to"
class="form-control"
id="assigned_to"
>
<option value>Please Select</option>
<option v-for="user in userlist" :key="user.id" :value="user.id">
<template v-if="user.id == currentUser.id">Me</template>
<template v-else>{{ user.name }}</template>
</option>
</select>
</template>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'assigned_to'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label>Close Date</label>
<div class="clearfix"></div>
<date-picker v-model="form.date" name="close_date"></date-picker>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label>Email</label>
<div class="form-group">
<input
type="text"
id="email"
name="email"
class="form-control"
placeholder="User Email"
v-model="form.email"
>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'email'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
<div class="col-md-6">
<label>Phone</label>
<div class="form-group">
<input
type="text"
id="phone"
name="phone"
class="form-control"
placeholder="User Phone Number"
v-model="form.phone"
>
<template v-if="errors">
<span v-for="(fieldsError, fieldName) in errors" :key="fieldName">
<template v-if="fieldName == 'phone'">
<p class="errors">
<strong>{{ fieldsError.join('\n') }}</strong>
</p>
</template>
</span>
</template>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<input
type="file"
multiple="multiple"
id="attachments"
#change="uploadFieldChange"
>
<hr>
<div class="col-md-12">
<div
class="attachment-holder animated fadeIn"
v-cloak
v-for="(attachment, index) in attachments"
>
<template v-if="attachment.file_name">
<span class="label label-primary">{{ attachment.file_name}}</span>
</template>
<template v-else>
<span
class="label label-primary"
>{{ attachment.name + ' (' + Number((attachment.size / 1024 / 1024).toFixed(1)) + 'MB)'}}</span>
</template>
<span
class
style="background: red; cursor: pointer;"
#click="removeAttachment(attachment)"
>
<button class="btn btn-xs btn-danger">Remove</button>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-check">
<label>Status</label>
<br>
<label class="form-radio-label">
<input
class="form-radio-input"
v-model="form.status"
type="radio"
name="status"
value="open"
checked
>
<span class="form-radio-sign">Open</span>
</label>
<label class="form-radio-label ml-3">
<input
class="form-radio-input"
v-model="form.status"
type="radio"
name="status"
value="sent"
>
<span class="form-radio-sign">Proposal Sent</span>
</label>
<label class="form-radio-label ml-3">
<input
class="form-radio-input"
v-model="form.status"
type="radio"
name="status"
value="won"
>
<span class="form-radio-sign">Won</span>
</label>
<label class="form-radio-label ml-3">
<input
class="form-radio-input"
v-model="form.status"
type="radio"
name="status"
value="lost"
>
<span class="form-radio-sign">lost</span>
</label>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<button type="submit" class="btn btn-success">Save</button>
<button #click="clearForm()" class="btn btn-danger">Cancel</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid" style="background:#fff;">
<kanban-board :stages="stages" :blocks="blocks" #update-block="updateBlock">
<div v-for="stage in stages" :slot="stage">
<h2>{{ stage }}</h2>
</div>
<div v-for="block in blocks" :slot="block.id">
<div>
<strong>{{ block.name }}</strong>
</div>
<p></p>
<button class="btn btn-danger">UKDH</button>
<button class="btn btn-warning">£ {{ block.value }}</button>
<router-link :to="`/account/${block.id}/convert`" class="btn btn-primary">create account</router-link>
<div class="text-right">
<router-link :to="`/leads/${block.id}`" class="btn btn-link btn-info">
<i class="la la-street-view"></i>
</router-link>
<a href="#" #click="deleteLead(block.id)" class="btn btn-link btn-danger">
<i class="la la-times"></i>
</a>
<a href="#" #click="editLead(block)" class="btn btn-link btn-primary">
<i class="la la-edit"></i>
</a>
</div>
</div>
</kanban-board>
</div>
</div>
</template>
<script>
import { addLeadsAPI } from "../../helpers/api";
import { updateStatus } from "../../helpers/api";
import { getCommonAPI } from "../../helpers/api";
import { deleteAPI } from "../../helpers/api";
import validate from "validate.js";
import DatePicker from "vue2-datepicker";
export default {
name: "leads",
components: {
DatePicker
},
data() {
return {
leads: [],
userlist: [],
attachments: [],
percentCompleted: 0,
upload_size: 0,
result: {},
stages: ["open", "sent", "lost", "won"],
blocks: [],
form: {
id: "",
name: "",
source: "",
value: 0,
notes: "",
user_id: "",
assigned_to: 1,
date: new Date(),
email: "",
phone: "",
status: ""
},
lead_id: "",
pagination: {},
edit: false,
isOpen: 0,
seen: true,
errors: null
};
},
created() {
this.fetchLeads();
this.getusers();
},
mounted() {
this.$store.dispatch("leads");
},
methods: {
getusers(page_url) {
let vm = this;
getCommonAPI("/users", "get", {
headers: {
Authorization: `Bearer ${this.currentUser.token}`,
Accept: "application/json"
}
}).then(res => {
vm.userlist = res.data;
});
},
fetchLeads(page_url) {
let vm = this;
page_url = page_url || "/leads/lead";
getCommonAPI(page_url, "get", {
headers: {
Authorization: `Bearer ${this.currentUser.token}`,
Accept: "application/json"
}
}).then(res => {
vm.blocks = res.data.data;
//vm.makePagination(res.meta, res.links);
});
},
makePagination(meta, links) {
let pagination = {
current_page: meta.current_page,
last_page: meta.last_page,
next_page_url: links.next,
prev_page_url: links.prev
};
this.pagination = pagination;
},
editLead(form) {
console.log(form);
this.edit = true;
this.seen = false;
this.form.id = form.id;
this.form.name = form.name;
this.form.lead_sid = form.lead_sid;
this.form.status = form.status;
this.form.type = form.type;
this.form.source = form.source;
this.form.value = form.value;
this.form.notes = form.notes;
this.form.email = form.email;
this.form.phone = form.phone;
this.form.assigned_to = form.assigned_to;
this.form.date = form.close_date;
this.attachments = form.uploads;
},
clearForm() {
this.edit = false;
this.form.id = null;
this.form.user_id = null;
this.form.assigned_to = "";
this.form.type = "";
this.form.status = true;
this.form.name = "";
this.form.source = "";
this.form.value = "";
this.form.notes = "";
this.form.email = "";
this.form.phone = "";
this.attachments = [];
},
addLeads() {
if (this.edit === false) {
// add new leads
this.errors = null;
const constraints = this.getConstraints();
const errors = validate(this.$data.form, constraints);
if (errors) {
this.errors = errors;
return;
}
// multiple file uploading
this.lead = document.getElementById("leadform");
const formData = new FormData(this.lead);
if (this.attachments.length > 0) {
for (var i = 0; i < this.attachments.length; i++) {
let attachment = this.attachments[i];
formData.append("attachments[]", attachment);
}
}
var config = {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress: function(progressEvent) {
this.percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
this.$forceUpdate();
}.bind(this)
};
//end
this.$store.dispatch("lead");
addLeadsAPI(formData, "post").then(res => {
swal("Good job!", "You clicked the button!", "success");
this.clearForm();
this.fetchLeads();
//this.attachments = [];
});
} else {
this.errors = null;
const constraints = this.getConstraints();
const errors = validate(this.$data.form, constraints);
if (errors) {
this.errors = errors;
return;
}
console.log("i am in edit");
// multiple file uploading
this.lead = document.getElementById("leadform");
let formData = new FormData(this.lead);
if (this.attachments.length > 0) {
for (var i = 0; i < this.attachments.length; i++) {
let attachment = this.attachments[i];
formData.append("attachments[]", attachment);
}
}
console.log(formData);
var config = {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress: function(progressEvent) {
this.percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
this.$forceUpdate();
}.bind(this)
};
//end
console.log(formData);
this.$store.dispatch("lead");
//update
addLeadsAPI(formData, "put").then(res => {
swal("Good job!", "You clicked the button!", "success");
this.clearForm();
this.fetchLeads();
//this.attachments = [];
});
}
},
getConstraints() {
return {
name: {
presence: true,
length: {
minimum: 6,
message: "Must be at least 6 characters long"
}
},
source: {
presence: true,
length: {
minimum: 6,
message: "Must be at least 6 characters long"
}
},
value: {
presence: true,
length: {
minimum: 1,
message: "Must be at least 1 characters long"
}
},
notes: {
presence: true,
length: {
minimum: 6,
message: "Must be at least 6 characters long"
}
}
};
},
updateBlock(id, status) {
//api call axios
updateStatus(id, status, "get").then(res => {
this.clearForm();
this.fetchLeads();
});
this.blocks.find(b => b.id === Number(id)).status = status;
},
deleteLead(id) {
swal({
title: "Are you sure?",
text: "Do you really want to delete Lead!",
type: "warning",
buttons: {
confirm: {
text: "Yes, delete it!",
className: "btn btn-success"
},
cancel: {
visible: true,
className: "btn btn-danger"
}
}
}).then(Delete => {
if (Delete) {
deleteAPI(`/lead/${id}`, "delete", {
headers: {
Authorization: `Bearer ${this.currentUser.token}`,
Accept: "application/json"
}
}).then(res => {
swal({
title: "Deleted!",
text: "Your lead has been deleted.",
type: "success",
buttons: {
confirm: {
className: "btn btn-success"
}
}
});
this.fetchLeads();
});
} else {
this.fetchLeads();
swal.close();
}
});
},
getAttachmentSize() {
this.upload_size = 0; // Reset to beginningƒ
this.attachments.map(item => {
this.upload_size += parseInt(item.size);
});
this.upload_size = Number(this.upload_size.toFixed(1));
this.$forceUpdate();
},
removeAttachment(attachment) {
this.attachments.splice(this.attachments.indexOf(attachment), 1);
this.getAttachmentSize();
},
// This function will be called every time you add a file
uploadFieldChange(e) {
console.log(this.attachments);
var files = e.target.files || e.dataTransfer.files;
if (!files.length) return;
for (var i = files.length - 1; i >= 0; i--) {
this.attachments.push(files[i]);
}
console.log("out");
// Reset the form to avoid copying these files multiple times into this.attachments
document.getElementById("attachments").value = [];
}
},
computed: {
users() {
return this.$store.getters.users;
},
currentUser() {
return this.$store.getters.currentUser;
}
}
};
</script>
<style lang="scss">
#import "../assets/board.scss";
</style>
<style scoped>
.vue-js-switch#changed-font {
font-size: 30px;
}
.hide {
display: none;
}
.errors {
color: lightcoral;
border-radius: 5px;
padding: 21px 0 2px 0;
}
</style>
when edit option true. I am calling method addLeadsAPI for posting data with axios put but Formdata is empty $request->all().
Anyone can help me with this?seems axios put is not working for editing data. through formdata.

Laravel can not handle multipart-formdata well with PUT method. See Input from PUT requests sent as multipart/form-data is unavailable #13457.
If your code actually uses the PUT method, it seems to be affected by this problem.
There are several workarounds.
Dealing with the client side:
Instead of PUT method, use POST method with _method parameter value set to PUT (called 'method spoofing')
Dealing with the server side:
Add a package that performs multipart processing to Laravel. (ex. illuminatech/multipart-middleware)
Use pecl/apfd extension which provides ability to parse 'multipart/form-data' HTTP requests for any request method.

I have changed the axioscall into post and set the value _method:put
addLeads() {
if (this.edit === false) {
// add new leads
this.errors = null;
const constraints = this.getConstraints();
const errors = validate(this.$data.form, constraints);
if (errors) {
this.errors = errors;
return;
}
// multiple file uploading
this.lead = document.getElementById("leadform");
const formData = new FormData(this.lead);
if (this.attachments.length > 0) {
for (var i = 0; i < this.attachments.length; i++) {
let attachment = this.attachments[i];
formData.append("attachments[]", attachment);
}
}
var config = {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress: function(progressEvent) {
this.percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
this.$forceUpdate();
}.bind(this)
};
//end
this.$store.dispatch("lead");
formData.append("_method", "post");
addLeadsAPI(formData, "post", config).then(res => {
swal("Good job!", "You clicked the button!", "success");
this.clearForm();
this.fetchLeads();
//this.attachments = [];
});
} else {
this.errors = null;
const constraints = this.getConstraints();
const errors = validate(this.$data.form, constraints);
if (errors) {
this.errors = errors;
return;
}
console.log("i am in edit");
// multiple file uploading
this.lead = document.getElementById("leadform");
let formData = new FormData(this.lead);
if (this.attachments.length > 0) {
for (var i = 0; i < this.attachments.length; i++) {
let attachment = this.attachments[i];
formData.append("attachments[]", attachment);
}
}
formData.append("_method", "put");
formData.append("id", this.form.id);
console.log(formData);
var config = {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
onUploadProgress: function(progressEvent) {
this.percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
this.$forceUpdate();
}.bind(this)
};
//end
console.log(formData);
this.$store.dispatch("lead");
//update
addLeadsAPI(formData, "put", config).then(res => {
swal("Good job!", "You clicked the button!", "success");
this.clearForm();
this.fetchLeads();
//this.attachments = [];
});
}
},

Well, I had an issue trying to update records using axios & vue.js.
The solution is to set the method value on the formData to putand send the request using the post method. Something like this:
console.log("i am in edit");
// multiple file uploading
this.lead = document.getElementById("leadform");
let formData = new FormData(this.lead);
if (this.attachments.length > 0) {
for (var i = 0; i < this.attachments.length; i++) {
let attachment = this.attachments[i];
formData.append("attachments[]", attachment);
}
}
formData.append("_method", "put");
console.log(formData);
In your axios request:
axios({
method: "POST", // this is important
url: {$your_destination_url},
data: formData,
headers: { "Content-Type": "multipart/form-data" }
})
.then(r => console.log(r.data))
.catch(e => console.log(e));

It may be related to this, https://laravel.com/docs/5.0/routing#method-spoofing.
when using PUT, PATCH or DELETE, you may need to also let laravel know the form method you are using. Try adding "_method" property with the value "PUT", and let me know if that works for you

My previous wrong code -
let data = new FormData()
data.append('message', 'AnyMessage')
Instead of FormData use the following String that works fine -
let data = "message=AnyMessage"

Related

Laravel 9 uploading to storage and using image for new blog post

Blog post
Above is my blog post, Below I will post my current set up.
New blog form
I'm new to this so, I want to be able to upload images and each of my blog post to pull the requested images. Currently the file selector isn't linked so it creates a fake path.
This is my blogs controller
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Requests\BlogsUpdateRequest;
use App\Http\Requests\DatatablesRequest;
use App\Modules\Blogs\BlogsService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
class BlogsController extends Controller
{
public function __construct(
private readonly BlogsService $service
)
{
}
public function index(DatatablesRequest $request): JsonResponse
{
try {
return response()->json($this->service->index($request->data()));
} catch (Exception $error) {
return response()->json(
[
"exception" => get_class($error),
"errors" => $error->getMessage()
],
Response::HTTP_BAD_REQUEST
);
}
}
public function get(int $id): JsonResponse
{
try {
return response()->json($this->service->get($id));
} catch (Exception $error) {
return response()->json(
[
"exception" => get_class($error),
"errors" => $error->getMessage()
],
Response::HTTP_BAD_REQUEST
);
}
}
public function update(BlogsUpdateRequest $request): JsonResponse
{
try {
return response()->json($this->service->update($request));
} catch (Exception $error) {
return response()->json(
[
"exception" => get_class($error),
"errors" => $error->getMessage()
],
Response::HTTP_BAD_REQUEST
);
}
}
public function delete(int $id): JsonResponse
{
try {
$this->service->delete($id);
return response()->json(null, Response::HTTP_NO_CONTENT);
} catch (Exception $error) {
return response()->json(
[
"exception" => get_class($error),
"errors" => $error->getMessage()
],
Response::HTTP_BAD_REQUEST
);
}
}
}
This is my blogsupdaterequest
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class BlogsUpdateRequest extends FormRequest
{
public function rules(): array
{
return [
"id" => "nullable|numeric",
"is_trending" => "required|boolean",
"author" => "required|string",
"author_image_url" => "required|string",
"image_url_portrait" => "required|string",
"image_url_landscape" => "required|string",
"date" => "required|string",
"title" => "required|string",
"tags" => "required|string",
"description" => "required|string",
"content" => "required|string",
];
}
public function data(): array
{
$id = $this->input("id", null);
return [
"id" => ($id === null) ? null : (int)$id,
"is_trending" => $this->input("is_trending", 0),
"author" => $this->input("author"),
"author_image_url" => $this->input("author_image_url"),
"image_url_portrait" => $this->input("image_url_portrait"),
"image_url_landscape" => $this->input("image_url_landscape"),
"date" => $this->input("date"),
"url" => $this->generateUrl($this->input("title")),
"title" => $this->input("title"),
"tags" => array_map(function($row) {
return trim($row);
}, explode(",", $this->input("tags", []))),
"description" => $this->input("description"),
"content" => $this->input("content"),
];
}
private function generateUrl(string $title): string {
$newUrl = trim(strtolower($title));
$newUrl = str_replace(" ", " ", $newUrl);
$newUrl = str_replace(" ", "-", $newUrl);
return preg_replace("/[^A-Za-z0-9\-]/", "", $newUrl);
}
}
Blogs model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Blogs extends Model
{
protected $table = "blogs";
protected $fillable = [
"id",
"is_trending",
"author",
"author_image_url",
"image_url_portrait",
"image_url_landscape",
"title",
"date",
"description",
"content",
"created_at",
"updated_at",
];
public function tags (){
return $this->hasMany(BlogTags::class);
}
}
Blogs.blade
<div class="col-lg-8 col-12">
#foreach($blogs as $blog)
<span onclick="redirectto('{{$blog['url']}}')">
<div class="row mt-4 mb-4 blog-card border rounded">
<div class="col-lg-4 col-12 p-0 m-0">
<img src="{{$blog ['img_url_portrait']}}" alt="" class="rounded w-100 h-100">
</div>
<div class="col-lg-8 col-12 p-lg-5">
<div class="row h-100 pt-4 align-item-center">
<div class="col-12 mx-auto">
<small class="text-muted fs-8">
{{$blog ['date']}}
</small>
<br>
#foreach ($blog['tags'] as $tag)
<span class="text-primary fw-bolder fs-6 pe-1">{{$tag['$tag']}}</span>
#if($loop->iteration < count($blog['tags'])))
<span class="text-primary fw-bolder fs-6 pe-1">•</span>
#endif
#endforeach
<h2 class="fw-lighter fs-2">{{$blog['title'] }}</h2>
<p class="text-muted">{{$blog['description'] }}</p>
<p>
<img src="{{$blog['author_image_url']}}" alt="author image" class="rounded-circle" height="35" width="35">
<span class="ps-1">{{$blog['author']}}</span>
</p>
</div>
</div>
</div>
</div>
</span>
#endforeach
</div>
Images model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Images extends Model
{
protected $table = "image";
protected $fillable = [
"id",
"name",
"location",
"created_at",
"updated_at",
];
}
Form.blade
<div class="modal fade" id="BlogsModal">
<div class="modal-dialog modal-fullscreen text-dark">
<div class="modal-content">
<div class="modal-content container" style="overflow-y:scroll;">
<div class="row">
<div class="col-12 text-end">
<span type="button" class="btn-close" data-bs-dismiss="modal"></span>
</div>
<div class="col-12">
<div id="BlogsErrorsContainer" class="alert alert-danger errorContainer" style="display:none;">
<h5 class="font-weight-bolder">Error!</h5>
<ul></ul>
</div>
</div>
<div class="col-12">
<hr>
<h5 class="modal-title font-weight-bolder">New Blog</h5>
<hr>
<form id="BlogsForm">
<input type="hidden" class="form-control" id="itemId">
<div class="row">
<div class="col-12 mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" id="blogsIsTrending">
<label class="form-check-label">Trending</label>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Title</label>
<input type="text" class="form-control" id="blogsTitle">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Date</label>
<input type="text" class="form-control" id="blogsDate">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Author Name</label>
<input type="text" class="form-control" id="blogsAuthorName">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Author Image URL</label>
<input type="file" class="form-control" id="blogsAuthorImageUrl">
</div>
<div class="col-12">
<hr>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Image Url Landscape</label>
<input type="file" class="form-control" id="blogsImageUrlLandscape">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Image Url Portrait</label>
<input type="file" class="form-control" id="images[]">
</div>
<div class="col-12">
<hr>
</div>
<div class="col-12">
<label class="form-label font-weight-bolder">Tags</label>
<input type="text" class="form-control" id="blogsTags">
<div class="form-text">Tags must be seperated by a comma. Example: <i>Laravel, Hosting</i></div>
</div>
<div class="col-12">
<hr>
</div>
<div class="col-12">
<label class="form-label font-weight-bolder">Description</label>
<textarea rows="2" type="text" class="form-control" id="blogsDescription"></textarea>
<div class="form-text">1-3 sentences max.</div>
</div>
<div class="col-12 mb-5">
<label class="form-label font-weight-bolder">Content</label>
<div id="blogsContent"></div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer container">
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Close</button>
<button id="SubmitBlogsForm" type="button" class="btn btn-sm btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<script src="//cdn.quilljs.com/1.3.6/quill.min.js"></script>
<script>
let formSubmitted = false;
let blogsEndpoint = "/api/dashboard/blogs";
let quillToolbar = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{
'header': 1
}, {
'header': 2
}], // custom button values
[{
'list': 'ordered'
}, {
'list': 'bullet'
}],
[{
'script': 'sub'
}, {
'script': 'super'
}], // superscript/subscript
[{
'indent': '-1'
}, {
'indent': '+1'
}], // outdent/indent
[{
'direction': 'rtl'
}], // text direction
[{
'size': ['small', false, 'large', 'huge']
}], // custom dropdown
[{
'header': [1, 2, 3, 4, 5, 6, false]
}],
[{
'color': []
}, {
'background': []
}], // dropdown with defaults from theme
[{
'font': []
}],
[{
'align': []
}],
['clean'] // remove formatting button
];
let quill = new Quill(
"#blogsContent", {
modules: {
toolbar: quillToolbar
},
placeholder: "Compose an epic ...",
theme: "snow"
}
);
function clearForm() {
$("#itemId").val(null);
$("#blogsTitle").val(null);
$("#blogsAuthorName").val(null),
$("#blogsAuthorImageUrl").val(null);
$("#blogsImageUrlLandscape").val(null);
$("#blogsImageUrlPortrait").val(null);
$("#blogsTags").val(null);
$("#blogsDescription").val(null);
$("#blogsDate").val(null);
$("#blogsIsTrending").prop("checked", false);
$("#blogsContent").val(null);
quill.setContents([{
insert: "\n"
}]);
clearErrors();
}
function clearErrors() {
$("#BlogsErrorsContainer ul").empty();
$("#BlogsErrorsContainer").hide();
}
function showErrors(errorsList = []) {
Object.keys(errorsList).forEach(key => {
$("#BlogsModal .errorContainer ul").append(
"<li><b>" + key + ": </b>" + errorsList[key] + "</li>"
);
$("#BlogsModal .errorContainer").show();
});
}
$(document).ready(function() {
$("#SubmitBlogsForm").click(function() {
event.preventDefault();
clearErrors();
if (formSubmitted !== true) {
formSubmitted = true;
$.ajax({
type: "POST",
url: blogsEndpoint,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
id: $("#itemId").val(),
title: $("#blogsTitle").val(),
author: $("#blogsAuthorName").val(),
author_image_url: $("#blogsAuthorImageUrl").val(),
image_url_landscape: $("#blogsImageUrlLandscape").val(),
image_url_portrait: $("#blogsImageUrlPortrait").val(),
tags: $("#blogsTags").val(),
description: $("#blogsDescription").val(),
date: $("#blogsDate").val(),
is_trending: $("#blogsIsTrending").prop("checked"),
content: quill.root.innerHTML.trim(),
}),
success: function(response) {
formSubmitted = false;
$BlogsModal.hide();
$("#dataTable").DataTable().ajax.reload();
}
}).fail(function(response) {
formSubmitted = false;
if (response.status === 422) {
showErrors(response.responseJSON["errors"]);
} else {
showErrors({
Error: "Could not process your request! Please try again later."
});
}
});
}
});
});
</script><div class="modal fade" id="BlogsModal">
<div class="modal-dialog modal-fullscreen text-dark">
<div class="modal-content">
<div class="modal-content container" style="overflow-y:scroll;">
<div class="row">
<div class="col-12 text-end">
<span type="button" class="btn-close" data-bs-dismiss="modal"></span>
</div>
<div class="col-12">
<div id="BlogsErrorsContainer" class="alert alert-danger errorContainer" style="display:none;">
<h5 class="font-weight-bolder">Error!</h5>
<ul></ul>
</div>
</div>
<div class="col-12">
<hr>
<h5 class="modal-title font-weight-bolder">New Blog</h5>
<hr>
<form id="BlogsForm">
<input type="hidden" class="form-control" id="itemId">
<div class="row">
<div class="col-12 mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" id="blogsIsTrending">
<label class="form-check-label">Trending</label>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Title</label>
<input type="text" class="form-control" id="blogsTitle">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Date</label>
<input type="text" class="form-control" id="blogsDate">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Author Name</label>
<input type="text" class="form-control" id="blogsAuthorName">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Author Image URL</label>
<input type="text" class="form-control" id="blogsAuthorImageUrl">
</div>
<div class="col-12">
<hr>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Image Url Landscape</label>
<input type="text" class="form-control" id="blogsImageUrlLandscape">
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<label class="form-label font-weight-bolder">Image Url Portrait</label>
<input type="text" class="form-control" id="blogsImageUrlPortrait">
</div>
<div class="col-12">
<hr>
</div>
<div class="col-12">
<label class="form-label font-weight-bolder">Tags</label>
<input type="text" class="form-control" id="blogsTags">
<div class="form-text">Tags must be seperated by a comma. Example: <i>Laravel, Hosting</i></div>
</div>
<div class="col-12">
<hr>
</div>
<div class="col-12">
<label class="form-label font-weight-bolder">Description</label>
<textarea rows="2" type="text" class="form-control" id="blogsDescription"></textarea>
<div class="form-text">1-3 sentences max.</div>
</div>
<div class="col-12 mb-5">
<label class="form-label font-weight-bolder">Content</label>
<div id="blogsContent"></div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer container">
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Close</button>
<button id="SubmitBlogsForm" type="button" class="btn btn-sm btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<script src="//cdn.quilljs.com/1.3.6/quill.min.js"></script>
<script>
let formSubmitted = false;
let blogsEndpoint = "/api/dashboard/blogs";
let quillToolbar = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{
'header': 1
}, {
'header': 2
}], // custom button values
[{
'list': 'ordered'
}, {
'list': 'bullet'
}],
[{
'script': 'sub'
}, {
'script': 'super'
}], // superscript/subscript
[{
'indent': '-1'
}, {
'indent': '+1'
}], // outdent/indent
[{
'direction': 'rtl'
}], // text direction
[{
'size': ['small', false, 'large', 'huge']
}], // custom dropdown
[{
'header': [1, 2, 3, 4, 5, 6, false]
}],
[{
'color': []
}, {
'background': []
}], // dropdown with defaults from theme
[{
'font': []
}],
[{
'align': []
}],
['clean'] // remove formatting button
];
let quill = new Quill(
"#blogsContent", {
modules: {
toolbar: quillToolbar
},
placeholder: "Compose an epic ...",
theme: "snow"
}
);
function clearForm() {
$("#itemId").val(null);
$("#blogsTitle").val(null);
$("#blogsAuthorName").val(null),
$("#blogsAuthorImageUrl").val(null);
$("#blogsImageUrlLandscape").val(null);
$("#blogsImageUrlPortrait").val(null);
$("#blogsTags").val(null);
$("#blogsDescription").val(null);
$("#blogsDate").val(null);
$("#blogsIsTrending").prop("checked", false);
$("#blogsContent").val(null);
quill.setContents([{
insert: "\n"
}]);
clearErrors();
}
function clearErrors() {
$("#BlogsErrorsContainer ul").empty();
$("#BlogsErrorsContainer").hide();
}
function showErrors(errorsList = []) {
Object.keys(errorsList).forEach(key => {
$("#BlogsModal .errorContainer ul").append(
"<li><b>" + key + ": </b>" + errorsList[key] + "</li>"
);
$("#BlogsModal .errorContainer").show();
});
}
$(document).ready(function() {
$("#SubmitBlogsForm").click(function() {
event.preventDefault();
clearErrors();
if (formSubmitted !== true) {
formSubmitted = true;
$.ajax({
type: "POST",
url: blogsEndpoint,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
id: $("#itemId").val(),
title: $("#blogsTitle").val(),
author: $("#blogsAuthorName").val(),
author_image_url: $("#blogsAuthorImageUrl").val(),
image_url_landscape: $("#blogsImageUrlLandscape").val(),
image_url_portrait: $("#blogsImageUrlPortrait").val(),
tags: $("#blogsTags").val(),
description: $("#blogsDescription").val(),
date: $("#blogsDate").val(),
is_trending: $("#blogsIsTrending").prop("checked"),
content: quill.root.innerHTML.trim(),
}),
success: function(response) {
formSubmitted = false;
$BlogsModal.hide();
$("#dataTable").DataTable().ajax.reload();
}
}).fail(function(response) {
formSubmitted = false;
if (response.status === 422) {
showErrors(response.responseJSON["errors"]);
} else {
showErrors({
Error: "Could not process your request! Please try again later."
});
}
});
}
});
});
</script>

how to hide and check a radio button if condition is true

Hy developers pls i have a vuejs code that i would love the radio checked if row.callback_phone starts with 05 or hide the div if callback_phone number is empty. pls my code is below.
<div class="row space" >
<div class="col-md-1">
<input type="radio" name="inp" :value="row.calling_no" aria-selected="true" :checked="checkcall" :disabled="checkInputFunction">
</div>
<div class="col-md-11">
<label class="mol">שלח למספר שממנו חייגו ({{ row.calling_no}})</label>
</div>
</div>
<div class="row space" >
<div class="col-md-1">
<input type="radio" name="inp" :value="row.callback_phone" :checked="checkback" :disabled="checkInputFunction2" >
</div>
<div class="col-md-11">
<label class="mol" >שלח למספר שהלקוח השאיר ({{ row.callback_phone}})</label>
</div>
</div>
my script is below
<script>
export default {
name: "MailModalComponent",
props: {
row: {
type: Object,
default: () => ({}),
}
},
data() {
return {
set_token: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
pageUrl: window.pageUrl,
}
},
computed:{
checkcall() {
if (this.row.callback_phone.substring(0, 2) == '05') {
return true;
} else {
return false;
}
},
checkback() {
if (this.row.calling_no.substring(0, 2) == '05') {
return true;
} else {
return false;
}
},
checkInputFunction(){
if(Object.keys(this.row).length > 0){
if(this.row.calling_no == '' ||this.row.calling_no == 'Null' ||
this.row.calling_no?
this.row.calling_no.substring(0,2)!='05':true){
return true;
}else{
return false;
}
}
},
checkInputFunction2(){
if(Object.keys(this.row).length > 0){
if(this.row.callback_phone == '' ||this.row.callback_phone == 'Null' ||
this.row.callback_phone?
this.row.callback_phone.substring(0,2)!='05':true){
return true;
}else{
return false;
}
}
}
},
methods: {
submitToWhatsapp(e) {
e.preventDefault();
},
}
}
i just updated this code, i am fetching the data from my database, but i want conditionally render elements as stated above, please i need help.
Your code worked fine for me. I've included a snippet below with a <input type="text /> to let you change row.callback_phone, then when you type a value starting with 05 the radio button becomes checked.
You can use v-if="row.calling_no && row.calling_no.length" or v-if="row.callback_no && row.callback_no.length" on a div to hide it.
Take a look at the documentation for conditional rendering.
I've added another computed for checkAnyButNotBoth.
new Vue({
el: "#app",
data: () => {
return {
row: {
callback_phone: "",
calling_no: ""
}
}
},
computed: {
checkcall() {
return this.row.calling_no.substring(0, 2) == '05';
},
checkback() {
return this.row.callback_phone.substring(0, 2) == '05';
},
checkAnyButNotBoth() {
return this.checkcall ? !this.checkback : this.checkback;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div id="app">
<div class="row space">
<div class="col-md-1">
<input type="radio" :checked="checkcall" v-if="row.calling_no && row.calling_no.length">
</div>
<div class="col-md-11">
<label class="mol">Calling No: ({{ row.calling_no}})</label>
<input type="text" v-model="row.calling_no" />
</div>
</div>
<div class="row space">
<div class="col-md-1">
<input type="radio" :checked="checkback" v-if="row.callback_phone && row.callback_phone.length">
</div>
<div class="col-md-11">
<label class="mol">Callback Phone: ({{ row.callback_phone}})</label>
<input type="text" v-model="row.callback_phone" />
</div>
</div>
<div class="row">
<div class="col-12">
calling_no starts with 05: {{checkcall}}
</div>
<div class="col-12">
callback_phone starts with 05: {{checkback}}
</div>
<div class="col-12">
either but not both start with 05: {{checkAnyButNotBoth}}
</div>
</div>
</div>

Dynamic select box data binding on vuejs

i have questions about data binding in vuejs and i hope everyone here can help me to solve this problem.
I'm in the stage of learning to use vuejs with laravel. I have problems doing data bindings in the data editing process, I can not display any information in the select box.
Next I include the data response that i want to display and the code.
data response
{
"data":{
"id":101,
"kode":"B100",
"nama":"Bendung Jules Rutherford",
"desa":{
"id":"5103050018",
"district_id":"5103050",
"name":"BONGKASA PERTIWI",
"district":{
"id":"5103050",
"city_id":"5103",
"name":"ABIANSEMAL",
"city":{
"id":"5103",
"province_id":"51",
"name":"KABUPATEN BADUNG",
"province":{
"id":"51",
"name":"BALI",
}
}
}
}
}
}
and this is the code :
<template>
<div>
<div v-if="!loading">
<div class="form-group row">
<label class="col-sm-3">Kode</label>
<div class="col-sm-9">
<input :class="{'is_invalid' : errors.kode}" v-model="bendung.kode" type="text" class="form-control" placeholder="B0001">
<div class="invalid-feedback" v-if="errors.kode">{{ errors.kode[0] }}</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3">Nama</label>
<div class="col-sm-9">
<input :class="{'is-invalid': errors.nama}" v-model="bendung.nama" type="text" class="form-control" placeholder="Bendungan Mengwi">
<div class="invalid-feedback" v-if="errors.nama">{{ errors.nama[0] }}</div>
</div>
</div>
<div :class="['form-group row', {'has-error' : errors.provinces }]">
<label class="col-sm-3">Provinsi</label>
<div class="col-sm-9">
<select #change="province" v-model="bendung.desa.district.city.province_id" class="form-control">
<option value="">Pilih Provinsi</option>
<option v-for="province in provinces" :value="province.id">
{{ province.name }}
</option>
</select>
</div>
</div>
<div :class="['form-group row', {'has-error' : errors.cities }]">
<label class="col-sm-3">Kota / Kabupaten</label>
<div class="col-sm-9">
<select #change="city" v-model="bendung.desa.district.city_id" class="form-control">
<option value="">Pilih Kota/Kabupaten</option>
<option v-for="city in cities" :value="city.id">
{{ city.name }}
</option>
</select>
</div>
</div>
</div>
<div class="row" v-else>
<div class="col-sm-12">
<content-placeholders>
<content-placeholders-text/>
</content-placeholders>
</div>
</div>
<div class="form-group row">
<div class="col-sm-3"></div>
<div class="col-sm-9">
<a class="btn btn-success" href="#" :disabled="submiting" #click.prevent="update">
<font-awesome-icon :icon="['fas', 'spinner']" v-if="submiting" />
<font-awesome-icon :icon="['fas', 'check']" v-else/>
<span class="ml-1">Perbaharui</span>
</a>
<a href="/sda/bendung" class="btn btn-danger">
<font-awesome-icon :icon="['fas', 'times-circle']" /> Batal</a>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
errors: {},
bendung: {},
provinces: [],
cities:[],
districts: [],
state: {
province: '',
city: '',
district: '',
},
loading: true,
submiting: false
}
},
mounted() {
this.getBendung();
this.getProvinces();
},
methods: {
getBendung() {
this.loading = true;
let str = window.location.pathname;
let res = str.split("/");
axios.get(`${process.env.MIX_WEBSERVICE_URL}/sda/bendung/${res[3]}`)
.then(response => {
this.bendung = response.data.data;
this.state.province = this.bendung.desa.district.city.province_id;
})
.catch(error => {
this.$toasted.global.error('Bendung tidak ditemukan!');
location.href = '/sda/bendung';
})
.then(() => {
this.loading = false;
});
},
getProvinces() {
axios.get(`${process.env.MIX_WEBSERVICE_URL}/location/province`).then(response => {
this.provinces = response.data;
}).catch(error => console.error(error));
},
province() {
this.state.city = '';
const params = {
province: this.state.province
}
axios.get(`${process.env.MIX_WEBSERVICE_URL}/location/city`, {params}).then(response => {
this.cities = response.data;
}).catch(error => console.error(error));
},
city() {
this.state.district = '';
const params = {
city: this.state.city
};
// /location/district?city=cityId
axios.get(`${process.env.MIX_WEBSERVICE_URL}/location/district`, {params}).then(response => {
this.districts = response.data;
}).catch(error => console.error(error));
}
}
}
</script>
<style scoped>
</style>
the result is like this picture.
i want to show city name on select box but i got blank selectbox and when i change the selectbox (e.g provinsi/province), the other selectbox (e.g. kabupaten/kota/city) will change it data.
You could fetch new data when previous data has been changed.
Here is the working example: https://codesandbox.io/embed/vue-template-2zv2o
Have you tried to use the v-bind:key prop within your v-for loop?
v-bind:key="city.id"
or better with an additional index:
v-for="(city, index) in cities"
[...]
v-bind:key="`${index}-${city.id}`"

How to save state of page even after reloading or closing the browser?

I am working on a quiz application using laravel as backend and vuejs to render the questions on the frontend. The thing that is confusing me how to store the state of quiz even after candidate reloads the page or he/she accidentally close the browser. I am thinking about saving the quiz progress in the database. Is there any better approach than this?
<template>
<div>
<div class="container quiz-steps" v-for="(question,index) in questions" v-bind:key="index">
<div v-show="index === currentIndex && timer>0">
<div>
<span class="badge badge-danger">{{ minutes }}</span>
<span class="badge badge-danger">{{ seconds }}</span>
</div>
<div class="progress">
<div class="progress-bar bg-danger" role="progressbar" :style="{width: returnTimerWidth()}" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<br>
<div class="container-quiz-question-pills">
<ul class="nav nav-pills quiz-question-pills">
<li> {{ wrong }}<i class="icon-remove"></i></li>
<li> {{ right }} <i class="icon-ok"></i></li>
</ul>
</div>
<div class="question-content">
<p>{{ question.question }}</p>
<!-- Material unchecked -->
<div class="form-check">
<input type="radio" v-model="picked" class="form-check-input" value="1" id="radio1" name="materialExampleRadios">
<label class="form-check-label" for="radio1">{{ question.option1 }}</label>
</div>
<!-- Material checked -->
<div class="form-check">
<input type="radio" v-model="picked" class="form-check-input" value="2" id="radio2" name="materialExampleRadios">
<label class="form-check-label" for="radio2">{{ question.option2 }}</label>
</div>
<div class="form-check">
<input type="radio" v-model="picked" class="form-check-input" value="3" id="radio3" name="materialExampleRadios">
<label class="form-check-label" for="radio3">{{ question.option3 }}</label>
</div>
<div class="form-check">
<input type="radio" v-model="picked" class="form-check-input" value="3" id="radio4" name="materialExampleRadios">
<label class="form-check-label" for="radio4">{{ question.option4 }}</label>
</div>
enter code here
</div>
<br><br><br><br>
<div>
<span> {{index+1}} / {{questions.length}} </span>
<button type="button" class="btn btn-outline-danger float-right btn-next" #click="nextQuestion(question.isCorrect)">Next</button>
</div>
<br>
<div class="progress">
<div class="progress-bar bg-danger" role="progressbar" :style="{width: returnWidth(index)}" aria-valuenow="100" aria-valuemin=0 aria-valuemax="100"></div>
</div>
</div>
</div>
<div v-if="currentIndex === questions.length || timer==0">
<div class="container thankyou-quiz-page">
<div class="text-center">
<p>Thnakyou for taking the Quiz!</p>
<br>
<div class="thankyou-msg">
<p>You have answered <span>{{ right }}</span> correct answers out of <span>{{ questions.length }}</span>. Your total time was <span>{{ minutesTaken }}:{{ secondsTaken }}</span>. The answers were sent to the administrator and he will contact you shortly.</p>
<p>Your total marks are {{ calculateScore() }}</p>
</div>
<br><br>
<div class="text-center quiz-choice">
Retake the Quiz<br>
Next Quiz
</div>
</div>
<br><br>
<div class="thankyou-message-button">
<button type="button" class="btn ">Retake the Quiz</button>
<button type="button" class="btn float-right ">Next Quiz</button>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name:'TEST',
props:['quizId'],
data(){
return{
currentIndex:0,
picked:'',
right:0,
wrong:0,
questions:[
],
timer:0,
total:0,
minutes:0,
seconds:0,
minutesTaken:0,
secondsTaken:0,
remainingTime:0,
done:false,
interval: '',
negative: 0,
totalMarks: 0,
type: 0
}
},
methods:{
nextQuestion:function(e){
if(this.picked){
if(e==this.picked){
this.right++;
}
else{
this.wrong++;
}
}
this.currentIndex++;
if(this.currentIndex == this.questions.length){
this.timer = 0;
}
this.picked = '';
},
returnWidth(e){
if( e==0 ){
return 0+'%';
}
else {
return e / this.questions.length * 100+'%';
}
},
returnTimerWidth(){
if( this.remainingTime == 0 )
{
return 0+'%';
}
else{
return this.remainingTime / this.total * 100 + '%';
}
},
loadQuestions(){
axios.get("http://192.168.1.3:8000/api/quiz/"+this.quizId).
then( ({ data }) => ( this.questions = data.data.questions,
this.timer = data.data.timeAllowed * 60,
this.total = this.timer,
this.negative = data.data.negativePercentage,
this.getTime(this)
) )
},
getTime(){
let interval = setInterval( () => {
this.minutes = parseInt(this.timer / 60, 10);
this.seconds = parseInt(this.timer % 60, 10);
this.minutes = this.minutes < 10 ? "0" + this.minutes : this.minutes;
this.seconds = this.seconds < 10 ? "0" + this.seconds : this.seconds;
if (--this.timer <0 ) {
// this.timer = 0;
this.totalTime();
clearInterval(interval);
}
else{
this.remainingTime++;
this.returnTimerWidth();
}
}, 1000);
},
totalTime(){
this.minutesTaken = parseInt(this.remainingTime / 60, 10);
this.secondsTaken = parseInt(this.remainingTime % 60, 10);
this.minutesTaken = this.minutesTaken < 10 ? "0" + this.minutesTaken : this.minutesTaken;
this.secondsTaken = this.secondsTaken < 10 ? "0" + this.secondsTaken : this.secondsTaken;
},
calculateScore(){
this.totalMarks = this.right - ( this.wrong * this.negative );
// if(this.type==1){
// axios.post('http://192.168.1.3:8000/api/quizMarks', {
// Marks: this.totalMarks
// })
// .then(function (response) {
// console.log(response);
// })
// .catch(function () {
// // console.log(error);
// });
//
// }
// else if(this.type==0){
// axios.post('http://192.168.1.3:8000/api/quizMarks', {
// Marks: this.totalMarks
// })
// .then(function (response) {
// console.log(response);
// })
// .catch(function () {
// // console.log(error);
// });
// }
return this.totalMarks;
}
},
created() {
this.loadQuestions();
}
}
</script>
One way is to use local storage and check if the key exists once the page is loaded.
A cleaner way is to use vuex https://vuex.vuejs.org/ and a local storage plugin such as vuex persist.

AJAX code not working on iPad only

We've created an event check-in page where nonprofits can add volunteers at an event. The page is working great on all platforms besides iPad. (iPhone Safari is working.)
On iPad, clicking id=add-volunteer button has no effect. Desired behavior is that clicking the button adds the volunteer name to the list of registered volunteers and saves the user info on the backend. (Other backend tasks such as sending an invite email should occur on the backend as well.)
HTML
<form id="form-add" method="post">
<input type="hidden" name="csrfmiddlewaretoken">
<div class="row one-margin-bottom center">
<div id="new-volunteers" class="small-12 medium-8 small-centered columns">
<h6 class="left">Add volunteer</h6>
<div class="row">
<div class="input-right small-6 columns">
<input class="center" id="new-firstname" name="user_firstname" placeholder="First name" type="text" required="">
</div>
<div class="input-right small-6 columns">
<input class="center" id="new-lastname" name="user_lastname" placeholder="Last name" type="text" required="">
</div>
<div class="input-right small-12 medium-12 columns">
<input class="lead center" id="new-email" name="user_email" placeholder="Email address" type="email" required="">
</div>
</div>
<div class="no-margin-top qtr-margin-bottom checkbox center">
<input type="checkbox" name="invite-optin" id="invite-optin" class="custom-checkbox" checked="">
<label for="invite-optin">Invite volunteer to openCurrents</label>
</div>
<a id="add-volunteer" class="half-margin-bottom button round small secondary">
Add volunteer
</a>
</div>
</div>
</form>
<form id="form-checkin" method="post">
<input type="hidden" name="csrfmiddlewaretoken">
<div class="row">
<div class="small-12 medium-8 small-centered columns">
<h6 class="left half-margin-bottom">Registered volunteers</h6>
<div class="row">
<div class="left small-8 columns">
<p class="small-text half-margin-top half-margin-bottom"><strong>Name / Email</strong></p>
</div>
<div class="right small-4 columns">
<p class="small-text half-margin-top half-margin-bottom"><strong>Check in</strong></p>
</div>
</div>
</div>
</div>
<div class="row">
<div id="registered-volunteers" class="small-12 medium-8 small-centered columns">
</div>
</div>
</form>
<div class="row">
<a href="/org-admin/" class="button round small secondary">
Back to org home
</a>
</div>
JS
let onCheckClick = function(event) {
let name = event.target.name;
let elem = $(`#${name}`);
let userid = elem.val();
let isChecked = elem.prop("checked");
$.ajax({
url: "{% url 'openCurrents:event_checkin' event_id %}",
type: 'POST',
data : {
csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[1].value,
userid: userid,
checkin: isChecked
},
dataType : "json",
context: document.body
}).done(function( data ){
// console.log('checkin request:');
// console.log(data);
if (isChecked) {
let dt = new Date();
$(`#vol-status-${userid}`).text(`${dt.toLocaleTimeString()}`);
$(`#vol-status-${userid}`).show();
}
else {
$(`#vol-status-${userid}`).text(`${data.responseText} min.`);
}
}).fail(function (data) {
// console.log('checkin error:')
// console.log(data);
elem.prop('checked', false);
$('#live-dashboard-error').slideDown();
});;
};
$(document).ready(function(){
// bind all existing checkbox to checkin
$("input[name^='vol-checkin']").click(onCheckClick);
$("#add-volunteer").click(function(event){
if ($(this).attr('disabled') === 'disabled') {
$('#live-dashboard-checkin-disabled').slideDown();
return;
}
if ($('#new-firstname').val() == '' || $('#new-lastname').val() == '' || $('#new-email').val() == '') {
$('#vol-error').slideDown();
} else if ( !(/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test($('#new-email').val() )) ) {
$('#vol-error').slideUp();
$('#email-error').slideDown();
} else {
$('#vol-error').slideUp();
$('#email-error').slideUp();
let new_firstname = $('#new-firstname').val();
let new_lastname = $('#new-lastname').val();
let new_email = $('#new-email').val();
let invite_optin = $('#invite-optin').prop('checked');
let process_signup_url;
let form_data = {
csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
user_firstname: new_firstname,
user_lastname: new_lastname,
user_email: new_email.toLowerCase()
};
if (invite_optin) {
process_signup_url = "{% url 'openCurrents:process_signup' endpoint=True verify_email=True %}"
form_data['org_admin_id'] = {{ user.id }};
}
else {
process_signup_url = "{% url 'openCurrents:process_signup' endpoint=True verify_email=False %}"
}
form_data['signup_status'] = 'vol'
$.ajax({
url: process_signup_url,
type: 'POST',
data: form_data,
dataType : "json",
}).done(function( data ) {
// console.log('signup response:')
// console.log(data);
let userid = data;
let form_data = {
csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
userid: userid
}
$.ajax({
url: "{% url 'openCurrents:event_register_live' eventid=event.id %}",
type: 'POST',
data: form_data,
dataType : "json",
}).done(function( data ) {
//console.log('register_live response:')
//console.log(data);
// hide any error messages present
$('#vol-exist').slideUp();
$('#live-dashboard-error-register').slideUp();
$('#live-dashboard-error').slideUp();
$('#vol-error').slideUp();
$('#email-error').slideUp();
$('#live-dashboard-not-in-progress').slideUp();
if (data.event_status >= 0) {
$('#registered-volunteers').prepend(`
<div class="row"> \
<div class="left small-9 columns"> \
<p class="half-margin-top"> \
${new_firstname} \
${new_lastname} \
</p> \
</div> \
<div class="right small-3 columns"> \
<input {% if checkin_disabled %}disabled{% endif %} type="checkbox" id="vol-checkin-${userid}" name="vol-checkin-${userid}" value="${userid}" class="hidden checkin-checkbox"/> \
<label class="checkin-checkbox-icon" for="vol-checkin-${userid}"> \
<i class="fa fa-lg fa-check-circle"></i> \
</label> \
</div> \
</div> \
`)
// clear form inputs
$('#new-firstname').val('');
$('#new-lastname').val('');
$('#new-email').val('');
$('#invite-optin').attr("checked", true);
// bind new checkbox to checkin
$(`input[name='vol-checkin-${userid}']`).click(onCheckClick);
if (data.event_status == 1) {
$(`input[name='vol-checkin-${userid}']`).trigger('click')
} else {
$('#live-dashboard-not-in-progress').slideDown();
}
}
else {
$('#new-firstname').val('');
$('#new-lastname').val('');
$('#new-email').val('');
$('#invite-optin').attr("checked", true);
$('#vol-exist').slideDown();
}
}).fail( function (data) {
// console.log('register_live error:')
// console.log(data);
$('#live-dashboard-error').slideDown();
});
}).fail(function (data) {
// console.log('signup error:')
// console.log(data);
$('#live-dashboard-error').slideDown();
});
}
});

Resources