I have this Materialize DropDown
<ul id="dropdown1" className="dropdown-content">
<li>
<a href="#!">
one
</a>
</li>
<li>
two
</li>
<li className="divider" tabIndex="-1"></li>
<li>
three
</li>
<li>
<a href="#!">
<i className="material-icons">view_module</i>four
</a>
</li>
<li>
<a href="#!">
<i className="material-icons">cloud</i>five
</a>
</li>
</ul>
and I have declared it as follows :-
componentDidMount() {
let dropdowns = document.querySelectorAll('.dropdown-trigger');
let options = {
inDuration: 300,
outDuration: 300,
hover: true, // Activate on hover
coverTrigger: false, // Displays dropdown below the button
};
M.Dropdown.init(dropdowns, options);
var instance = M.Dropdown.getInstance(dropdowns);
}
which displays fine, however I cannot manage to get the selected value.
I tried the following JQuery:-
$(document).ready(function(){
$('dropdown1').formSelect();
});
but I am getting a type error :-
TypeError: jquery__WEBPACK_IMPORTED_MODULE_6___default(...)(...).formSelect is not a function
Any help will be very much appreciated!
Thanks
I think one should use <Select> in case of a form to select from a list of options. So, you can initialize the Materialize components in componentDidMount() lifecycle method.
I attached onChange event listener on <Select> so that we can track the value and save it in our state. I also gave the defaultValue property so that a person knows in form what he has to choose, for example, Choose a country.
CodeSandbox - Working Demo (Select in React)
Select Component
import React, { Component } from "react";
import M from "materialize-css";
import "materialize-css/dist/css/materialize.min.css";
class Select extends Component {
constructor() {
super();
this.state = {
selectVal: null
};
}
componentDidMount() {
M.FormSelect.init(this.FormSelect);
}
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
console.log(this.state.selectVal);
return (
<div className="input-field col s12">
<select
ref={FormSelect => {
this.FormSelect = FormSelect;
}}
name="selectVal"
onChange={this.handleChange}
defaultValue="0"
>
<option value="0" disabled>
Choose your option
</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
<label>Materialize Select</label>
</div>
);
}
}
export default Select;
Related
I am working on a to do list using React and Typescript. I am trying to return a different colored div when selecting a priority in the option select dropdown (prioritySelect). I have tucked in a function within the ToDoItem JSX Element. What I want to happen is for the prioritySelect component to change the background color of the div it is in "to-do-item" to signify importance.
I tried to add handleOptionsChange function and call it within the div return at the bottom but I keep running into errors when trying to add handleOptionsChange, I am not sure why.
//TODOITEM TO RETURN TO DO COMPONENT
`function ToDoItem(props: { toDo: ToDo; onDeleteToDo: any; prioritySelect: any;}) {`
///TRYING TO CREATE FUNCTION EXPRESSION THAT CALLS EVENT SET STATE FOR PRIORITY BELOW
`handleOptionsChange:any; setState:any;}) {
handleOptionsChange = (event: any) => {
props.setState({
option: event.target.value
});
}`
`return (
<div className="to-do-item" id="to-do-item">
<div className="checkbox-title-container">
<div className="check-title-div">
{Checkbox()}
<h2 className="to-do-title">{props.toDo.title}</h2>
</div>
<div id="delete-div">`
//PRIORITY OPTION SELECT BELOW
`<select name="Priority" className="select-field" value={props.prioritySelect.option} onChange={props.handleOptionsChange}>
<option className="important" value='1'>Important</option>
<option selected value='2'>Normal</option>
</select>
<button id="delete" onClick={props.onDeleteToDo}>
Delete
</button>
</div>
</div>
<div className="description-box">
<span className="description">{props.toDo.description}</span>
</div>
<br />
<span className="to-do-date">
{/* {props.toDo.duedate.toLocaleDateString()} */}
</span>
</div>
);
}`
I am trying to call handleOptionsChange when returning a JSX component below, I have taken out the rest of the components within the to-do-item div for the sake of readability
`<div className="to-do-item" id="to-do-item">
{prioritySelect={handleOptionsChange}} </div>
<div>`
I'm trying to filter certain films based on selections from the below dropdowns.
<select id="filmDropdown">
<option value="0">All Films</option>
<option value="1">Film 1</option>
<option value="2">Film 2</option>
<option value="3">Film 3</option>
</select>
<select id="dateDropdown">
<option value="All">All Dates</option>
<option value="Mon">Mon</option>
<option value="Tues">Tues</option>
<option value="Wed">Wed</option>
<option value="Thurs">Thurs</option>
<option value="Fri">Fri</option>
<option value="Sat">Sat</option>
<option value="Sun">Sun</option>
</select>
<input type="button" value="Search" onclick="getSelectedValues()" />
And here's the html for the films to be selected.
<div id="allFilms">
<div id="1" class="filmTag">
Film 1
<p>
Mon
</p>
<ul>
<li>12:00</li>
<li>16:00</li>
</ul>
<p>
Wed
</p>
<ul>
<li>19:00</li>
</ul>
</div>
<div id="2" class="filmTag">
Film 2
<p>
Tues
</p>
<ul>
<li>12:00</li>
<li>16:00</li>
<li>18:00</li>
</ul>
</div>
<div id="3" class="filmTag">
Film 3
<p>
Wed
</p>
<ul>
<li>12:00</li>
<li>16:00</li>
</ul>
</div>
</div>
I'm trying to use Javascript to hide all the films other than the one selected. This is what I'm doing but it doesn't seem to pick up the selected film and will just hide everything.
<script>
function getSelectedValues() {
var f = document.getElementById("filmDropdown");
var selectedFilm = f.value;
if (selectedFilm !== 0) {
var filmClass = document.getElementsByClassName("filmTag");
for (var i = 0; i < filmClass.length; i++) {
if (filmClass.id === selectedFilm) {
filmClass[i].style.display = "block";
}
else {
filmClass[i].style.display = "none";
}
}
}
}
</script>
If you are developing your own application you might want to render the html based on a structure, like React does, for example. So you have a state where you can also represent your filters. Doing the other way around using selectors is not convenient and very confusing, you might incur in race conditions too, it doesn't worth it.
If you are doing it as external script in a page you didn't develop, something like a Grease Monkey script, then it's a different story. But if you can control how your page is rendered, you might want to change your approach.
For a quick and dirty fix, this line:
if (filmClass.id === selectedFilm) {
needs to be:
if (filmClass[i].id === selectedFilm) {
It's generally not a good idea to use inline event handlers
Here's an alternative using event delegation css selectors and data attributes to filter the elements. Dropped the button and applied a change handler and simplified html.
document.addEventListener("change", showSelected);
function showSelected(evt) {
if (/dropdown/i.test(evt.target.id)) {
const selectValues = {
films: document.querySelector("#filmDropdown").value,
weekday: document.querySelector("#dateDropdown").value
};
// (re)show all initially
document.querySelectorAll("[data-films], [data-weekday]")
.forEach(elem => elem.classList.remove("hide"));
// nothing to do (selected all/all)
if (selectValues.films < 1 && selectValues.weekday === "All") {
return true
}
// css queryselectors based on values
// i.e. all elements *not* in current selection
const films = selectValues.films !== "0" ?
`[data-films]:not([data-films='Film ${selectValues.films}'])` : '';
const weekdays = selectValues.weekday !== "All" ?
`${films ? `, ` : ''}[data-weekday]:not([data-weekday='${
selectValues.weekday}'])` : '';
//hide all not selected elements
document.querySelectorAll(`${films}${weekdays}`)
.forEach(elem => elem.classList.add("hide"));
}
}
body {
font: normal 12px/15px verdana, arial;
margin: 2rem;
}
.hide {
display: none;
}
ul[data-films] {
margin-left: -1rem;
}
li[data-weekday]:before {
content: attr(data-weekday)' ';
}
ul[data-films]:before {
content: attr(data-films);
position: relative;
background-color: green;
color: white;
padding: 1px 3px;
margin-left: -1rem;
margin-bottom: 1rem;
bottom: 4px;
font-weight: bold;
}
<select id="filmDropdown">
<option value="0">All Films</option>
<option value="1">Film 1</option>
<option value="2">Film 2</option>
<option value="3">Film 3</option>
</select>
<select id="dateDropdown">
<option value="All">All Days</option>
<option value="Mon">Monday</option>
<option value="Tues">Tuesday</option>
<option value="Wed">Wednesday</option>
<option value="Thurs">Thursday</option>
<option value="Fri">Friday</option>
<option value="Sat">Saturday</option>
<option value="Sun">Sunday</option>
</select>
<ul data-films="Film 1">
<li data-weekday="Sun">14:00</li>
<li data-weekday="Sun">16:00</li>
<li data-weekday="Sun">20:00</li>
<li data-weekday="Mon">16:00</li>
<li data-weekday="Mon">12:00</li>
<li data-weekday="Mon">16:00</li>
</ul>
<ul data-films="Film 2">
<li data-weekday="Tues">12:00</li>
<li data-weekday="Tues">16:00</li>
<li data-weekday="Wed">12:00</li>
<li data-weekday="Wed">16:00</li>
<li data-weekday="Sun">18:00</li>
<li data-weekday="Sun">20:00</li>
</ul>
<ul data-films="Film 3">
<li data-weekday="Tues">10:00</li>
<li data-weekday="Tues">14:00</li>
<li data-weekday="Wed">12:00</li>
<li data-weekday="Wed">16:00</li>
<li data-weekday="Fri">12:00</li>
<li data-weekday="Fri">16:00</li>
<li data-weekday="Sat">10:00</li>
<li data-weekday="Sat">16:00</li>
<li data-weekday="Sat">17:00</li>
</ul>
What I have
I'm importing the same component twice. This component is used to display a dropdown with collections.
When an item in the dropdown is selected, an event is triggered.
The component is imported in list.js and in BulkActions.vue.
The problem
An event is fired when a collection in the dropdown is selected. It then triggers an event using $emit. Somehow this event is only catched in list.blade.php and not in BulkActions.vue.
For both dropdowns (loading from the same component) there should be a different behaviour.
I have no idea why this happens or why the event is only catched at my root.
What I've tried
I've tried to pass an additional prop in the HTML to have a "variable event name", but that didn't work. I've tried various ways of importing the component as well.
Does anyone know how to solve this issue?
The files
list.blade.php:
<div class="media-list-navbar mt-3 mb-3">
<shown-results :text="resultText"></shown-results>
<search-bar #update-filters="updateFilters"></search-bar>
<document-types #update-filters="updateFilters"></document-types>
<collection-dropdown eventname="update-filters"
#update-filters="updateFilters"></collection-dropdown>
<div class="clearfix"></div>
</div>
<bulk-actions #select-all="selectAll"
#deselect-all="deselectAll"
:items="items"
:multiselect="multiSelect"></bulk-actions>
BulkActions.vue
<template>
<div class="multiselect-list-navbar mt-3 mb-3" v-if="multiselect">
<div class="float-left">
<button type="button"
class="btn btn-outline-secondary"
#click="$emit('deselect-all')">
<i class="fas fa-fw fa-times"></i> {{ Lang.get('media/item.index.list.multi-select.deselect-all') }}
</button>
<button type="button"
class="btn btn-outline-secondary"
#click="$emit('select-all')">
<i class="fas fa-fw fa-check"></i> {{ Lang.get('media/item.index.list.multi-select.select-all') }}
</button>
</div>
<bulk-collection
#update-filters="doSomething"></bulk-collection>
<div class="clearfix"></div>
</div>
</template>
<script>
export default {
name: "Bulk",
props: {
multiselect: Boolean,
items: Array
},
components: {
'bulk-collection': () => import('./Collections')
},
methods: {
doSomething() {
console.log(this.items)
}
}
}
</script>
<style scoped>
</style>
list.js
import MediaItem from '../components/MediaItem';
import UploadModal from '../components/media/UploadModal';
import ItemDetail from '../components/media/ItemDetail';
import ShownResults from '../components/media/list/ShownResults';
import SearchBar from '../components/media/list/SearchBar';
import DocumentTypes from '../components/media/list/DocumentTypes';
import {default as CollectionDropdown} from '../components/media/list/Collections';
import Order from '../components/media/list/Order';
import BulkActions from '../components/media/list/BulkActions';
if (document.getElementById('media-list')) {
const mediaList = new Vue({
el: '#media-list',
components: {
MediaItem,
ShownResults,
SearchBar,
UploadModal,
ItemDetail,
DocumentTypes,
CollectionDropdown,
Order,
BulkActions
},
[...]
Collections.vue
<template>
<div class="dropdown float-left">
<button class="btn btn-secondary dropdown-toggle"
type="button"
data-toggle="dropdown">
{{ Lang.get('media/item.index.list.filters.collections.title') }}
</button>
<div class="dropdown-menu" ref="collectionDropdown">
<div class="dropdown-item no-pseudo">
<input type="search"
class="form-control"
name="search"
:placeholder="Lang.get('media/item.index.list.filters.collections.filter')"
v-model="query"
#keyup="search">
</div>
<div class="dropdown-item last-item no-pseudo">
<alert type="warning">
<template v-slot:body>
{{ Lang.get('media/item.index.list.filters.collections.none-filter') }}
</template>
</alert>
</div>
<div v-for="item in list"
class="dropdown-item"
v-if="!item.hidden">
<span class="custom-control custom-checkbox">
<input type="checkbox"
class="custom-control-input"
name="collection[]"
:checked="item.checked"
:id="item.slug"
:value="item.id"
#change="selectItem">
<label class="custom-control-label" :for="item.slug">
{{ item.name }}
</label>
</span>
</div>
</div>
</div>
</template>
<script>
import Alert from '../../partials/Alert';
export default {
name: "Collections",
components: {
Alert
},
data() {
return {
displayAmount: 10, // amount of items displayed without search
list: [],
query: ''
}
},
computed: {
/**
* Return an array of selected items only
*/
checked() {
return this.list.filter(item => {
return item.checked === true;
})
}
},
methods: {
/**
* Mark an item as selected
*/
selectItem(e) {
let selectedId = e.target.value;
this.markItem(selectedId);
},
/**
* Mark an item from the list as selected
*
* #param {Number} itemId
*/
markItem(itemId) {
this.list.forEach(item => {
if (item.id === parseInt(itemId)) {
item.checked = !item.checked;
}
});
this.$emit('update-filters', {
props: this.checked.map(item => item.id).join(',')
});
},
/**
* Search in the current URL for collection ids
*/
markItemsFromUrl() {
let urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('collection')) {
let urlFilters = urlParams.get('collection').split(','); // split url parameters
urlFilters.forEach(itemId => {
this.markItem(itemId);
});
}
},
},
mounted() {
this.fetchList();
this.markItemsFromUrl();
/**
* Prevent Bootstrap dropdown from closing after clicking inside it
*/
$(this.$refs.collectionDropdown).on('click.bs.dropdown', function (e) {
e.stopPropagation();
});
}
}
</script>
Below is a GIF animation to demonstrate the problem. The first dropdown (the one on the top) has normal behaviour. The second one (that appears later on) does not. When clicking the second one, an event of the first one is happening.
I want to dynamically set a value within my cart constructor. My cart variable has price, item. and size values. I want to assign a value to item dynamically on what item has been clicked. This is my code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addCart } from '../../actions';
import { withRouter } from 'react-router';
//import { Cart } from './webcart';
import CamoHat from './camodye.jpeg';
import TonyTape from './TonyTape.png';
import RegularHat from './regularhat.jpg';
import JaguarShirt from './JAGUARLOGO.png';
import SeltzShirt from './seltzshirt.jpg';
import NocHoodie from './NOCNOCHOODIE.png';
export class WebShop extends Component {
constructor(props){
super(props);
this.state = {value: 'medium', cartData: {} };
this.handleClick = this.handleClick.bind(this);
this.change = this.change.bind(this);
this.goProductPage = this.goProductPage.bind(this);
this.goProductPage2 = this.goProductPage2.bind(this);
this.goProductPage3 = this.goProductPage3.bind(this);
}
handleClick() {
let cart = {price:0,item:"userselection",size:this.state.value};
this.props.onCartAdd(cart);
this.itemSelection(cart);
}
change(e){
this.setState({value: e.target.value})
}
itemSelection(cart){
let userItem = cart;
let userSelection = cart
if (userItem == "shirt1") {
let itemPrice = 20.00;
}
}
...
render() {
return (
<div className='Webshop' id='Webshop'>
<ul id="Productlist">
<div className='Product'>
<img src={CamoHat} onClick={this.goProductPage}></img>
<div id='infoname'>NOC NOC HAT (CAMO)</div>
<div id='infoprice'>40.00</div>
</div>
<div className='Product'>
<img src={JaguarShirt} onClick={this.goProductPage2}></img>
<div id='infoname'>JAGUAR PYRAMIDS T-SHIRT</div>
<div id='infoprice'>25.00</div>
</div>
<div className='Product'>
<img src={SeltzShirt} onClick={this.goProductPage3}></img>
<div id='infoname'>JAGUAR PYRAMIDS T-SHIRT</div>
<div id='infoprice'>25.00</div>
</div>
<div className='Product'>
<img src={NocHoodie} onClick={this.goProductPage}></img>
<div id='infoname'>JAGUAR PYRAMIDS T-SHIRT</div>
<div id='infoprice'>25.00</div>
</div>
<div className='Product'>
<img src={RegularHat}></img>
<button onClick={this.handleClick} className="addit">Add to cart</button>
<select id="size" onChange={this.change} value={this.state.value}>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="x-large">X-large</option>
</select>
</div>
<div className='Product'>
<img src={TonyTape}></img>
<button onClick={this.handleClick} className="addit">Add to cart</button>
<select id="size" onChange={this.change} value={this.state.value}>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="x-large">X-large</option>
</select>
</div>
</ul>
</div>
);
}
}
...
export default connect(mapStateToProps,mapDispatchToProps)(WebShop);
I want to assign the item value in cart using the description of the item within the item div id=infoname (the text itself) for ex: NOC NOC Hat. How can I do this within my function? Is there a better way to do it?
The quickest way to do what you want to do is to use refs :
https://facebook.github.io/react/docs/refs-and-the-dom.html
Quote from the official doc :
The ref attribute takes a callback function, and the callback will be
executed immediately after the component is mounted or unmounted.
When the ref attribute is used on an HTML element, the ref callback
receives the underlying DOM element as its argument
I have removed some lines from your code so that it compiles for me.
Here it is :
https://codesandbox.io/s/314N8jxG9
Look at line 22 and 46. (check the console.log output)
The ref attributes receives a callback that can access the div element, thus it innerHTML value.
This is not the best way to do it but this will fix your issue.
Now the best is to use props.
Please, refactor your code by first extracting your products items into ProductIem this way :
const ProductItdem = ({description, price})=>{
return (
<div className='Product'>
<div id='infoname' >{description}</div>
<div id='infoprice'>{price}</div>
</div>
);
}
You can even compose them in a ProductList Component.
Then you will pass props from you WebShop container component to the dumm components.
Try it and let us know your questions.
I'm trying to get the bootstrap multiselect widget to work. It works when I hardcode all the options, like this:
<select id="topic-select" multiple="multiple">
<option val='math'>math</option>
<option val='critical_reading'>critical reading</option>
<option val='writing'>writing</option>
</select>
$("#topic-select").multiselect({
includeSelectAllOption: true,
selectAllText: 'composite score',
allSelectedText: 'composite score',
selectAllNumber: false,
});
but if I try to populate the options with angular, like this:
<select id="topic-select" multiple="multiple" ng-option="topic in topicList">
</select>
then the dropdown window kindof bugs out, and doesn't show any of the options.
If I remove the javascript turning it into a multiselect, then it DOES show all the options.
I took a look at this similar question:
angularjs-ng-repeat-in-bootstrap-multiselect-dropdown
but couldn't didn't have any luck with it.
you don't really require Bootstrap multi-select if you're going for its functionality. You can get the same functionality in Angular, by populating your options in a dropdown, and adding them to a new list on ng-click.
<span uib-dropdown on-toggle="toggled(open)" auto-close = "outsideClick" >
<a class = "filter-names" href id="simple-dropdown" uib-dropdown-toggle>
My_Dropdown<span ng-repeat="list in generated_list">{{list.genre_name}},</span><b class="caret"></b>
</a>
<ul class="dropdown-menu" uib-dropdown-menu aria-labelledby="simple-dropdown" >
Search: <input type = "text" placeholder = "search in my list" ng-model = "search.name" />
<li ng-repeat="item in list_to_populate | filter:search" ng-class = "{true: 'filter-selected', false: ''}[item.selected == true]" ng-click="addToFilter(item)">
{{item.name}}
</li>
</ul>
</span>
And in the controller:
$scope.addToFilter = function(item) {
if(item.selected == "undefined" || item.selected == false)
{
item.selected = true;
Filters.addToFilters(item);
}
else
{
Filters.removeFromFilters(item);
item.selected = false;
}
}
And finally have a service "Filters" to store this list and call functions to use it anywhere.
You are missing "ng-model".
It is "ng-options" and not "ng-option".
Try this:
<select id="topic-select" multiple ng-model="selectedTopics" ng-options="topic as topic.name for topic in topicList">
</select>
Instead of populating the options with angular, I just add them to the div with vanilla javascript like this:
var topicSelect = $("#topic-select");
for (var topicId in topicList) {
topicSelect[0].add(new Option(topicList[topicId], topicId));
}
and everything works now.