Scoping issues while using context menu - ag-grid

I'm following the docs here to add a context menu item to my grid. The issue is that from the scope of getContextMenuItems (in the example), I'm unable to access any other methods or variables in my component. Is this possible? Example below:
private varIWantToAccess: boolean = false;
function getContextMenuItems(params) {
var result = [
{ // custom item
name: 'Alert ' + params.value,
action: function ()
{
window.alert('Alerting about ' + params.value);
this.varIWantToAccess = true; // Builds fine, but throws a run time exception, since this "this" context is different than the one that has "varIWantToAccess"
}
},
....
return result;
}
Thanks!

You can add the reference to this in grid's context -
this.gridOptions.context = {
thisComponent : this
};
And then, thisComponent can be access as below -
private getContextMenuItems(params) {
console.log(params);
var result = [
{ // custom item
name: 'Sample',
action: function () {params.context.thisComponent.callMe(); },
icon: '<i class="fa fa-pencil" />'
}];
return result;
}
Same can be done for any other call backs like cellRenderer.

I assume that you are speaking of an Angular 2 or 4 component using TypeScript.
If so then use fat arrow to connect to your function.
Example:
gridOptions.getContextMenuItems = () => this.getContextMenuItems();
This should provide you the scope you need.

You need to provide parent context property to the item.
Sample context menu item:
{
name: 'BreakoutReport',
action: function () {
this.context.isDrillDownData = false;
this.context.isBreakOutReport = true;
this.context.populateBreakOutGrid();
},
cssClasses: ['redFont', 'bold'],
disabled: !params.value.drillDownReport,
context: params.context
}
Here, this.context has access to all the parent functions.
Remember, this context needs to be set in grid options first and then can be transferred to context menu items.
1st step: set context in gridOptions
getGridOption() {
return {
getContextMenuItems: this.getContextMenu,
context: this//pass parent context
};
}
2nd step: pass context to context menu subitems
getContextMenu(params) {
const result = [
{
name: 'Drilldown Report',
action: function () {
this.context.populateDrillDownReport();//access parent context using this.context inside the function.
},
cssClasses: ['redFont', 'bold'],
disabled: !params.value.drillDownReport,
context: params.context//pass parent context
},
'separator',
'copy',
'copyWithHeaders'];
return result;
}

you can just amend your getContextMenuItems
getContextMenuItems = (params) => {
var result = [
{
name: 'Activate ' + params.value,
action: function () {
window.alert('Activated Successfully ');
},
cssClasses: ['redFont', 'bold'],
},
{
name: 'Details',
action: () => {
this.router.navigate(['container-authorization/listing/distributor-container-store/details']);
},
cssClasses: ['redFont', 'bold']
},
}
method to fat arrow method like below.

Related

Rendering a menu in vue 3 after ajax method

I've gotten this menu to work without filtering it, but now I'm doing an ajax request to filter out menu items the user isn't supposed to see, and I'm having some trouble to figure out how to set the resulting menu data, the line that is not working is commented below:
<script>
import { ref } from 'vue';
import axios from 'axios';
var currentSelected = 'device_access';
var menuData = [
{
text: 'Device Access',
id: 'device_access',
children: [
{
text: 'Interactive',
link: '/connection_center'
},{
text: 'Reservation',
link: '/reserve_probe'
}, {
text: 'Reservation Vue',
link: '/reservation.html'
}
]
}, {
text: 'Automation',
id: 'automation',
show: ['is_mxadmin', 'can_schedule_scripts'],
children: [
{
text: 'Builder',
link: '/builder',
},{
text: 'Execution Results',
link: '/test_suite_execution_results'
},
]
}
];
function hasMatch(props, list) {
var match = false;
for (var i=0; i < list.length && !match; i++) {
match = props[list[i]];
}
return match;
}
export default {
name: 'Header',
setup() {
const cursorPosition = ref('0px');
const cursorWidth = ref('0px');
const cursorVisible = ref('visible');
//the menu is zero length until I get the data:
const menu = ref([]);
return {
menu,
cursorPosition,
cursorWidth,
cursorVisible
}
},
created() {
let that = this;
axios.get('navigation_props')
.then(function(res) {
var data = res.data;
var result = [];
menuData.forEach(function(item) {
if (!item.show || hasMatch(data, item.show)) {
var children = [];
item.children.forEach(function (child) {
if (!child.show || hasMatch(data, child.show)) {
children.push({ text: child.text, link: child.link });
}
});
if (children.length > 0) {
result.push({ text: item.text,
children: children, lengthClass: "length_" + children.length });
}
}
});
//continues after comment
this is probably the only thing wrong, I've run this in the debugger and I'm getting the
correct data:
that.$refs.menu = result;
since the menu is not being rebuilt, then this fails:
//this.restoreCursor();
})
.catch(error => {
console.log(error)
// Manage errors if found any
});
},
this.$refs is for template refs, which are not the same as the refs from setup().
And the data fetching in created() should probably be moved to onMounted() in setup(), where the axios.get() callback sets menu.value with the results:
import { onMounted, ref } from 'vue'
export default {
setup() {
const menu = ref([])
onMounted(() => {
axios.get(/*...*/).then(res => {
const results = /* massage res.data */
menu.value = results
})
})
return {
menu
}
}
}
I finally figured out the problem.
This code above will probably work with:
that.menu = result;
You don't need: that.$refs.menu
You can't do it in setup because for some reason "that" is not yet defined.
In my working code I added a new method:
methods: {
setMenuData: function() {
this.menu = filterMenu();
},
}
And "this" is properly defined inside them.

Code migration from tinymce 4 to tinymce 5 - problem with action function (true / false)

I have a problem with migrating the plugin from tinymce 4 to tinymka 5. The console tells me "Uncaught (in promise) TypeError: btn.active is not a function"
I can not find an equivalent for tinymce 5. Can someone replace it?
Code below:
tinymce.PluginManager.add('phonelink', function(editor, url) {
// Add a button that opens a window
var linkText = "";
var linkTitle = "";
var link = "";
// tinymce.DOM.loadCSS(url + '/css/phonelink.css');
editor.ui.registry.addButton('phonelink2', {
text: 'asddas',
icon: 'image-options',
onSetup: updateOnSelect,
onAction: onClickPhoneButton
});
// Adds a menu item to the tools menu
editor.ui.registry.addMenuItem('phonelink', {
text: 'asddas',
icon: 'image-options',
context: 'tools',
onAction: onClickPhoneButton,
onSetup: updateOnSelect
});
function onClickPhoneButton(){
editor.windowManager.open({
title: '123213123',
body: {
type: 'panel',
items: [
{type: 'input', name: 'phone', label: 'Teléfono', value: link},
{type: 'input', name: 'showtext', label: 'Texto a mostrar', value: linkText},
{type: 'input', name: 'title', label: 'Título', value: linkTitle}
]
},
buttons: [
{
text: 'Close',
type: 'cancel',
onclick: 'close'
},
{
type: 'submit',
name: 'submitButton',
text: 'Stwórz',
primary: true
}
],
onAction: function(e) {
alert('Toggle menu item clicked');
},
onSubmit: function(e) {
var data = e.getData();
var hrefLink = '<a title="' + data .title + '" href="tel:+34' + data .phone + '">' + data .showtext + '</a>';
if(link !== ''){
editor.setContent(hrefLink);
}else{
editor.insertContent(hrefLink);
}
e.close();
}
});
}
function updateOnSelect() {
var btn = this;
const editorEventCallback = function (e) {
var node = editor.selection.getNode();
var isTelLink = node.href !== undefined && node.href.indexOf('tel:+') !== -1
btn.active(isTelLink);
if(isTelLink){
link = node.href;
link = link.replace("tel:+34", "");
linkTitle = node.title;
linkText = node.text;
}
};
editor.on('NodeChange', editorEventCallback);
return function (btn) {
editor.off('NodeChange', editorEventCallback);
}
}
});
I searched the documentation for a replacement, but found nothing.
TinyMCE 5 no longer passes the button and menu instance via this. Instead it passes an API instance as the first parameter, so you'll want to change your updateOnSelect function to something like this:
function updateOnSelect(api) {
const editorEventCallback = function (e) {
var node = editor.selection.getNode();
var isTelLink = node.href !== undefined && node.href.indexOf('tel:+') !== -1
api.setActive(isTelLink);
if(isTelLink){
link = node.href;
link = link.replace("tel:+34", "");
linkTitle = node.title;
linkText = node.text;
}
};
editor.on('NodeChange', editorEventCallback);
return function (btn) {
editor.off('NodeChange', editorEventCallback);
}
}
You'll note var btn = this has been removed and that the API to set an item as active is setActive instead of active. This can be found in the documentation here: https://www.tiny.cloud/docs/ui-components/typesoftoolbarbuttons/#togglebutton and https://www.tiny.cloud/docs/ui-components/menuitems/#togglemenuitems (see the API section in both links).
In the above, you may have noticed both reference "Toggle" items. That's another change in TinyMCE 5, as different types of buttons/menu items have a separate registration API. So you'll also need to swap to using editor.ui.registry.addToggleButton and editor.ui.registry.addToggleMenuItem. More details about that can be found here if needed: https://www.tiny.cloud/docs/migration-from-4x/#customtoolbarbuttons
Here's an example fiddle showing the changes mentioned above: https://fiddle.tiny.cloud/B5haab.
Hopefully that helps!

Title and menu items of sap.ui.unified.Menu control

I am trying to add title to a sap.ui.unified.Menu control and it displays only the title; other menu items after this are not displayed.
How to bind the menu items to this, and how to get same view with other data without new menu element being created?
I want this :
I am getting :
My code:
sap.ui.define([], function () {
var dynamicMenu= new sap.ui.unified.Menu.extend("com.google.copa.common.CustomMenu", {
metadata: {
library: "com.google.copa.common",
properties: {
title: {
type: "string" } } },
init : function () { },
renderer:function(oRm,oControl){
oRm.write("<div");
oRm.writeControlData(oControl);
oRm.addClass("myCustTitle");
oRm.writeClasses();
oRm.write(">");
oRm.write("<h2>"+oControl.getTitle()+"</h2>");
oRm.write("</div>");
oRm.write(oControl); },
setTitle:function(val){
this.setProperty("title",val,true);
return this; } });
return dynamicMenu; })
This is how I am calling my custom Control:
var menu=new CustomMenu({ title:"MyCustom Title" });
var oItemTemplate = new sap.ui.unified.MenuItem({
text: "{description}",press:e=>alert("test") });
menu.bindAggregation("items", {
path: "/plants",
template: oItemTemplate });
thisOfBtn.getView().addDependent(menu);
menu.setModel(that.getModel());
// const eDock = sap.ui.core.Popup.Dock;
// unable to align position of popup
// menu.openBy(oButton.getFocusDomRef(),true, "EndTop", "EndBottom","0 -2");
menu.open(true,oButton.getFocusDomRef(),"BeginTop", "BeginBottom", oButton.getParent().getDomRef());
Try calling oRm.renderControl(oControl) instead of oRm.write(oControl)

How to use formatter method with async call

im try to display some data using b-table and the formatter method using axios with the spread method but this its not displaying correctly.
this is what i have https://codepen.io/damian-garrido/pen/MWgxqeZ
html template
<div id="app">
<b-table
:fields="fields"
:items="items">
</b-table>
</div>
js file
window.onload = () => {
new Vue({
el: '#app',
data() {
return {
fields: [
{
key: 'owner',
label: 'Poke Owner'
},
{
key: 'pokemonIds',
label: 'Poke Name',
formatter: 'getPokeName'
}
],
items: [
{
owner: 'Juan',
pokemonIds: [3,4]
},
{
owner: 'Diego',
pokemonIds: [7,9,14]
}
]
}
},
methods: {
getPokeName: function (pokeIds) {
let promises = []
for (let id of pokeIds) {
promises.push(axios.get(`https://pokeapi.co/api/v2/pokemon/${id}`))
}
axios.all(promises)
.then( axios.spread((...responses) => {
let names = ''
for (let r of responses) {
names += r.data.name + ', '
}
console.log(names)
return names
}))
}
}
})
}
the console.log return the names, as i need, but not display this on the table.
Table formatter methods must be synchronous.
You would need to make your formatter an async method that uses await to return the value from your formatter. Note that this will slow your app rendering to a crawl as each row will have to wait for the async call to finish before it can render the table row.
Your best bet would be to do the lookup processing in your app, and then pass that data to the table.

Change props value in vuejs2

I am new in vuejs2 development. I am working in a modal development. I kept the modal body code in a component and displaying that modal in another component. I have below code in modal body component.
<script>
import SemanticModal from 'vue-ya-semantic-modal'
export default {
components: { SemanticModal: SemanticModal() },
name: 'ModalBody',
props: ['active1',],
data() {
return {
visible: false
}
},
methods: {
close() {
this.$emit('sendValue', false); //this is working
this.visible = false
},
open () {
this.visible = true
},
},
watch: {
active1 () {
if (this.active1 && !this.visible) this.open()
else if (!this.active1 && this.visible) this.close()
},
},
directives: {
'click-outside': {
bind: function(el, binding, vNode) {
el.onclick = function(e) {
var modal = document.getElementsByClassName("modal");
el.addEventListener('click', function (event) {
if (!modal[0].contains(event.target)) {
vNode.context.$emit('sendValue', false); //this is not working
this.visible = false
}
})
}
}
}
}
}
I am calling that model (child) component in parent component like below
<modal-body :active1="active1" #sendValue="active1 = $event"></modal-body>
I need to change the below props active1 value to false from child to parent component.
You are handling click event by using directives.
According to your requirement , clickoutside directive should emit sendValue event from child to parent. But i feel like your code has some complications.
The proper code to accomplish your scenario is below
directives: {
'clickoutside': {
bind: function(el, binding, vNode) {
el.onclick = function(e) {
console.log("binding clicked");
vNode.context.$emit('sendValue', false);
}
}
}
}
if your objective is to use click event you can use #click binding to accomplish the same