How do you use confirm dialogues in a custom Laravel Nova tool? - laravel

Is it possible to use the built in Laravel Nova confirm dialogue in your own tool? All I would like to use is interact with it how Nova does itself.
The docs are quite light on the JS topic, as the only built in UI you seem to be able to work with is the toasted plugin: https://nova.laravel.com/docs/1.0/customization/frontend.html#javascript

You can use <modal> component whenever you want.
Here is how it work internally in Nova:
<template>
<modal
data-testid="confirm-action-modal"
tabindex="-1"
role="dialog"
#modal-close="handleClose"
class-whitelist="flatpickr-calendar"
>
<form
autocomplete="off"
#keydown="handleKeydown"
#submit.prevent.stop="handleConfirm"
class="bg-white rounded-lg shadow-lg overflow-hidden"
:class="{
'w-action-fields': action.fields.length > 0,
'w-action': action.fields.length == 0,
}"
>
<div>
<heading :level="2" class="border-b border-40 py-8 px-8">{{ action.name }}</heading>
<p v-if="action.fields.length == 0" class="text-80 px-8 my-8">
{{ __('Are you sure you want to run this action?') }}
</p>
<div v-else>
<!-- Validation Errors -->
<validation-errors :errors="errors" />
<!-- Action Fields -->
<div class="action" v-for="field in action.fields" :key="field.attribute">
<component
:is="'form-' + field.component"
:errors="errors"
:resource-name="resourceName"
:field="field"
/>
</div>
</div>
</div>
<div class="bg-30 px-6 py-3 flex">
<div class="flex items-center ml-auto">
<button
dusk="cancel-action-button"
type="button"
#click.prevent="handleClose"
class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
>
{{ __('Cancel') }}
</button>
<button
ref="runButton"
dusk="confirm-action-button"
:disabled="working"
type="submit"
class="btn btn-default"
:class="{
'btn-primary': !action.destructive,
'btn-danger': action.destructive,
}"
>
<loader v-if="working" width="30"></loader>
<span v-else>{{ __('Run Action') }}</span>
</button>
</div>
</div>
</form>
</modal>
</template>
<script>
export default {
props: {
working: Boolean,
resourceName: { type: String, required: true },
action: { type: Object, required: true },
selectedResources: { type: [Array, String], required: true },
errors: { type: Object, required: true },
},
/**
* Mount the component.
*/
mounted() {
// If the modal has inputs, let's highlight the first one, otherwise
// let's highlight the submit button
if (document.querySelectorAll('.modal input').length) {
document.querySelectorAll('.modal input')[0].focus()
} else {
this.$refs.runButton.focus()
}
},
methods: {
/**
* Stop propogation of input events unless it's for an escape or enter keypress
*/
handleKeydown(e) {
if (['Escape', 'Enter'].indexOf(e.key) !== -1) {
return
}
e.stopPropagation()
},
/**
* Execute the selected action.
*/
handleConfirm() {
this.$emit('confirm')
},
/**
* Close the modal.
*/
handleClose() {
this.$emit('close')
},
},
}
</script>
Here is simplified example:
<modal>
<form
autocomplete="off"
class="bg-white rounded-lg shadow-lg overflow-hidden"
>
<div>
<heading :level="2" class="border-b border-40 py-8 px-8">test</heading>
test
</div>
<div class="bg-30 px-6 py-3 flex">
<div class="flex items-center ml-auto">
<button
type="button"
class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
>
{{ __('Cancel') }}
</button>
<button
ref="runButton"
type="submit"
class="btn-danger"
>
<span>{{ __('Run Action') }}</span>
</button>
</div>
</div>
</form>
</modal>

You need to create a new component in the same folder of Tool.vue
I'll attach the component I used here
Then in the "handleConfirm" method, you can add a Ajax call to API
You can add you logic in that API.
You can find the API file in path, ToolName/routes/api.php
/* CustomModal.vue */
<template>
<modal tabindex="-1" role="dialog" #modal-close="handleClose">
<form #keydown="handleKeydown" #submit.prevent.stop="handleConfirm" class="bg-white rounded-lg shadow-lg overflow-hidden w-action">
<div>
<heading :level="2" class="border-b border-40 py-8 px-8">Confirm action</heading>
<p class="text-80 px-8 my-8"> Are you sure you want to perform this action? </p>
</div>
<div class="bg-30 px-6 py-3 flex">
<div class="flex items-center ml-auto">
<button type="button" #click.prevent="handleClose" class="btn btn-link dim cursor-pointer text-80 ml-auto mr-6">
Cancel
</button>
<button :disabled="working" type="submit" class="btn btn-default btn-primary">
<loader v-if="working" width="30"></loader>
<span v-else>Confirm</span>
</button>
</div>
</div>
</form>
</modal>
</template>
<script>
export default {
methods: {
handleConfirm() {
// Here you can add an ajax call to API and you can add your logic there.
},
handleClose() {
// Logic to hide the component
},
},
}
</script>
For more detailed explanation : https://medium.com/vineeth-vijayan/how-to-use-confirm-dialogs-in-a-laravel-nova-tool-b16424ffee87

Related

Description not saving when using summernote

I'm trying to create a page that when you click on the create product button
a modal pops up and then you enter whatever you like.
I'm using summernote as my WYSIWYG editor and I'm also using vue as my frontend.
The problem I'm having is that my description isn't being saved.
In my layouts.app I have summernote's cdn.
Here is my code
My Create.vue
<template>
<transition name="modal-fade">
<div>
<div class="modal-backdrop show"></div>
<div class="modal" style="display: inline;">
<div class="modal-dialog modal-dialog-scrollable" role="document" style="width: 680px; max-width: 680px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create Product</h5>
<button type="button" class="close" #click="close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div>
<label>Name</label>
<input type="text" class="form-control" v-model="name">
</div>
<div class="mt-2">
<label>Description</label>
<textarea id="summernote" v-model="description"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" #click="close">Close</button>
<button type="button" class="btn btn-success" #click="save">Save</button>
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
props: [],
data() {
return {
name: null,
description: null,
}
},
methods: {
close() {
this.$emit('close');
},
save() {
var description = document.getElementById("summernote").value;
axios.post('/admin/products', {
name: this.name,
description: description,
}).then((response) => {
});
}
},
mounted(){
$('#summernote').summernote();
}
}
</script>
<style>
.modal-fade-enter,
.modal-fade-leave-active {
opacity: 0;
}
.modal-fade-enter-active,
.modal-fade-leave-active {
transition: opacity .25s ease

vuejs stop propagation of click when clicked outer div

I am trying to create a search show hide feature. I have a click event that shows the search bar, but if I click somewhere in the put it get removed again. I tried with click.stop but it doesn't work. I am using vue.js inside a laravel project.
Here is my code
<template>
<div>
<div class="menu_srch d-flex" #click.stop="dos">
<i class="fa fa-search search_btn"></i>
<div class="header_serch " v-if="showSearch">
<div class="header_serch_input">
<input type="" name="" placeholder="Search">
</div>
<div class="header_serch_i">
<i class="fa fa-search"></i>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
showSearch : false,
}
},
methods: {
dos(){
this.showSearch = !this.showSearch
}
},
}
</script>
using click.self doesn't even work.. dos method doesn't run when click.self is used.
Vue.js version : "^2.6.11"
You can capture the click event end to stop the propagation one level down.
<template>
<div>
<div class="menu_srch d-flex" #click="dos">
<i class="fa fa-search search_btn"></i>
<div #click.stop class="header_serch" v-if="showSearch">
<div class="header_serch_input">
<input type name placeholder="Search">
</div>
<div class="header_serch_i">
<i class="fa fa-search"></i>
</div>
</div>
</div>
</div>
</template>
Or you need to restructure your template.
<template>
<div>
<div class="menu_srch d-flex" #click="dos">
<i class="fa fa-search search_btn"></i>
</div>
<div class="header_serch" v-if="showSearch">
<div class="header_serch_input">
<input type name placeholder="Search">
</div>
<div class="header_serch_i">
<i class="fa fa-search"></i>
</div>
</div>
</div>
</template>

bootstrap 4 modal not showing after button is clicked with VueJs

I just learned vuejs with bootstrap 4 and I tried to display modal provided that when the create button was not clicked then the HTML modal tag is not displayed in the inspect element, and after the create button click bootstrap modal displayed. When the button create first time click HTML modal tag display on inspect element but bootstrap modal cannot be displayed on the browser page and the second time the bootstrap modal can be displayed. Here is the source code that I made with Laravel, can you help me on this issue, thank you.
User.vue
HTML
<template>
<div class="container">
<div class="row">
<div class="col-sm">
<div class="card-deck mb-3">
<div class="card mb-4 box-shadow">
<div class="card-header bg-success text-white">
<h4 class="my-0 font-weight-bold">
<!-- Button create user -->
<button #click="initAddUser()" class="btn btn-danger btn-sm"><i class="fa fa-plus"></i> Create New User</button>
<span class="float-right">User</span>
</h4>
</div>
<div class="card-body">
<!-- Bootstrap modal -->
<div v-if="showModal" class="modal fade" id="add_user_model" tabindex="-1" role="dialog" aria-labelledby="add_user_model_label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-info text-white">
<h5 class="modal-title" id="add_user_model_label"><i class="fa fa-plus"></i> Add New User</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<!-- Message errors create user -->
<div class="alert alert-danger" v-if="errors.length > 0">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<h5>{{ messages }}</h5>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</div>
<div class="form-group">
<label class="font-weight-bold" for="name">Name :</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Name" v-model="user.name">
</div>
<div class="form-group">
<label class="font-weight-bold" for="name">Email :</label>
<input type="email" class="form-control" id="email" name="email" placeholder="E-mail" v-model="user.email">
</div>
<div class="form-group">
<label class="font-weight-bold" for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Password" v-model="user.password">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-sm btn-primary" #click="createUser()">Submit</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
Js
<script>
export default {
data() {
return {
user: {
name: '',
email: '',
password: ''
},
errors: [],
users: [],
showModal: false,
}
},
mounted() {
this.readUsers();
},
methods: {
initAddUser() {
this.errors = [];
$("#add_user_model").modal("show");
this.showModal = true;
},
reset() {
this.user.name = '';
this.user.email = '';
this.user.password = '';
},
readUsers() {
axios.get('/user/show')
.then(response => {
this.users = response.data.users;
});
},
createUser() {
axios.post('/user', {
name: this.user.name,
email: this.user.email,
password: this.user.password,
})
.then(response => {
this.reset();
this.users.push(response.data.user);
this.readUsers();
$("#add_user_model").modal("hide");
})
.catch(error => {
this.errors = [];
this.messages = error.response.data.message;
if (error.response.data.errors.name) {
this.errors.push(error.response.data.errors.name[0]);
}
if (error.response.data.errors.email) {
this.errors.push(error.response.data.errors.email[0]);
}
if (error.response.data.errors.password) {
this.errors.push(error.response.data.errors.password[0]);
}
});
}
}
}
</script>
Result images inspect element
This image button first time click https://ibb.co/jKENbU
This image button second time click https://ibb.co/nRy4qp

cannot insert data into vue select sent from laravel

This is the vuejs component for editing song info. the problem here is with tags.I cannot show the tags of the song in vue select for editing.
<template>
<div>
<a data-toggle="modal" :data-target="'#EditModal'+ modalid" #click="select(song)"><span title="edit" class="glyphicon glyphicon-pencil" ></span></a>
<a class=""><span title="delete" class="glyphicon glyphicon-trash"></span></a>
<!-- Modal for editing song tracks-->
<div class="modal fade" :id="'EditModal'+ modalid" tabindex="-1" role="dialog" aria-labelledby="EditModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="EditModalLabel">Edit Song</h5>
<button type="button" class="close" ref="closemodal" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form ref="uploadform">
<div class="form-group">
<div class="row">
<div class="col-md-12">
<div class="col-md-5">
<button type="button" #click="browseImage" class="btn btn-md btn-default">Choose image:</button>
<div id="image_previews">
<img ref='image' class="" v-bind:src="image" width="200px" height="200px" >
<input class="form-control-file" ref="imageinput" type="file" name="feature_image" #change="showImage($event)">
</div>
</div>
<div class="col-md-7">
<div class="form-group">
<label for="title">Song Title:</label>
<input type="text" v-model="esong.title" class="form-control" required maxlength="255">
</div>
<div class="form-group">
<label for="genre"> Genre (tag atleast one) </label>
<v-select :placeholder="'choose tag'" v-model="tagids" label="name" multiple :options="tags"></v-select>
</div>
<div class="form-group">
<label for="upload_type">Song Upload Type</label>
<select name="upload_type" v-model="esong.upload_type" class="form-control">
<option value="public">public( free )</option>
<option value="private">private( for sale )</option>
</select>
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Description:</label>
<textarea class="form-control" id="message-text" v-model="esong.song_description"></textarea>
</div>
<div class="form-group" v-if="private">
<label for="upload_type">Song price</label>
<input type="text" v-model="esong.amount" class="form-control" required maxlength="255">
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" #click="edit">Save</button>
</div>
</div>
</div>
</div><!-- end of modal -->
</div>
</template>
<script>
import vSelect from 'vue-select'
export default {
props: ['song','modalid','index','tags'],
components: {vSelect},
mounted() {
},
watch: {
tagids() {
console.log('changed tagids value');
// this.value = this.tagsid;
}
},
computed: {
private() {
if(this.esong.upload_type == 'private') {
return true;
}
return false;
},
},
methods: {
select(song) {
console.log(song.title);
this.getTagIds(song);
},
edit() {
let formData = new FormData();
formData.append('title', this.esong.title);
formData.append('img', this.esong.img);
formData.append('description', this.esong.song_description);
formData.append('upload_type', this.esong.upload_type);
formData.append('amount', this.esong.amount);
formData.append('tags', JSON.stringify(this.tagids));
formData.append('_method', 'PUT');
axios.post('/artist/songs/' + this.esong.id, formData,{
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response =>{
this.$refs.closemodal.click();
toastr.success('successfully edited song.');
this.$emit('update', {song:this.esong,index:this.index});
}).catch(error => {
console.log(error);
});
},
getTagIds(song) {
axios.post('/gettagids', song ).then(response =>{
this.tagids = response.data;
}).catch(error =>{
console.log(error);
});
},
browseImage() {
this.$refs.imageinput.click();
},
showImage(event) {
this.esong.img = event.target.files[0];
this.image = URL.createObjectURL(event.target.files[0]);
}
},
data() {
return {
esong: this.song,
tagids: {id:'', name:'', label:''},
name:'name',
image:this.song.image
}
}
}
</script>
<style scoped>
input[type="file"] {
display: none;
}
#image_previews {
border-radius: 5px;background-color: whitesmoke; width: 200px; height: 200px;
}
.btn{
border-radius: 0px;
}
</style>
here I cannot get the selected value that was inserted in my table. I wanted to show the tagged values for a song. I am able to get all object of tagged songs from axios post request but v-select doesn't shows the selected value retrieved from a table.
the object received from laravel is similar to the object provided in options which work well with v-select..but it doesn't show the same structure object provided to v-model..or should I provide other props. the document for vue select has not mentioned any of these things

Can't pass Vue variable to hidden input v-model in view(v-for)

I'm new to Vue JS and I'm building an application with Laravel Spark and trying to utilize Vue as much as possible.
I have a form to simply add an 'Asset Type' with a component. Once the Asset Type is successfully created, a list of properties is grabbed from the database and set to a 'data' attribute. In my view(I'm using an inline template), I have a 'v-for' that creates a form for each property that has two hidden inputs for the property id and the type id, and one "Add" button that assigns the property to the newly created type.
THE PROBLEM:
I can't seem to assign the value of the hidden inputs within the view while using v-models. When I submit one of the forms, the form request data always returns the initial data value from the new SparkForm object.
In other words, I need to assign the hidden input values within the v-for loop in the view.
Here's my component:
Vue.component('new-asset-type', {
props: [],
data() {
return {
// type_id: this.type_id,
properties: this.properties,
newAssetType: new SparkForm({
label: null,
enabled: false,
}),
assignPropForm: new SparkForm({
prop_id: "",
type_id: "",
}),
};
},
methods: {
createType: function () {
Spark.post('/asset-types', this.newAssetType)
.then(response => {
this.type_id = response.type_id;
axios.get('/getTypeNotProps/' + this.type_id).then((response) => {
this.properties = response.data;
console.log(this.properties);
});
})
.catch(response => {
console.log("fail");
});
},
assignProp: function () {
Spark.post('/asset-properties/add', this.assignPropForm)
.then(response => {
console.log(response);
})
.catch(response => {
console.log("fail");
});
}
}
});
And here's my view:
#extends('spark::layouts.app')
#section('content')
<new-asset-type inline-template>
<div class="container">
<!-- Application Dashboard -->
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Add a New Asset Type</div>
<div class="panel-body" id="addTypeForm">
<div class="form-horizontal">
<div class="form-group" :class="{'has-error': newAssetType.errors.has('label')}">
{{ Form::label('label', 'Label', ['class' => 'col-md-4 control-label']) }}
<div class="col-md-6" >
<input type="test" name="label" v-model="newAssetType.label">
<span class="help-block" v-show="newAssetType.errors.has('label')">
#{{ newAssetType.errors.get('label') }}
</span>
</div>
</div>
<div class="form-group">
{{ Form::label('enabled', 'Enabled?', ['class' => 'col-md-4 control-label']) }}
<div class="col-md-6">
<input type="checkbox" name="enabled" v-model="newAssetType.enabled" >
</div>
</div>
<!-- Submit -->
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<button class="btn btn-primary" #click="createType" :disabled="newAssetType.busy">
Create Asset Type
</button>
</div>
</div>
<div id="assignProps" v-if="newAssetType.successful">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
Add Property
</button>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Add New Property to Asset Type</h4>
</div>
<div class="modal-body">
<assign-asset-prop v-for="property in properties" class="panel panel-info property-item">
<div class="panel-heading">#{{ property.label }}</div>
<div class="panel-body"><strong>Input Type: </strong>#{{ property.input_type }}
<div class="pull-right">
<input type="hidden" name="prop_id" v-bind:value="property.p_id" v-model="assignPropForm.prop_id">
<input type="hidden" name="type_id" v-bind:value="property.p_id" v-model="assignPropForm.type_id">
<button class="btn btn-primary" #click="assignProp" :disabled="assignPropForm.busy">
Add
</button>
</div>
</div>
</assign-asset-prop>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</new-asset-type>
#endsection
Thanks to the helpful comments, I learned that I shouldn't have been using hidden inputs at all. Instead, I simply passed the variables to the function on the click event.
<button class="btn btn-primary" #click="assignProp(type_id, property.p_id)" >
Add
</button>
Then in my component
methods: {
assignProp: function (type_id, property_id) {
var assignPropForm = new SparkForm({
propvalue: property_id,
typevalue: type_id,
});
Spark.post('/asset-properties/add', assignPropForm)
.then(response => {
console.log(response);
})
.catch(response => {
console.log("fail");
});
}
}
You need store variables at local data() dep., and geting it by getters function.

Resources