Passing data from Component to parent - javascript

I have created a simple component that displays a SELECT tag. On making a selection, I want to pass the selected value to parent (Vue). This is the component code:
// Period Selection
Vue.component ('period-component', {
props: [],
data () {
return {
periods: [
{ text: '-- Select --', value: ''},
{ text: 'Today', value: 'Today'},
{ text: 'Up to Last 7 Days', value: '7D'},
{ text: 'Up to Last 30 Days', value: '30D'},
{ text: 'Up to 3 Months', value: '3M'},
{ text: 'Up to 6 Months', value: '6M'},
{ text: 'Up to 1 Year', value: '1Y'},
{ text: 'All', value: '1Y'},
],
selectedPeriod:false,
}
},
methods: {
periodChanged() {
console.log('In periodChanged.' ,this.selectedPeriod);
this.$emit('periodChangedEvent', this.selectedPeriod);
},
},
template: `<div>
<select
v-model="selectedPeriod"
v-on:change="periodChanged()">
<option
v-for="period in periods"
:value="period.value">{{ period.text }}
</option>
</select>
</div> `
})
I use the component in the folloing way:
<period-component
v-on:periodChangedEvent="selectedPeriodChanged($event)" >
</period-component>
In the vue object, I have the selectedPeriodChanged() method like this.
selectedPeriodChanged: function(value){
console.log('in periodChanged' );
},
When I make selection in , the periodChanged() method fires and I get the selected value in component's selectedPeriod. However, I am unable to emit the event to the parent. (Parent's selectedPeriodChanged() never fires) I don't see any errors. What's wrong in my code? Thanks.

Because HTML attributes are case insensitive, the event name should be kebab-case in the template instead of camelCase. See: https://v2.vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case
<period-component v-on:period-change-event="selectedPeriodChanged($event)"></period-component>
this.$emit('period-change-event', this.selectedPeriod);
Here is a working fiddle

Related

VueGoodTable filter dropdown options vue2

I'm trying to populate possible dropdown options on vue good table. The idea is that I conduct an API call to the server to bring back what values can possibly go into the drop down and I'm trying to assign it to the column filter. However, I can't seem to get it to work.
<vue-good-table
:paginationOptions="paginationOptions"
:sort-options="sortOptions"
:isLoading.sync="isTableLoading"
:rows="rows"
:columns="columns"
:lineNumbers="true"
mode="remote"
:totalRows="totalRecords"
#on-row-click="onRowClick"
#on-page-change="onPageChange"
#on-sort-change="onSortChange"
#on-column-filter="onColumnFilter"
#on-per-page-change="onPerPageChange"
#on-search="onSearch"
:search-options="{
enabled: true
}"
styleClass="vgt-table striped bordered"
ref="table"
>
Fairly standard vgt set up.
columns: [
{
label: 'some column',
field: 'column1'
},
{
label: 'Customer',
field: 'customerName',
filterOptions: {
enabled: true,
placeholder: 'All',
filterDropdownItems: Object.values(this.customers)
}
},
{
label: 'other columns',
field: 'column234'
}
]
and the API call
methods: {
async load () {
await this.getTableOptions()
},
async getTableOptions () {
try {
var response = await axios.get(APICallUrl)
this.customers= []
for (var i = 0; i < response.data.customers.length; i++) {
this.customers.push({ value: response.data.customers[i].customerId, text: response.data.customers[i].customerName})
}
} catch (e) {
console.log('e', e)
}
}
The only thing that I thought of is that the table has finished rendering before the load method is complete. However just creating a static object in my data and assigning it to a filterDropDownItems yielded no better results. The result whenever I try to set it to an object is that the box is a type-in box rather than a dropdown.
You can make the table update after it's rendered by making columns a computed property. The other problem you have is this.customers is an Array but Object.values() expects an Object. You could use the Array.map function instead
this.customers.map(c => c.value)
Although according to the VueGoodTable docs an array of objects like you have should work just fine
computed: {
columns() {
return [
{
label: 'some column',
field: 'column1'
},
{
label: 'Customer',
field: 'customerName',
filterOptions: {
enabled: true,
placeholder: 'All',
filterDropdownItems: this.customers
}
},
{
label: 'other columns',
field: 'column234'
}
];
}
}

Tooltip disappears after disabling and re-enabling BootstrapVue table column

I have a BootstrapVue table which looks like this;
When you hover your mouse on the First Name column, the tooltip Tooltip for First name will appear. The checkboxes at the top will cause the corresponding table columns to appear/disappear.
Here's the description of the bug I encounter.
I uncheck First Name checkbox. Column First Name disappears. Now, I recheck the First Name checkbox. Column First Name reappears again. This is fine. However, the tooltip no longer works when I hover my mouse on the First Name column.
Here's the complete code in a single HTML file.
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.min.js"></script>
<div id='app'>
<b-checkbox
:disabled="visibleFields.length == 1 && field.visible"
v-for="field in showFields"
:key="field.key"
v-model="field.visible"
inline
>
{{ field.label }}
</b-checkbox>
<b-table :items="items" :fields="visibleFields" bordered>
</b-table>
<b-tooltip target="HeaderFirst" triggers="hover" container="HeaderFirst">
Tooltip for First name<br>
</b-tooltip>
</div>
<script>
new Vue({
el: '#app',
computed: {
visibleFields() {
return this.fields.filter(field => field.visible)
},
showFields() {
return this.fields.filter(field => field.key.includes('first') || field.key.includes('last'))
}
},
data: dataInit,
})
function dataInit() {
let init_data = {};
init_data.fields = [
{ key: 'id', label: 'ID', visible: true },
{ key: 'first', label: 'First Name', visible: true,
thAttr: {
id: "HeaderFirst"
},
},
{ key: 'last', label: 'Last Name', visible: true },
{ key: 'age', label: 'Age', visible: true },
];
init_data.items = [
{ id: 1, first: 'Mike', last: 'Kristensen', age: 16 },
{ id: 2, first: 'Peter', last: 'Madsen', age: 52 },
{ id: 3, first: 'Mads', last: 'Mikkelsen', age: 76 },
{ id: 4, first: 'Mikkel', last: 'Hansen', age: 34 },
];
return init_data;
}
</script>
I am using vue v2.6, BootstrapVue.
The <b-tooltip>'s target must exist in the DOM upon mounting. It does not dynamically attach a new tooltip to newly created elements in the DOM.
The first run of your code shows a tooltip because <b-table> initially contains the #HeaderFirst element. When you uncheck the First Name box, the existing elements in <b-table> are replaced with new elements via the computed property. The elements removed from the DOM include the one that <b-tooltip> initially attached a tooltip to, and no new tooltip is generated after <b-table> was updated.
Solution
One solution is to render <b-tooltip> only when the target element is visible:
Create a computed prop that determines whether the #HeaderFirst field is visible.
Conditionally render <b-tooltip> based on that computed prop.
new Vue({
computed: { 1️⃣
firstNameHeaderVisible() {
return this.fields.find(field => field.thAttr?.id === 'HeaderFirst')?.visible
}
},
⋮
})
2️⃣
<b-tooltip target="HeaderFirst" v-if="firstNameHeaderVisible">
demo
On disabled elements event hover is not working. You need to wrap element by other e.g. div where you set up tooltip and then it should work well

How can I use multiple b-form-radio-group avoiding visual interference among them?

I'm new using Vue and specifically Bootstrap Vue and I'm trying to build a form with multiple radio groups.
My problem is that when I change the value in one of them the others don't change their values (this was checked with Vue DevTools) but visually it looks like none of the values are selected
I don't know what is wrong with my approach
I post here a simplified version of the code looking for some help, thanks in advance:
<template>
<div>
<b-form-group label="Radio group 1" v-slot="{ ariaDescribedby }">
<b-form-radio-group
id="radio-group-1"
v-model="selected1"
:options="options1"
:aria-describedby="ariaDescribedby"
name="radio-options"
></b-form-radio-group>
</b-form-group>
<b-form-group label="Radio Group 2" v-slot="{ ariaDescribedby }">
<b-form-radio-group
id="radio-group-2"
v-model="selected2"
:options="options2"
:aria-describedby="ariaDescribedby"
name="radio-options"
></b-form-radio-group>
</b-form-group>
</div>
</template>
<script>
export default {
data() {
return {
selected1: 'first',
options1: [
{ text: 'First', value: 'first' },
{ text: 'Second', value: 'second' },
],
selected2: 'one',
options2: [
{ text: 'One', value: 'one' },
{ text: 'Two', value: 'two' },
]
}
}
}
</script>
Since both <b-form-radio-group> elements have the same name, "radio-options", visually they are treated like one group. The different v-model would still function correctly but this isn't what you want visually. Give the second group a different name:
<b-form-radio-group
id="radio-group-2"
v-model="selected2"
:options="options2"
:aria-describedby="ariaDescribedby"
name="radio-options2"
></b-form-radio-group>
Here I changed it to radio-options2.

EXTjs getEl() fails before showing the element

Good day all.
In EXTjs, probably 4.x version, I have a menu with a couple of levels of sub-menus
I'd like to add a data attribute to some of those sub-menus and actually I can do this with no problems with:
Ext.getCmp("notificationMenu").getEl().set({"data-notifynumber": 4});
but this is working only for the first element of the menu (to be clear, the element that is shown upon loading.
For any other element of the menu, first of all I have to click the menu to show all the sub-menu and only at that time I can use the getEl() function, otherwise this error is shown:
Uncaught TypeError: Cannot read property 'set' of undefined
I understand that I'd need to... show? render? well, "do something" to those sub elements in order to have them in the dom properly... I attach part of the code:
this is part of the menu I create:
xtype: 'button',
id:"notificationMenu",
hidden: false,
reference: 'userType',
style: 'color: #ffffff;width:58px;height:58px;',
glyph: 0xf0f3,
menu:{
border:0,
menuAlign: 'tr-br?',
bodyStyle: {
background: '#3e4752',
},
items:[
{
text:"TASKS",
disabled:true
},
{
text:"Campaigns",
data_id:"me_campaigns",
glyph:0xf0c1,
id:"notification_me_campaigns_root",
hidden:true,
menu:{
border:0,
menuAlign: 'tr-br?',
bodyStyle: {
background: '#3e4752',
},
items:[
{
text:"Approval",...
in this example, if I make after Render:
Ext.getCmp("notificationMenu").getEl().set({"data-notifynumber": 10})
but if I use
Ext.getCmp("notification_me_campaigns_root").getEl().set({"data-notifynumber": 4})
the error above is shown. please do you have some advice? may I call a "force render" somehow?
Try to use afterrender to get dom element.
ExtJs getEl() Retrieves the top level element representing this component but work when dom is prepared without dom creation it will return null.
I have created an Sencha Fiddle demo hope this will help you to achieve you requirement/solution.
var panel = new Ext.panel.Panel({
renderTo: document.body,
title: 'A Panel',
width: 200,
tools: [{
xtype: 'button',
text: 'Foo',
menu: {
defaults: {
handler: function () {
Ext.Msg.alert('Successs', 'You have click on <b>data-notifynumber</> ' + this.up('menu').getEl().getAttribute('data-notifynumber'))
}
},
items: [{
text: 'Item 1'
}, {
text: 'Item 2',
menu: {
listeners: {
afterrender: function () {
this.getEl().set({
"data-notifynumber": 20//only for example you can put as basis of your requirement
});
}
},
defaults: {
handler: function () {
Ext.Msg.alert('Successs', 'You have click on <b>data-notifynumber</> ' + this.up('menu').getEl().getAttribute('data-notifynumber'))
}
},
items: [{
text: 'Sub Item 1',
}, {
text: 'Sub Item 2'
}]
}
}],
listeners: {
afterrender: function () {
this.getEl().set({
"data-notifynumber": 10//only for example you can put as basis of your requirement
});
}
}
}
}]
});

Angular-Formly dynamically change select options

I would like to know if I could change select options using Angular-Formly depending on another select's selected value.
Lets say I have beverageTypeSelect and drinkSelect.
beverageTypeSelect has options Alcoholic and Non-Alcoholic.
drinkSelect would have Heineken, Budweiser, Alcohol 96%, if beverageTypeSelect value is Alcoholic.
drinkSelect would have Coca-Cola, Pepsi, Water, if beverageTypeSelect value is Non-Alcoholic.
How can I do that?
Editing:
Ok, let's try to make it a little more clear:
This is not my real application, but I want to keep it simple and get the general solution for this, so I'll keep with the beverage.
We are using https://github.com/formly-js/angular-formly-templates-bootstrap.
We have a formly forms generator. The part of code that is interesting in here is this:
if (obj.ngOptions){
o.templateOptions.options=[];
o.templateOptions.ngOptions = obj.ngOptions;
}
We have figured out that options must by an empty array so ngOptions can take place.
So we have a formly form like this:
[{
key: 'beverageTypeSelect',
type: 'select',
templateOptions: {
options: [{
name: 'Alcoholic',
value: 'A'
},
{
name: 'Non-Alcoholic',
value: 'N'
}]
}
}, {
key: 'drinkSelect',
type: 'select',
templateOptions: {
options: [],
ngOptions: 'item as item.label for item in model.drinkSelect track by item.id'
}]
Then we'll have a html to show our form:
<formly-form fields="formFields" model="DrinkingCtrl.data" form="DrinkingCtrl.form">
</formly-form>
And, finally, our controller:
'use strict'
angular.module('drunkApp')
.controller('DrinkingCtrl', drinking);
function drinking($scope, $stateParams, $state, $reactive, AutoSaveReactiveFormScope) {
var self = this;
var vm = AutoSaveReactiveFormScope($scope, this, 'NAME_TO_SEARCH_FOR_DRINK_FORMLY_FORM', 'drinkPublishName', drinkMongoCollection, $stateParams.id); // This is a factory that gives us everything (or almost it) that we need in a controller with form autosave functionallity :).
var drinkOptions = [{
id: 'H',
label: 'Heineken',
types: ['A']
}, {
id: 'B',
label: 'Budweiser',
types: ['A']
}, {
id: 'A',
label: 'Alcohol 96%',
types: ['A']
}, {
id: 'C',
label: 'Coca-Cola',
types: ['N']
}, {
id: 'P',
label: 'Pepsi',
types: ['N']
}, {
id: 'W',
label: 'Water',
types: ['N']
}];
vm.autorun(function() {
if (self.data) {
self.data.drinkSelect = _.filter(drinkOptions, function(drk) {
return _.contains(drk.types, self.data.beverageTypeSelect);
});
}
});
}
This really works, but I was expecting something cleaner, something that I could do directly inside of the form declaration. Maybe do something like we can do with required or hideExpression (that we can write something like 'model.option1 != "BLA"' or a function).
p.s.: Hope this time I made myself clearer.

Categories

Resources