I am working with multiple panels and I added functionality to expandAll/collapseAll panels. My issue is:
Lets say:
1) I expand all panels
2) Now, I collapse ONLY the second panel
3) Now I click on the ExpandAll button (Expecting all panels to be expanded).
Issue: After following the above steps, I can't get the panels to expand.
Here's my code:
PLUNKER
<button (click)="collapseAll()">ExpandAll</button>
<button (click)="expandAll()">CollapseAll</button>
You have two problems with this code:
You have just one property responsible for the collapsed state of every panel. This way it's impossible to control them separately.
You have one-way binding, so when your collapse the panels manually in your view, the model is not updated. I fixed your code and forked the plunker: https://plnkr.co/edit/rfiVj8vEIBDOZ8rIHV5i
The main ideas are:
Have dynamic template to keep your html dry:
<p-panel *ngFor="let panel of panels"
[header]="panel.name"
[toggleable]="true"
[(collapsed)]="panel.isCollapsed" <== notice the "banana-in-a-box" notation [()], which allows two-way binding
[style]="{'margin-bottom':'20px'}">
{{ panel.text }}
</p-panel>
export class AppComponent {
panels = [
{
name: 'Panel 1',
isCollapsed: false,
text: `The story begins as Don Vito Corleone...`
},
{
name: 'Panel 2',
isCollapsed: false,
text: `The story begins as Don Vito Corleone...`
},
{
name: 'Panel 3',
isCollapsed: false,
text: `The story begins as Don Vito Corleone...`
},
{
name: 'Panel 4',
isCollapsed: false,
text: `The story begins as Don Vito Corleone...`
},
] // you can also move this to a separate file and import
constructor() {
}
expandAll(){
this.panels.forEach(panel => {
panel.isCollapsed = false;
});
}
collapseAll(){
this.panels.forEach(panel => {
panel.isCollapsed = true;
});
}
}
UPDATE
If you don't want to extract the text from your panels you don't have to but in that case you won't be able to use *ngFor and you'll have to store the state of each panel in some datastructure of your component.
You can have somthing like this:
panelsCollapsed = [
{
isCollapsed: false,
},
{
isCollapsed: false,
},
{
isCollapsed: false,
},
{
isCollapsed: false,
}, // this can also be just an arry of booleans, I kep it as an object in case you want to add other fields to it
Then in your markup you'd have:
<p-panel header="Panel 1" [toggleable]="true" [collapsed]="panelsCollapsed[0].isCollapsed" [style]="{'margin-bottom':'20px'}">
The story begins as Don Vito Corleone...
</p-panel>
<p-panel header="Panel 2" [toggleable]="true" [collapsed]="panelsCollapsed[1].isCollapsed" [style]="{'margin-bottom':'20px'}">
The story begins as Don Vito Corleone...
</p-panel>
The methods for expanding/collapsing all would stay the same.
Related
I have in my Electron app the submenu with a couple of MenuItems of type 'checkbox':
{
label: 'Search settings',
submenu: [
{
type: 'checkbox',
checked: false,
label: 'Aerodromes',
click: (/* menuItem, currentWindow, event */) => {
console.log('')
console.log('menuItem Aerodromes is clicked')
// TODO: change userPrefs about search sources
}
},
{
type: 'checkbox',
checked: false,
label: 'Heliports and HL',
click: (/* event, focusedWindow, focusedWebContents */) => {
console.log('')
console.log('menuItem Heliports and HL is clicked')
// TODO: change userPrefs about search sources
}
},
{
type: 'checkbox',
checked: false,
label: 'LP',
click: (/* event, focusedWindow, focusedWebContents */) => {
console.log('')
console.log('menuItem LP is clicked')
// TODO: change userPrefs about search sources
}
},
...
]
},
When I click on every of these items, it's state 'checked/unchecked' updates correctly, but I can see the changes only after reopening this submenu again because immediately after clicking on any item the whole submenu gets closed (hidden). This is slightly not what I want. I would prefer to give the user the ability activate/inactivate (eg check/uncheck) all these checkbox-items inside the whole submenu until he decides he is done. How can I achieve this behaviour? Any good advice will be appreciated.
P.S.: I've played a lot with params of 'click' methods but without success ('event.preventDefault is not a function' etc)
In a navbar I have to put a list of buttons. While some are links, others should call a method. (e.g. Home should link to /home and Log out should call a function logOut). Because of the large number of items, it is better to have an array with all the buttons/elements that should be rendered
items: [
{ divider: true },
{ icon: "tasks", text: "Today", link: "/tasks" },
{ icon: "sticky-note", text: "Notes", link: "/notes" },
{ divider: true },
{ heading: "Label" },
{ icon: "code-branch", text: "Branch" },
{ divider: true },
{ icon: "user", text: "Account", link: "/profile" },
{ icon: "cog", text: "Settings" },
{ icon: "sign-out-alt", text: "Log out", action: "logOut" }
]
However, I am not sure how to call a method using its name(the last action in the array above).
I saw that in js I could use window[item.action]. Is there something similar in Vue? In other words something like #click="methods[item.action]"
You can create a function to receive the method name and parameters and call the method in this function. You can see in this example
handleFunctionCall(functionName, event) {
this[functionName](event)
},
<button v-else-if="item.action" #click="handleFunctionCall(item.action, $event)" >{{item.text}}</button>
VueJS is still javascript so yes calling the method by its name should work, although I would suggest to use this[*method_name_here*](); to call the function (In the Vue controller I'm not sure if the window would work).
But as you have an action in your list, have you thought of something like this in your view :
<ul>
<router-link v-for="item in items" to="item.link" #click="item.action">
{{item.text}}
</router-link>
</ul>
I'm using router-link directly here but you can wrap in <li> tags
Hope this helps !
I'm using the video.js 4.12 library and I want replace control bar items. For example, move one of my custom buttons to the 2nd slot of the control bar.
How do I change the order of items on the taskbar? I had no luck on Google.
Videojs place good class on elements. By this way you can identify control bar's elements.
To handle the item's order I used Jquery :
var createPrevButton = function() {
var props = {
className: 'vjs-control player-prev-button', //We use this class in Jquery
innerHTML: '<div class="vjs-control-content"></div>',
role: 'button',
'aria-live': 'polite',
tabIndex: 0
};
return videojs.Component.prototype.createEl(null, props);
};
var myPlayer = me.player = videojs(me.idVideo, {
plugins : { chapters : {} },
children: {
controlBar: {
children: [
{
name: 'playToggle'
},
{
name: 'currentTimeDisplay'
},
{
name: 'timeDivider'
},
{
name: 'durationDisplay'
}
/*
...........
*/
]
}
}
});
$(".player-prev-button").insertAfter(".vjs-play-control");
$(".player-next-button").insertAfter(".player-prev-button");
After the instanciation of my player just handle item by Jquery.
I think it's better than use CSS.
But the best way should be by videojs's option or somethink like that
I am working on a sencha touch mobile app, and I ham having a problem. I will try to include as much details as possible.
I have a TabPanel containing 2 items: home view and contact view.
Ext.define('myapp.view.Main', {
extend: 'Ext.TabPanel',
requires: [
'myapp.view.Home',
'myapp.view.Contacts'
],
config: {
fullscreen : true,
tabBar: {
docked: 'bottom',
defaults: {
flex: 1
},
layout: {
pack: 'center'
}
},
layout: {
type: 'card',
animation: {
type: 'slide'
}
},
items: [
{ xtype: 'homeView' },
{ xtype: 'contactsView' }
]
}
});
I want to have the same navigation view for each item. The navigation view has a button that push another view.
For that I have created a BaseNavigationView.js
Ext.define('myapp.view.BaseNavigationView', {
extend : 'Ext.navigation.View',
xtype : 'baseNavigationView',
config : {
navigationBar : {
items : [ {
xtype : 'button',
text : 'My Info',
align : 'right',
id : 'myInfoButton',
ui : 'small'
}
]
},
}
});
And in the 2 views (Home and Contacts) I use
extend: 'myApp.view.BaseNavigationView'
In order to have the navigation view for both.
Until now everything is working fine.
Now for the controller, I want to add a tap event on the MyInfo button and push the myInfoView when tapped in the corresponding view.
In the controller I added to the Control section:
'#myInfoButton' : {
tap : 'onMyInfoButton'
}
and the handler:
onMyInfoButton: function(self, e, eOpts) {
//Create a new instance of the
if (!this.myInfoView) {
this.myInfoView= Ext.create('myApp.view.myInfoView');
}
//Get the parent navigation view and push the view
self.up('baseNavigationView').push(this.myInfoView);
}
When I run the application, Both the Home and Contact View are correctly showing the navigation view with the 'myInfoButton'.
The problem
When I click on the myInfoButton from the "Home" view, the "myInfoView" is pushed correctly with the back button.
However if I switch to the "Contacts" Tab and click on the myInfoButton , The "myInfoView" is pushed is Pushed in the "Home" tab and not the "Contacts" tab. so in the contacts tab nothing happens and I have to go back to the Home tab to see the pushed view.
So it is like self.up('baseNavigationView') is always referencing the navigation view of the Home and not the contact even when the button in the contact view is clicked.
How can I solve this issue?
Thanks a lot for any help.
I'm going to ask another newbie question. I have come across multiple ways for a child to reference functions and data defined at the parent class level, but I'm not sure what is the recommended way. Below is an example that I am dealing with, but this topic is generally important for me since I do not have a good understanding of reference and scope of parents and children. How can I reference functions and data of a parent from a child of a child element?
As usual, any help will be highly appreciated.
Mohammad
San Jose, CA
/******
This is a floating panel with a formpanel inside it, that has an email field and a button to process the email.
When the button is tapped, I want to call the processEmails() function from the button.
******/
Ext.define('myapp.view.ForwardPanel', {
extend : 'Ext.Panel',
xtype : 'forwardPanel',
initialize: function() {
this.callParent(arguments);
var btn = {
xtype: 'button',
ui: 'action',
text: 'Send',
listeners: {
tap: function(){
processEmails(); //<- **How can I reference and call this function?**
// This function is defined at the parent class level
// (indicated at the bottom of the code listing)
}}
};
var form = {
items: [{
xtype: 'fieldset',
instructions: 'Enter multiple emails separated by commas',
title: 'Forward this message.',
items: [ {
xtype: 'emailfield',
name: 'email',
label: 'Email(s)'
},btn] // The button is added to the form here
}]
};
this.add(form);
},
// this is the parent level function I want to call upon button tap.
processEmails: function(){
console.log('Processing email...');
}
});
I'm not sure about a "right" way to do it, but this is what I would do, and what I see most of the time in the Sencha forum and in their own code:
Ext.define('myapp.view.ForwardPanel', {
...
initialize: function() {
this.callParent(arguments);
var me = this; // <---- reference to the "ForwardPanel" instance
var btn = {
xtype: 'button',
ui: 'action',
text: 'Send',
listeners: {
tap: function(){
me.processEmails(); // <-- notice the "me" in front
}
}
};
...
},
// this is the parent level function I want to call upon button tap.
processEmails: function(){
console.log('Processing email...');
}
});
You can make use of prototype to achieve it:
myapp.view.MyView.prototype.processEmails.call(this);
Here is a working fiddle: http://www.senchafiddle.com/#MvABI