How do I seperate an array so the data shows up as individual list items? - vue.js

I am struggling re-writing some older code I made in vue. I have a form which allows the user to enter multiple email recipients. The problem is that the array in which these emails are stored outputs them as one single item.
How do I seperate these email addresses?
This is my code. I know that Vue has something called Split, but I am not sure where I could use it here (or if it would change anything).
<template>
<b-form-group :label="label">
<vue-tags-input
v-model="inputValue"
:tags="tags"
:autocomplete-min-length="0"
:add-on-key="[',', 13]"
:autocomplete-items="filteredAutocompleteItems"
:placeholder="$t('addEmailAddressesHere')"
data-test-id="send-doc-email-to"
#before-adding-tag="addTag"
#tags-changed="tagsChanged"
class="mw-100"
/>
</b-form-group>
</template>
<script>
import {propertyService} from '#/services/property';
import {tenancyService} from '#/services/tenancy';
import {unitService} from '#/services/unit';
import {userService} from '#/services/user';
import VueTagsInput from '#johmun/vue-tags-input';
export default {
components: {
VueTagsInput
},
props: {
value: Array,
label: String,
entityId: String,
entityType: String,
prefill: {
type: Boolean,
default: false
},
asNotification: {
type: Boolean,
default: false
},
includeUser: {
type: Boolean,
default: false
}
},
data() {
return {
inputValue: '',
autocompleteItems: []
};
},
computed: {
tags() {
return (this.value || []).map(this.setText);
},
filteredAutocompleteItems() {
return this.autocompleteItems.filter(autocompleteItem =>
autocompleteItem.text.toUpperCase().includes(this.inputValue.toUpperCase()));
}
},
methods: {
addTag({tag, addTag}) {
if (!tag.recipients) {
tag.recipients = [{emailAddress: tag.text}];
}
addTag(tag);
},
setText(tag) {
tag.text = [tag.description, tag.recipients.map(recipient => recipient.emailAddress).join(', ')].filter(Boolean).join(' | ');
return tag;
},
tagsChanged(newTags) {
this.$emit('input', newTags);
},
load() {
switch (this.entityType) {
case 'TENANCY':
userService.getCurrentUser().then(userResult => {
tenancyService.getTenants(this.entityId).then(result => {
const defaultTags = [];
const recipients = result.data
.map(tenant => tenant.legalEntity)
.filter(legalEntity => legalEntity.email || (!legalEntity.email && this.asNotification ? legalEntity.name : null))
.map(legalEntity => ({
emailAddress: legalEntity.email || (!legalEntity.email && this.asNotification ? legalEntity.name.concat(' ', `(${this.$t('letterMail').toLowerCase()})`) : null),
legalEntityId: legalEntity.id
}));
if (recipients.length) {
defaultTags.push(this.setText({description: this.$t('tenants'), recipients}));
}
this.autocompleteItems.push(...defaultTags);
if (this.includeUser) {
defaultTags.push(this.setText({
description: this.$t('user'),
recipients: [{emailAddress: userResult.data.email}]
}));
}
if (this.prefill) {
this.tagsChanged(defaultTags);
}
tenancyService.getUnits(this.entityId).then(result =>
result.data.forEach(unitTenancy => this.addPropertyContactsToAutocompleteItems(unitTenancy.unit.propertyId)));
});
});
break;
case 'UNIT':
unitService.get(this.entityId).then(result =>
this.addPropertyContactsToAutocompleteItems(result.data.propertyId));
break;
case 'PROPERTY':
this.addPropertyContactsToAutocompleteItems(this.entityId);
break;
}
},
addPropertyContactsToAutocompleteItems(propertyId) {
propertyService.listContacts(propertyId).then(result => {
this.autocompleteItems.push(...result.data
.filter(contact => contact.email)
.map(contact => this.setText({
description: contact.profession ? this.$t(`model.contact.professions.${contact.profession}`) : null,
recipients: [{emailAddress: contact.email, legalEntityId: contact.id}]
}))
);
});
}
},
created() {
this.load();
}
};
</script>

Related

Definition for rule 'vue/require-prop-types' was not found vue/require-prop-types

I'm currently struggling with this issue when trying to build the administration on my shopware6 instance.
I am not sure to understand what it's expecting with this error
Definition for rule 'vue/require-prop-types' was not found vue/require-prop-types
This is pointing the line 22 which is :
props: {
Here's the index.js file :
import './custom-entity-single-select.scss';
import template from './custom-entity-single-select.html.twig';
const { Component, Mixin, Utils } = Shopware;
const { Criteria, EntityCollection } = Shopware.Data;
const { debounce, get } = Shopware.Utils;
Component.register('custom-entity-single-select', {
template,
inject: { repositoryFactory: 'repositoryFactory', feature: 'feature' },
mixins: [
Mixin.getByName('remove-api-error'),
],
model: {
prop: 'value',
event: 'change',
},
props: {
value: {
required: false,
},
highlightSearchTerm: {
type: Boolean,
required: false,
default: true,
},
placeholder: {
type: String,
required: false,
default: '',
},
resetOption: {
type: String,
required: false,
default: '',
},
labelProperty: {
type: [String, Array],
required: false,
default: 'name',
},
labelCallback: {
type: Function,
required: false,
default: null,
},
entity: {
required: true,
type: String,
},
resultLimit: {
type: Number,
required: false,
default: 25,
},
criteria: {
type: Object,
required: false,
default() {
return (new Criteria(1, this.resultLimit)).getAssociation('stateMachine').addFilter(Criteria.equals('state_machine_state.stateMachine.technicalName', 'order_transaction.state'));
},
},
context: {
type: Object,
required: false,
default() {
return Shopware.Context.api;
},
},
disableAutoClose: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
searchTerm: '',
isExpanded: false,
resultCollection: null,
singleSelection: null,
isLoading: false,
// used to track if an item was selected before closing the result list
itemRecentlySelected: false,
lastSelection: null,
};
},
computed: {
inputClasses() {
return {
'is--expanded': this.isExpanded,
};
},
selectionTextClasses() {
return {
'is--placeholder': !this.singleSelection,
};
},
repository() {
return this.repositoryFactory.create(this.entity);
},
/**
* #returns {EntityCollection}
*/
results() {
return this.resultCollection;
},
},
watch: {
value(value) {
// No need to fetch again when the new value is the last one we selected
if (this.lastSelection && this.value === this.lastSelection.id) {
this.singleSelection = this.lastSelection;
this.lastSelection = null;
return;
}
if (value === '' || value === null) {
this.singleSelection = null;
return;
}
this.loadSelected();
},
},
created() {
this.createdComponent();
},
methods: {
createdComponent() {
this.loadSelected();
},
/**
* Fetches the selected entity from the server
*/
loadSelected() {
if (!this.value) {
if (this.resetOption) {
this.singleSelection = {
id: null,
name: this.resetOption,
};
}
return Promise.resolve();
}
this.isLoading = true;
return this.repository.get(this.value, { ...this.context, inheritance: true }, this.criteria).then((item) => {
this.criteria.setIds([]);
this.singleSelection = item;
this.isLoading = false;
return item;
});
},
createCollection(collection) {
return new EntityCollection(collection.source, collection.entity, collection.criteria);
},
isSelected(item) {
return item.id === this.value;
},
debouncedSearch: debounce(function updateSearchTerm() {
this.search();
}, 400),
search() {
if (this.criteria.term === this.searchTerm) {
return Promise.resolve();
}
this.criteria.setPage(1);
this.criteria.setLimit(this.resultLimit);
this.criteria.setTerm(this.searchTerm);
this.resultCollection = null;
const searchPromise = this.loadData().then(() => {
this.resetActiveItem();
});
this.$emit('search', searchPromise);
return searchPromise;
},
paginate() {
if (!this.resultCollection || this.resultCollection.total < this.criteria.page * this.criteria.limit) {
return;
}
this.criteria.setPage(this.criteria.page + 1);
this.loadData();
},
loadData() {
this.isLoading = true;
return this.repository.search(this.criteria, { ...this.context, inheritance: true }).then((result) => {
this.displaySearch(result);
this.isLoading = false;
return result;
});
},
displaySearch(result) {
if (!this.resultCollection) {
this.resultCollection = result;
} else {
result.forEach(item => {
// Prevent duplicate entries
if (!this.resultCollection.has(item.id)) {
this.resultCollection.push(item);
}
});
}
if (this.resetOption) {
if (!this.resultCollection.has(null)) {
this.resultCollection.unshift({
id: null,
name: this.resetOption,
});
}
}
},
displayLabelProperty(item) {
if (typeof this.labelCallback === 'function') {
return this.labelCallback(item);
}
const labelProperties = [];
if (Array.isArray(this.labelProperty)) {
labelProperties.push(...this.labelProperty);
} else {
labelProperties.push(this.labelProperty);
}
return labelProperties.map(labelProperty => {
return this.getKey(item, labelProperty) || this.getKey(item, `translated.${labelProperty}`);
}).join(' ');
},
onSelectExpanded() {
this.isExpanded = true;
// Always start with a fresh list when opening the result list
this.criteria.setPage(1);
this.criteria.setLimit(this.resultLimit);
this.criteria.setTerm('');
this.resultCollection = null;
this.loadData().then(() => {
this.resetActiveItem();
});
// Get the search text of the selected item as prefilled value
this.searchTerm = this.tryGetSearchText(this.singleSelection);
this.$nextTick(() => {
this.$refs.customSelectInput.select();
this.$refs.customSelectInput.focus();
});
},
tryGetSearchText(option) {
if (typeof this.labelCallback === 'function') {
return this.labelCallback(option);
}
let searchText = this.getKey(option, this.labelProperty, '');
if (!searchText) {
searchText = this.getKey(option, `translated.${this.labelProperty}`, '');
}
return searchText;
},
onSelectCollapsed() {
// Empty the selection if the search term is empty
if (this.searchTerm === '' && !this.itemRecentlySelected) {
this.clearSelection();
}
this.$refs.customSelectInput.blur();
this.searchTerm = '';
this.itemRecentlySelected = false;
this.isExpanded = false;
},
closeResultList() {
this.$refs.selectBase.collapse();
},
setValue(item) {
this.itemRecentlySelected = true;
if (!this.disableAutoClose) {
this.closeResultList();
}
// This is a little against v-model. But so we dont need to load the selected item on every selection
// from the server
this.lastSelection = item;
this.$emit('change', item.id, item);
this.$emit('option-select', Utils.string.camelCase(this.entity), item);
},
clearSelection() {
this.$emit('before-selection-clear', this.singleSelection, this.value);
this.$emit('change', null);
this.$emit('option-select', Utils.string.camelCase(this.entity), null);
},
resetActiveItem(pos = 0) {
// Return if the result list is closed before the search request returns
if (!this.$refs.resultsList) {
return;
}
// If an item is selected the second entry is the first search result
if (this.singleSelection) {
pos = 1;
}
this.$refs.resultsList.setActiveItemIndex(pos);
},
onInputSearchTerm(event) {
const value = event.target.value;
this.$emit('search-term-change', value);
this.debouncedSearch();
},
getKey(object, keyPath, defaultValue) {
return get(object, keyPath, defaultValue);
},
},
});
And here you can find the whole gist containing this index.js, the html.twig file and the scss : https://gist.github.com/Youmar0504/2154bd1d16866d14644aa2a3a6fd513f
ALREADY TRIED
value: {
type: String,
required: false,
default: '',
},
value: {
type: Array,
required: false,
default: [],
},
I would assume you have an extra closing '}' at the value prop.
props: {
value: {
required: false,
} <--
},
highlightSearchTerm: {
type: Boolean,
required: false,
default: true,
},
placeholder: {
type: String,
required: false,
default: '',
},
resetOption: {
type: String,
required: false,
default: '',
},
labelProperty: {
type: [String, Array],
required: false,
default: 'name',
},
labelCallback: {
type: Function,
required: false,
default: null,
},
entity: {
required: true,
type: String,
},
resultLimit: {
type: Number,
required: false,
default: 25,
},
criteria: {
type: Object,
required: false,
default() {
return (new Criteria(1, this.resultLimit)).getAssociation('stateMachine').addFilter(Criteria.equals('state_machine_state.stateMachine.technicalName', 'order_transaction.state'));
},
},
context: {
type: Object,
required: false,
default() {
return Shopware.Context.api;
},
},
disableAutoClose: {
type: Boolean,
required: false,
default: false,
},
},

Vuejs - Resolve deep nested v-model property at runtime

I have a dynamic form where the v-model of the input control is resolved at runtime. It works for simple 0 or 1 level deep objects. But I do not know how to get it working for nested properties that are more than 1 level deep.
My HTML is like:
<div v-for="element in elements" v-bind:key="element.name">
<q-input v-model="inputdata[element.model]"></q-input>
</div>
Javascript
<script>
export default {
data () {
return {
inputdata: {
account: {
name: '',
address: {
street: ''
}
},
},
}
},
}
</script>
Array with data:
elements: [
{
type: 'text',
hint: 'Address',
label: 'Street',
model: 'account.address.street', // does not work. i want to be able to set any level deep property
name: 'street'
}
]
As long as I try to set the property at 0 or 1st level (inputdata or inputdata.account), it works.
How to get a property as deep as inputdata.account.name or inputdata.account.address.street to work?
maybe you can use custom iterative methods instead of v-model
const getValueByModel = (model, data) => {
if(model.includes('.')){
model = model.split('.');
let key = model.shift();
return getValueByModel(model.join('.'), data[key]);
}
else{
return data[model];
}
}
const setValueByModel = (model, oldObject, newValue) => {
if(model.includes('.')){
model = model.split('.');
let key = model.shift();
oldObject[key] = setValueByModel(model.join('.'), oldObject[key], newValue);
}
else{
oldObject[model] = newValue;
}
return oldObject;
}
const getValueByModel = (model, data) => {
if(model.includes('.')){
model = model.split('.');
let key = model.shift();
return getValueByModel(model.join('.'), data[key]);
}
else{
return data[model];
}
}
const setValueByModel = (model, oldObject, newValue) => {
if(model.includes('.')){
model = model.split('.');
let key = model.shift();
oldObject[key] = setValueByModel(model.join('.'), oldObject[key], newValue);
}
else{
oldObject[model] = newValue;
}
return oldObject;
}
new Vue({
el: '#app',
data () {
return {
inputdata: {
account: {
name: '',
address: {
street: ''
}
},
},
elements: [
{
type: 'text',
hint: 'Name',
label: 'Name',
model: 'account.name',
name: 'name'
},
{
type: 'text',
hint: 'Address',
label: 'Street',
model: 'account.address.street',
name: 'street'
},
]
}
},
methods: {
getInputValue(model){
return getValueByModel(model, this.inputdata);
},
updateInputValue(model, event){
let newValue = event.target.value;
this.inputdata = {...setValueByModel(model, this.inputdata, newValue)};
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="app">
<div v-for="element in elements" v-bind:key="element.name">
<input :value="getInputValue(element.model)"
#input="updateInputValue(element.model, $event)"
:placeholder="element.name"/>
</div>
{{ inputdata }}
</main>

Shopware 6: How to get all properties in custom module

I have one custom module and I need to get properties list in this module as a multi-select.
How it's possible in shopware 6?
I did try below code, but I can't get it.
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/custom-product-detail.html.twig
{% block custom_product_detail %}
<sw-page class="custom-product-detail">
<template slot="smart-bar-actions">
<sw-button
:disabled="isLoading"
:routerLink="{ name: 'custom.product.list' }">
{{ $t('custom-product.detail.actions.cancel-button') }}
</sw-button>
<sw-button-process
:isLoading="isLoading"
custom="primary"
#process-finish="saveFinish"
#click="onClickSave">
{{ $t('custom-product.detail.actions.save-button') }}
</sw-button-process>
</template>
<template slot="content">
<sw-card-view>
<sw-card
v-if="module"
:isLoading="isLoading"
class="information-card">
<sw-entity-multi-select
:label="$t('custom-product.detail.assignPropertyLabel')"
v-model="module.propertyGroupTranslation">
</sw-entity-multi-select>
</sw-card>
</sw-card-view>
</template>
</sw-page>
{% endblock %}
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/index.js
import template from './custom-product-detail.html.twig';
const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
const newLocal = 'custom_product';
Component.register('custom-product-detail', {
template,
inject: [
'repositoryFactory',
],
mixins: [
Mixin.getByName('notification')
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data() {
return {
module: null,
isLoading: false,
processSuccess: false,
repository: null
};
},
created() {
this.repository = this.repositoryFactory.create(newLocal);
this.getCustom();
},
computed: {
propertyGroupRepository() {
return this.repositoryFactory.create('property_group');
},
customProperties() {
const criteria = new Criteria();
criteria.addFilter(Criteria.equals('relations.entityName', 'property_group'));
return criteria;
}
},
methods: {
getCustom() {
this.customProperties();
const criteria = new Criteria();
criteria.addAssociation('propertyGroupTranslation');
this.repository
.get(this.$route.params.id, Shopware.Context.api, criteria)
.then((entity) => {
this.module = entity;
});
},
onClickSave() {
this.isLoading = true;
this.repository
.save(this.module, Shopware.Context.api)
.then(() => {
this.getCustom();
this.isLoading = false;
this.processSuccess = true;
}).catch((exception) => {
this.isLoading = false;
this.createNotificationError({
title: this.$t('custom-variant-product.detail.error-message'),
message: exception
});
});
},
saveFinish() {
this.processSuccess = false;
}
}
});
I got the solution.
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/custom-product-detail.html.twig
<sw-entity-multi-select
:label="$t('custom-product.detail.assignPropertyLabel')"
v-model="module.propertyGroupTranslation">
</sw-entity-multi-select>
replace to
<sw-multi-select
v-if="customProduct"
:label="$t('custom-product.detail.assign-property-group-options')"
:options="propertyOpt"
labelProperty="translated.name"
valueProperty="id">
</sw-multi-select>
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/index.js
import template from './custom-product-detail.html.twig';
const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('custom-product-detail', {
template,
inject: [
'repositoryFactory'
],
mixins: [
Mixin.getByName('notification')
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data() {
return {
customProductId: undefined,
customProduct: {},
isLoading: false,
processSuccess: false,
propertyOpt: [],
};
},
created() {
this.createdComponent();
},
computed: {
propertyOptionCriteria() {
const criteria = new Criteria();
criteria.addAssociation('group');
return criteria;
},
customRepository() {
return this.repositoryFactory.create('custom_product');
},
propertyOptionRepository() {
return this.repositoryFactory.create('property_group_option');
},
},
methods: {
getModule() {
this.customRepository.get(this.$route.params.id, Shopware.Context.api).then((entity) => {
this.customProduct = entity;
this.isLoading = false;
});
},
createdComponent() {
this.isLoading = true;
if (this.$route.params.id && this.customProduct.isLoading !== true) {
this.customProductId = this.$route.params.id;
this.loadPropertyOption();
}
this.isLoading = false;
},
loadPropertyOption() {
return this.propertyOptionRepository.search(this.propertyOptionCriteria, Shopware.Context.api)
.then((propertyOption) => {
this.propertyOpt = propertyOption;
});
},
}
});

Vuejs2: Vue Tables 2 - Multiple Custom filters

I'm trying to use a Vue table 2 filter to filter data by date, unfortunately it is not wroking and I am not able to find the reason. Has anyone tried such multiple filters with Vue table 2?
I went through the documentation but cannot find a solution.
https://matanya.gitbook.io/vue-tables-2/custom-filters
Html code to filter the data by date
<div class="col-md-4">
<div class="form-group">
<label for="sel1">Start Date:</label>
<input type="text" class="form-control" #keyup="applyFilterSearchText(searchText)" v-model="searchText" placeholder="End date" />
</div>
</div>
import { Event } from "vue-tables-2";
import axios from "axios";
export default {
title: "HelloWorld",
props: {
msg: String
},
data() {
return {
letters: ["Filled", "Unfilled", "Expired"],
selectedLetter: "",
searchText: "",
columns: ["refNumber", "vacancyTitle", "sector", "startDate", "endDate", "vacancyStatus"],
//data: getdetails(),
options: {
headings: {
refNumber: "Ref",
vacancyTitle: "Title",
sector: "Sector",
startDate: "Start",
endDate: "End",
vacancyStatus: "Status"
},
customFilters: [
{
name: "alphabet",
callback: function(row, query) {
return row.vacancyStatus == query;
}
},
{
name: "datefilter",
callback: function(row, query) {
return row.startDate == query;
}
}
],
// filterAlgorithm: {
// textsearch(row, query) {
// return (row.title).includes(query);
// }
// },
sortable: ["startDate", "vacancyTitle","endDate"],
sortIcon: {
base: "fa",
is: "fa-sort",
up: "fa-sort-asc",
down: "fa-sort-desc"
},
texts: {
filter: "Search by text:"
}
},
tableData:[],
};
},
methods: {
applyFilter(event) {
this.selectedLetter = event.target.value;
Event.$emit("vue-tables.filter::alphabet", this.selectedLetter);
},
applyFilterSearchText() {
console.log(this.searchText,"heiiiiiiiiiiii");
Event.$emit("vue-tables.filter::datefilter", this.searchText);
},
getdetails(){
axios.get("https://localhost:44399/api/Vacancy")
.then((res) => {
console.log(res.data,"ressssssss");
this.tableData = res.data;
})
.catch(function(error) {
console.log("Error: ", error);
});
}
},
mounted() {
this.getdetails();
}
};
A potential solution to that is to sort the data before using it in your data-table. Start by creating a computed of your data but with all your potential filters in it and create data variables with "parameters" (sort direction, sort column...)
export default {
title: "HelloWorld",
props: {
msg: String
},
data () {
return {
yourData: [],
sortBy: 'name',
sortDir: 'asc',
filterSearch: ''
}
},
computed: {
filteredData () {
if (filterSearch != '') {
let _this = this;
return this.sortedData.filter(item => {
return item.name.toLowerCase().includes(_this.filterSearch.toLowerCase());
})
} else {
return this.sortedData;
}
},
sortedData() {
return this.yourData.sort((a, b) => {
let modifier = 1;
if (this.sortBy == "order") {
if (this.sortDir === 'asc') {
return a[this.sortBy] - b[this.sortBy]
} else if (this.sortDir === 'desc') {
return b[this.sortBy] - a[this.sortBy]
}
} else {
if (this.sortDir === 'desc') modifier = -1;
if (a[this.sortBy] < b[this.sortBy]) return -1 * modifier;
if (a[this.sortBy] > b[this.sortBy]) return modifier;
return 0;
}
});
}
}
}
With that you just have to replace the props value you use to pass data to your VueTables

Vue - using $emit, .sync pattern with not required props

If I have a non-required prop is it possible to emit (using $emit('update:<prop name>', value) ) it in the child component and have it update to the emitted value without the parent component having :<prop name>.sync="<data name>"
example child component:
Vue.component('child-component', {
props: {
bar: {
type: String,
default: () => 'foo'
}
},
methods: {
doSomethingWithBar () {
this.$emit('update:bar', 'bar')
}
},
template: '<button #click="doSomethingWithBar">{{ bar }}</button>'
})
example parent:
<child-component /> <!-- notice no :bar.sync -->
Example on codepen:
https://codepen.io/anon/pen/jXaGJg
My current solution is:
Vue.component('child-component', {
props: {
bar: {
type: String,
default: () => 'foo'
}
},
data () {
return {
innerBar: null
}
},
mounted () {
this.innerBar = this.bar
},
methods: {
doSomethingWithBar () {
this.innerBar = 'bar'
}
},
template: '<button #click="doSomethingWithBar">{{ bar }}</button>',
watch: {
bar () {
this.innerBar = this.bar
},
innerBar () {
if (this.innerBar !== this.bar) {
this.$emit('update:bar', this.innerBar)
}
}
}
})
But this requires a lot of unnecessary code and I am quite sure there is a better idea.
UPDATE: (Actual context)
I am designing an audio component that wraps controls and playing Audio.
Example for audio.loop (I would have to do something similar to currentTime, paused, volume, etc.):
export default {
props: {
loop: {
type: Boolean,
default: () => false
},
audio: {
required: true,
type: HTMLAudioElement
},
},
data () {
return {
innerLoop: null
}
},
methods: {
toggleLoop () {
if (this.innerLoop) {
this.innerLoop = false
} else {
this.innerLoop = true
}
}
},
watch: {
loop () {
this.innerLoop = this.loop
},
innerLoop () {
this.audio.loop = this.innerLoop
if (this.innerLoop !== this.loop) {
this.$emit('update:loop', this.innerLoop)
}
}
}
}
This works but is there a better way?