Vue, invoke method using its name as a string - javascript

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 !

Related

Binding a typescript variable to translate service

I'm trying to have a TypeScript variable bind to the translate service just like binding in the HTML markup, which works fine.
Here's what I've tried so far
ngOnInit() {
this.customTranslateService.get("mainLayout.userProfileDropdown.changeLocale").subscribe((result) => {
this.changeLocaleText = result;
})
this.customTranslateService.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.changeLocaleText = this.customTranslateService.instant("mainLayout.userProfileDropdown.changeLocale");
});
this.userProfileMenuOptions = [
{
text: this.changeLocaleText, itemId: "LocaleSelect"
},
{
text: "Report a bug", itemId: "BugReport"
},
{
text: "Request a feature", itemId: "FeatureRequest"
},
{
text: "Log Out", itemId: "LogOut"
}
];
}
customTranslateService is just a service that wraps TranslateService.
The first subscription works ok, but when I switch languages, the onLangChange does trigger, changing the variable content correctly, but userProfileMenuOptions's reference to changeLocaleText is not binded therefore not updated.
Using a BehaviorSubject can't really be done here as it is typescript code, not html markup that can use the async pipe.
Maybe recreating the userProfileMenuOptions array everytime the language change subscription is called could be an option, although I'm not sure the component that uses the array will like it.
PS: instant will work here because I have an application loader that loads all available languages before the application is available to the user.
Any ideas ?
ngOnInit() {
this.customTranslateService.get("mainLayout.userProfileDropdown.changeLocale").subscribe((result) => {
this.changeLocaleText = result;
})
const getUserPorfileMenuOptions = (changeLocaleText: string) => {
return [
{
text: this.changeLocaleText, itemId: "LocaleSelect"
},
{
text: "Report a bug", itemId: "BugReport"
},
{
text: "Request a feature", itemId: "FeatureRequest"
},
{
text: "Log Out", itemId: "LogOut"
}
];
}
this.customTranslateService.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.changeLocaleText = this.customTranslateService.instant("mainLayout.userProfileDropdown.changeLocale");
this.userProfileMenuOptions = getUserPorfileMenuOptions(this.changeLocaleText);
});
this.userProfileMenuOptions = getUserPorfileMenuOptions(this.changeLocaleText);
}

How can i use my array/object data in the method object to then ref to the spceified link VUEJS

How can i possibly use the "LINK" in the methods object?
I think you can see what im trying to do, but if you don't here it is
With a array/object in VUEJS im trying to get all the data from the object in the Data method to then send the link object as a ref to open a new tab with the already defined link
const Main = new Vue({
el: ".ui",
data: {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fa fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
},
methods: {
openLink: function( event ) {
var vm = this;
window.location = vm.socials.link
}
}
})
I don't see any reason to use a method for this when you can just use a plain old anchor tag.
For example
new Vue({
el: ".ui",
data: {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fab fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
}
})
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<ul class="ui">
<li v-for="social in socials">
<a target="_blank" :href="social.link">
<i :class="social.icon"></i> {{ social.label }}
</a>
</li>
</ul>
you just need to iterate socials data you have defined and throw the link to new tab
export default {
data() {
return {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fa fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
};
},
watch:{
},
computed:{
},
methods: {
openLink() {
this.socials.map(val => window.open(val.link));
}
},
mounted(){
}
};
You need to send a parameter when using the openLink function besides the event parameter because otherwise you won't know which link to open. This parameter could be the actual URL (which would be a little bit redundant) or the index of the object in the array that contains the link.
So the first option would look something like this:
methods: {
openLink: function( event, link ) {
var vm = this;
window.location = link
}
}
and the second one something like this:
methods: {
openLink: function( event, index ) {
var vm = this;
window.location = vm.socials[index].link
}
}

vuejs how to run emit on every array change

I've got an array in child component like this:
arrayChild:
[
{name: 'name1', text: 'text1', buttons: 'false', active: "true"},
{name: 'name2', text: 'text2', buttons: 'false', active: "false"},
...
]
I want to emit the arrayChild on every change in the name, text and active (not buttons change!)
How can I do it?
I created basic function to emit this on button click:
<btn #click="emitParent()">emit my Array</btn>
emitUp() {
this.$emit('offerArray', this.arrayChild)
}
But it emit only on button click. I need to emit this automatically on any change within name, text and active. How can I do it? Should I use some kind of computed?
Vue.js offers watchers! So you can "watch" a property and when a change is made you can do stuff.See below:
export default {
data() {
return {
propertyName: 'valueOfProperty'
}
},
watch: {
propertyName(theNewChangedValue) {
//do stuff here
}
}
}
Also consider a deep watcher,that is useful when working with array of objects
propertyName: {
handler: function(newValue) {
//do stuff when array of object changes
},
deep: true
}

How to expand multiple panels properly?

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.

How to add a class to custom tinymce toolbar menu item?

I've added custom toolbar button for tinymce with image, I want to add a class to that. I've tried of adding class parameter but didnt work for me.
pls advice
tinyMCE.PluginManager.add('subBtn', function(editor, url) {
editor.addButton('subBtn', {
class :'buttonClas',
image: icon-path,
icon: true,
tooltip: tooltip,
onclick: function() {
// Open window
editor.windowManager.open({
title: 'PTION',
body: [
{type: 'textbox',value: subBtin, name: 'title', label: 'subBtn'}
],
onsubmit: function(e) {
}
});
}
});
});
Try "classes" instead of "class" - you can add more than one, divided by spaces.

Categories

Resources