Materialize: Cannot set property 'tabIndex' of null at Dropdown._makeDropdownFocusable - vuejs2

I am trying to test my vuejs component via jest that contains materialize select.
When performing a component test, I get the following error in materialize.js:
TypeError: Cannot set property 'tabIndex' of null at Dropdown._makeDropdownFocusable
How fix this error?

This problem can happen when the input field is not wrapped inside a div with the class input-field:
<div class="input-field">
<input type="text" class="autocomplete"></input>
</div>
Adding a div with the class "input-field might solve this problem.

use id selector instead class selector. for example call dropdown like this :
html :
<a class='dropdown-trigger' id="dropdowner" href='#' data-target='dropdown1'>Drop Me!</a>
<!-- Dropdown Structure -->
<ul id='dropdown1' class='dropdown-content'>
<li>one</li>
<li>two</li>
<li class="divider" tabindex="-1"></li>
<li>three</li>
<li><i class="material-icons">view_module</i>four</li>
<li><i class="material-icons">cloud</i>five</li>
</ul>
js:
$('#dropdowner').dropdown();

Can only be used once.
data-target="name_target" must not be repeated
Exam1.❌
<nav>
<div class="nav-wrapper">
<ul class="right hide-on-med-and-down">
<li><a class="dropdown-trigger" href="#!" data-target="name_target1">Dropdown<i class="material-icons right">arrow_drop_down</i></a></li>
<li><a class="dropdown-trigger" href="#!" data-target="name_target1">Dropdown<i class="material-icons right">arrow_drop_down</i></a></li>
</ul>
</div>
</nav>
<!-- Dropdown Structure -->
<ul id="name_target1" class="dropdown-content">
<li>one</li>
<li>two</li>
</ul>
Exam2.✔️
<nav> <div class="nav-wrapper">
Logo
<ul class="right hide-on-med-and-down">
<li><a class="dropdown-trigger" href="#!" data-target="name_target2">Dropdown<i enter code here class="material-icons right">arrow_drop_down</i></a></li>
</ul> </div> </nav> <ul id="name_target2" class="dropdown-content"> <li>one</li> <li>two</li> </ul>

When I ran into this issue I was trying to create the whole dropdown list dynamically in JS. The fix for me was creating the list and any default list elements in HTML:
<div id="select1" class=\"input-field col s12\">
<select>
<option value="" selected>Default</option>
</select>
<label>Test</label>
</div>
Then appending any dynamic values in JS:
contents.forEach(function(content) {
var buffer = "<option></option>";
var template = $(buffer);
$(template).text(content);
$("select1").find("select").append(template);
});
$("select").formSelect();

pre 1.0.0 you would use data-activates, if data-target is not specified you will get this error

My problem was, that jQuery object was not attached to the DOM yet, so inner materialise code could not init element due to inability to find element by ID:
// materializecss initing dropdown (in my case for input autocomplete), where `t` is the input element
i.id = M.getIdFromTrigger(t),
i.dropdownEl = document.getElementById(i.id),
i.$dropdownEl = h(i.dropdownEl),
M.getIdFromTrigger(t) returned some random ID (not the one I provided) and dropdownEl was inited with null, and later method _makeDropdownFocusable failed on using it `this.dropdownEl.tabIndex = 0
So my problem code looked like this:
let root = $('#root'); // root in the DOM already
let wrapper = $('<div>'); // wrapper is just created and NOT attached to the DOM yet
let input = $('<input>').appendTo(wrapper); // creating input and attaching to the wrapper, but still not in DOM
initAutocomplete(input) // M.Autocomplete.init logic here FAILS
root.append(wrapper) // too late, error above
So the quick fix is to append elements first and only than do M.Autocomplete.init

I just stumbled this issue too while using Materializecss for my Vue project. As mentioned by sajjad, using id selector instead of class works. However, this is problem for initializing multiple dropdown, since each dropdown must have unique ID.
The way I solve this is just by selecting all the elements with the '.dropdown-trigger' class, and initialize every each of those. It works for me.
$.each($('.dropdown-trigger'), function(index, value) {
$(value).dropdown();
});

Related

ASP.NET Core Web App project (Dropdown buttons, handle click action, show on card with header

I'm new in this Visual studio coding.
I'm trying to handle the click event in a dropdown button in order to shoe me some content in the "header and footer card" on the same view.
I have this so far
<button type="button" style="margin:5px" class="btn btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" >
DATA ANALYTICS
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
My question is the follow.
So I click the button. Then I want to show some content on the card, but I don't know where should I put the content (in cshtml or cshtml.cs) and how to handle the event (click).
As far as my understanding about this code is, I need to declare a var (id) inside of the div class.
Well I'm kind of lost right now.
I'll appreciate any help you guys.
P.S. This is not a homework question or something like that. I just want to learn more about this coding style.
If you want to click Action and then change the content of div,you can call JavaScript function in href:
<a class="dropdown-item" href="javascript:MyFunction();">Action</a>
js:
<script>
var id;
function MyFunction() {
//you can change the content of div here,also you can change a js variable here
}
</script>
You will have to use javascript to do this on the same page. If you want to be redirected to another page you can use tag-helpers for that, but I think it is not what you want. I would do it like so - either I would put an Id on the anchor tag, put href="javascript:void(0)" and in the script use:
<script>
const element = document.getElementById('id');
element.addEventListener('click', onClick);
function onClick(ev) {
ev.preventDefault();
//do rendering logic.
}
</script>
Or in the html:
<a class="dropdown-item" href="javascript:void(0)" onclick="javascript:onClickRender();">Action</a>
And script
<script>
function onClickRender(ev) {
//Render Logic
}
</script>
For this type of dynamic rendering you need javascript.

Pass data from Vue loop to laravel component

I am having this issue and I can not find my way around it without duplicating lots of code.
I have an array of entries coming from an axios request. Everything will go in an ul.
If I am doing it like this, everything is ok:
resource-index.blade.php
<ul>
<li v-for="entry in entries" :key="entry.id" >
<div>
<div>
<a :href="entry.links.show">
<x-button-icon color="gray-400">
#include('partials.svg.outline-eye')
</x-button-icon>
<span class="ml-3">{{ __('View') }}</span>
</a>
</div>
<div>
<a :href="entry.links.edit">
<x-button-icon color="gray-400">
#include('partials.svg.pencil')
</x-button-icon>
<span class="ml-3">{{ __('Edit') }}</span>
</a>
</div>
</div>
</li>
</ul>
However, in case I want to try to extract some of that stuff in different components, the details I am sending from Vue no longer get passed to the component.
resource-index.blade.php
<ul>
<li v-for="entry in entries" :key="entry.id" >
<div>
<x-grid-list-item-button label="{{ __('View') }}" :href="entry.links.show">
<x-slot name="icon">
#include('partials.svg.outline-eye')
</x-slot>
</x-grid-list-item-button>
<x-grid-list-item-button label="{{ __('Edit') }}" :href="entry.links.edit">
<x-slot name="icon">
#include('partials.svg.pencil')
</x-slot>
</x-grid-list-item-button>
</div>
</li>
</ul>
And here is the grid-list-item-button.blade.php
<div>
<a href="{{ $href }}">
#if($icon)
<x-button-icon color="gray-400">
{{ $icon }}
</x-button-icon>
#endif
<span class="ml-3">{{ $label }}</span>
</a>
</div>
I already tried:
moving the href="entry.links.show" in a named slot;
passing the data with v-bind: v-bind:href="entry.links.show";
::href="entry.links.show"
Can someone please tell what am I doing wrong or at least point me in the right direction with this?
Thanks!
If I got you right: You are trying to pass data from Vue.Js to Laravel-Components. Unfortunately this is not possible. Blade gets processed on the server-side where Vue.Js is not yet available. So the variable entry.links.show do not yet exist in Laravel (only available on client-side) and therefore cannot be passed to the Laravel-Component. After the HTML got rendered by Blade and passed to the Browser, Vue.Js can pick it up and replicate your template for the v-for and generate your list. At this point your 'link'-variables get available.
Solutions:
You could extract your code to a Vue.Js-Component rather than a Laravel-Component. This would be interpreted on client-side.
An other solution would be to generate this list through Blade so you could use Laravel-Components.

v-on:click not working - dynamically added html element

I am trying to get all images for a category using Vue
<div class="col-md-12 col-sm-2 p-2">
<a v-on:click="onCategoryManageImageClick($event)" data-target="#location-
category-images">
</span>
</a>
</div>
So the event onCategoryManageImageClick ($event) does not work, if I am adding a html block and then click on get image button.
this is index.js
methods:{
onImagesTabClick(){
this.$root.$emit('activated-tab:location-images');
},
onCategoriesTabClick(){
window.j1App.eventBus.$emit("j1-location-image-list:shown");
},
onCategoryManageImageClick: function(event) {console.log('working event..');
event.preventDefault();
window.j1App.eventBus.$emit("j1-location-category-image-
list:shown",event.currentTarget.id);
}
}
So basically it need to to work like we do in jQuery
$(document).on('click',function{
})
So it works either page load or if adding any new html element on DOM. same I want in Vue.
You cannot alter the Vue template outside of Vue. That won't work. Vue compiles the template once when starting up and adds the event listeners to the rendered elements. If you add elements afterwards, Vue will not know about them.
The correct way of doing this would be to add those new elements in Vue.
<div
class="col-md-12 col-sm-2 p-2"
v-for="item in items"
:key="item.id"
>
<a
v-on:click="onCategoryManageImageClick($event, item)"
data-target="#location-category-images"
>
</a>
</div>
See https://v2.vuejs.org/v2/guide/list.html for documentation. In this case you need the items array variable in data and add more array elements to it, if you need more links.
Try raplacing your a tag with p
<div class="col-md-12 col-sm-2 p-2">
<p v-on:click="onCategoryManageImageClick" data-target="#location-
category-images">
</p>
</div>

change active tab on document ready

I would like to change the active pill/tab on document load. I know you can set the active pill like I have below but for other reasons I want to change it after document load. I have tried various bits of JS but nothing seems to work. Here's the HTML and JS (I have also tried replacing data-toggle="pill" with data-toggle="tab" below and still doesn't work).
<div>
<ul class="nav nav-pills pillstyle">
<li class="active tabstyle"><a data-toggle="pill" href="#apple">Apple</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#banana">Banana</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#pear">Pear</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#orange" >Orange</a></li>
</ul>
</div> <!-- nav pills close -->
<div class="tab-content">
<div id="apple" class="tab-pane fade in active"> `
.... content of tabs.
JS
$(document).ready(function(){
$('#banana').tab('show');
});
or
$(document).ready(function(){
$('#banana').pill('show');
});
You just need to change your jQuery selector to address the a element instead of the tab-pane div.
$(document).ready(function(){
$('a[href="#banana"]').tab('show');
});
If you need, you can find more detailed description about bootstrap tabs in the official documentation.
#Stu Here you go.
HTML:
Assign an ID myTab to UL element.
<ul class="nav nav-pills pillstyle" id="myTab">
JS:
$(function () {
$('#myTab a[href="#banana"]').tab('show');
});
Also refer to Bootstrap documentation on selecting different elements on load here. It will give you better understanding.
http://getbootstrap.com/2.3.2/javascript.html#tabs
Working demo: https://jsfiddle.net/tf9k9j27/
Note: Just to answer your trial and error.
You can activate a tab or pill navigation without writing any JavaScript by simply specifying data-toggle="tab" or data-toggle="pill" on an element. Adding the nav and nav-tabs classes to the tab ul will apply the Bootstrap tab styling. (From bootstrap docs. read more to get better clarity)

How to allow nested accordions but manage top level as accordion (and not collapsable) with materializecss?

I'm using nested accordions with Materializecss. I want to be able to have nested accordions, but to let each level to only have 1 item of the accordion opened (as of data-collapsible='accordion').
I can't get it to work: if I set data-collapsible='accordion' I cannot open nested accordions, and if I set data-collapsible='collapsible', I can open any number of items per accordion.
Any workaround?
Thanks!
If you are managing the inner elements of the collapsibles dinamically, then you need to "initialize" them using a jquery method included in "materialize.js". This is written in the "materializecss" documentation here.
I'll provide a practical example.
Given the next HTML:
...
<ul class="collapsible" data-collapsible="accordion">
<li>
<div class="collapsible-header">
My nested collapsible
</div>
<div class="collapsible-body">
<ul class="nested collapsible" data-collapsible="accordion">
<!-- No data initially -->
</ul>
</div>
</li>
<li>
<div class="collapsible-header">Second</div>
<div class="collapsible-body">
<p>Normal data...</p>
</div>
</li>
</ul>
...
I suppose the problem comes because you are appending data into the ".nested" div, and it's not working as an accordion as expected.
You should then do something like:
// ... Your handler code ...
// ... Data appended into $('.nested')
$('.nested').collapsible({accordion: true});
// ...
The {accordion: true} option is not mandatory, as it will be treated as an accordion by default.
It should work in this case. Good luck.