Foundation tabs are not displaying if data is being dynamically retrieved from the database - ajax

I have a vue js application that fetches data from a database using ajax and presents them in a foundation tab. I have emulated everything as best I know how but unfortunately the content is not appearing.
My Vue component code is as below.
<template>
<section>
<pos-header></pos-header>
<div id="products">
<ul class="tabs" data-tabs id="product-types">
<li class="tabs-title" v-for="type, index in products.types" :class="{ 'is-active': index == 0}">
<a :href="'#' + type.name">
{{ type.name }}
</a>
</li>
</ul>
<div class="tabs-content" data-tabs-content="product-types">
<div v-for="type, index in products.types" class="tabs-panel" :class="{ 'is-active': index == 0}" :id="type.name">
<p>Products</p>
<p>{{ index }} {{ type }}</p>
</div>
</div>
</div>
</section>
</template>
<script>
import { mapActions } from 'vuex';
import Header from './includes/Header.vue';
export default{
data() {
return {
products: {
types: []
}
}
},
components: {
'pos-header': Header,
},
mounted(){
let url = this.$store.getters.getApiUrl + '/product/types';
axios.get(url).then( response => {
this.products.types = response.data.productTypes;
$(document).foundation();
});
}
}
</script>
The mounted method fetches the data and saves them in a data property called products types and I have ascertained that the items are being retrieved. This application is contained within a laravel application and laravel handles the backend api.
I should also add that i am able to see the links that change the tab content but the contents of the tabs are not displayed.

Related

Livewire nestable is resetting order on drop

using the following library to add sorting and nesting https://www.hesamurai.com/nested-sort/latest, I'm also using the pre-rendered list option to populate the items (https://www.hesamurai.com/nested-sort/latest/pre-rendered).
Now my html structure looks like the following:
<div x-data class="pageContent__content__components mb10">
<span class="subTitle">Menu items</span>
<ol x-init="initSort($refs.draggable)" x-ref='draggable' id="draggable"
class='nested-sort nested-sort--enabled'>
#foreach($navigation->navigation_items as $item)
<li wire:key='{{ $item->id }}' draggable='true'
data-id="{{ $item->id }}"> {{ $item->navigatable->page_name ?? $item->navigatable->blog_title }}
<i wire:click="showAlert('{{ $item->id }}')"
class="fa-solid fa-trash-can"></i>
</li>
#if($item->children->count() > 0)
<ol data-id="{{ $item->id }}">
#foreach($item->children as $child)
<li draggable='true'
data-id="{{ $child->navigatable->id }}"> {{ $child->navigatable->page_name ?? $child->navigatable->blog_title }}
<i wire:click="showAlert('{{ $child->navigatable->id }}')"
class="fa-solid fa-trash-can"></i>
</li>
#endforeach
</ol>
#endif
#endforeach
</ol>
</div>
With the scripts being included at the bottom of the page
#push('scripts')
#once
<script src="https://cdn.jsdelivr.net/npm/nested-sort#5/dist/nested-sort.umd.min.js"></script>
<script type='text/javascript'>
function initSort(ref) {
return {
nested: new NestedSort({
el: ref,
actions: {
onDrop: function(data) {
#this.
call('saveMenuOrder', data);
},
},
}),
};
}
</script>
#endonce
#endpush
Now, adding and removing items to the list works as intended, but reordering or nesting any item causes livewire to reset the list to it's initial state.
If I where to add a wire:ignore to the <ol> that technically fixes the issue of livewire updating the DOM, but that also means that, when adding or removing items it no longer updates the list without manually refreshing the page.
My backend component looks like this:
// the model containing the items to be displayed (via a HasMany relation)
public NavigationModel $navigation;
// the method that is called everytime the onDrop action fires (the $data array contains the new order of elements)
public function saveMenuOrder(array $data): void
{
foreach ($data as $menuItem) {
$menuItemObject = $this->navigation->navigation_items->find(
$menuItem['id']
);
$menuItemObject->order = $menuItem['order'];
if (isset($menuItem['parent'])) {
$menuItemObject->parent_id = $menuItem['parent'];
} else {
$menuItemObject->parent_id = null;
}
$menuItemObject->save();
}
}
And that's basically it for the component, all I want is for livewire to update the list without messing up the DOM elements created by the library.
any ideas?
alpineJS is also installed, if that's a better solution that's also accepted.
thanks!
--- Edit
What I currenly have:
I converted the laravel foreach to an alpine x-for:
<div class="pageContent__content" style="grid-area: unset">
<div x-data='initSort($refs.draggable)'
wire:ignore
class="pageContent__content__components mb10">
<span class="subTitle">Menu items</span>
<ol class='nested-sort' x-ref='draggable' id="draggable">
<template x-for="item in items">
<li draggable="true" :data-id='item.id'>
<div class='nested-sort-item'>
<div class='nested-sort-item__text' x-text='item.text' />
<div class='nested-sort-item__actions'>
<button type='button' class='nested-sort-item__actions__button'
#click='$wire.showAlert(item.id)'
>
<i class='fas fa-trash-alt'></i>
</button>
</div>
</div>
</li>
</template>
</ol>
</div>
</div>
and rewrote the init function:
function initSort(ref) {
return {
navigation_items: #js($this->navigation_items),
get items() {
return this.navigation_items;
},
init() {
new NestedSort({
el: ref,
actions: {
onDrop: function(data) {
},
},
});
},
};
}
Now I can't seem to figure out how to access the navigation_items inside of my onDrop action, simply using this.navigation_items or this.items console.logs undefined.

Laravel Vue Loading Side Bar Menu API

I am getting menu like below from api request with laravel vue.
{"data":[{"id":67,"slug":"link","name":"Dashboard","href":"\/","hasIcon":true,"icon":"mdi mdi-home-account","iconType":"coreui","sequence":1}]}
and my componant is below and displaying well but click event not woking , when i mannully put this json data --> menu then every thing working good. so i think mounted is not working as per expectation in this senario. i tried like passing it from another component as props , then putting it in data --> menu but not working as well any idea ?
<template>
<div class="vertical-menu">
<div data-simplebar="init" class="h-100 mm-show">
<div id="sidebar-menu">
<ul class="metismenu list-unstyled" id="side-menu">
<li v-for="(item, index) in menu" :key="index">
<router-link :to="item.href ? item.href : ''" :class="item.slug === 'dropdown' ? 'has-arrow' : '' ">
<i :class="item.icon"></i>
<span :data-key="item.datakey">{{item.name}}</span>
</router-link>
<ul class="sub-menu" aria-expanded="false">
<li v-for="subItem in item.elements" :key="subItem.name" >
<router-link :to="subItem.href ? subItem.href : ''"> {{subItem.name}} </router-link>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name:"DashboardLeftBar",
props:['app_base_url'],
data(){
return {
menu: null
}
},
mounted(){
this.menuSideBar();
},
methods:{
menuSideBar(){
let token= localStorage.getItem('jwt');
let crl=JSON.parse(localStorage.getItem('user')).crl;
axios.post(myconstant.APPICATION_API_URL+'/getmenu',{
token: token,
crl: crl,
}).then((response) => {
this.menu = response.data.data;
});
}
}
}
</script>

Why doesn't infinite-loading-vue3 work in a Laravel Project?

I am having a problem using this library in a Vue file. The project is being done with InertiaJS and Laravel.
The controller is composed of the following code:
public function getSaldosContables()
{
$saldocontables = SaldoContable::paginate(10);
return response()->json($saldocontables);
}
And the Vue file contains the following code:
<template>
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Listado</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div id="app" #scroll.passive="scrollear">
<infinite-scroll
#infinite-scroll="loadDataFromServer"
:message="message"
:noResult="noResult"
>
<div>
<div
v-for="repo in saldocontables"
:key="repo.idsaldocont"
style="margin-bottom: 20px"
>
<div>
<img :src="repo.idsaldocont" alt="" style="height: 50px" />
</div>
<div>{{ repo.nombresaldocont }}</div>
</div>
</div>
</infinite-scroll>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from "#/Layouts/AppLayout";
import InfiniteScroll from "infinite-loading-vue3";
import axios from "axios";
export default {
components: {
AppLayout,
InfiniteScroll,
},
data() {
return {
saldocontables: [],
page: 1,
noResult: false,
message: "",
};
},
methods: {
async loadDataFromServer() {
try {
const result = await axios.get("/obtenerSaldos?page=" + this.page);
if (result.data.data.length) {
console.log(this.page);
this.saldocontables.push(...result.data.data);
this.page++;
} else {
this.noResult = true;
this.message = "No result found";
}
} catch (err) {
this.noResult = true;
this.message = "Error loading data";
}
},
},
mounted() {
this.loadDataFromServer();
},
};
</script>
The Laravel route file handles the following encoding:
Route::get('/obtenerSaldos', [SaldoContableController::class, 'getSaldosContables']);
The data loaded in the database is rendered correctly. The problem is generated when the Infinite Scroll must load the following pages with the respective data. When it reaches the end of the page, it remains only loaded with the data of Page 1 and does not update the data contained in the following pages taking into account the pagination returned by the Backend.
This can be seen in the following image:
If I delete the following lines from my Vue:
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Listado</h2>
</template>
</app-layout>
The Template responds correctly and the Infinite Scroll also works perfectly.
You can see it in the following image:
The problem is that by doing this in the view the components that refer to the Side Menu, Top Menu, Responsive Menu, etc. do not appear.
I appreciate any help you can give me.

Vuejs preserve element attributes

My case looks like this:
Laravel template:
<div class="block-id" is="Header">
<span>ID #3265872</span>
<ul class="tools">
<li>History</li>
<li>TOP</li>
</ul>
<button>Check</button>
</div>
The vuejs component looks just the same
<template>
<div class="block-id">
<span>ID #{{ids.id}}</span>
<ul class="tools">
<li><a>History</a></li>
<li><a>TOP</a></li>
</ul>
<button>Check</button>
</div>
</template>
<script>
import {mapGetters} from 'vuex';
export default {
name: "Header",
computed: {
...mapGetters('global', [
'ids'
])
}
}
</script>
The problem is that when I render the component the href attribute is gone. So is there any way to preserve the href attribute in a element?

vue.js 2.0 load component dynamically in laravel 5.3

Is it possible to load a component dynamically when clicking a list item in vuejs 2.0 and laravel 5.3? I have a standard Laravel app but on one section of the site (shows reports) I want to turn this page into a single page application using vuejs. I load the default report in using a vue component and all works well, like so:
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li>Daily</li>
<li>Weekly</li>
<li>Season</li>
<li>Label</li>
<li></li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<reports-homepage></reports-homepage>
</div>
</div>
What I'm trying to achieve is when one of the li items is clicked the <reports-homepage></reports-homepage> is changed dynamically according to which li is pressed. So it could become <reports-daily></reports-daily> for example.
I've tried putting a #click on the li component and catching that in a script below in the blade section but that gives me the following error:
[Vue warn]: Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>.
As pointed in the official documentation you can use dynamic components. In your case it could look something like this:
HTML
<div id="app">
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li #click="setComponent('daily')">Daily</li>
<li #click="setComponent('weekly')">Weekly</li>
<li #click="setComponent('season')">Season</li>
<li #click="setComponent('label')">Label</li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<component v-bind:is="currentComponent"></component>
</div>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
currentComponent: 'daily'
},
components: {
'daily': { /* component object */ },
'weekly': { /* component object */ },
'season': { /* component object */ },
'label': { /* component object */ }
},
methods: {
setComponent: function (component) {
this.currentComponent = component;
}
}
})
And that should do what you are trying to achieve. Let me know if it helped!

Resources