accessing dojo attach point outside templated widget - dojo

I have a dojo attach point for list item which is inside a templated widget.I need to access the dojo attach point outside the widget in order to assign onclick to the list item created by the template.How do I do it?

Well, if you want to attach an event handler to it, you have to provide a function. You can override functions/properties from outside using getters and setters.
I also suggest using data-dojo-attach-event if you only need the node for attaching event handlers. For example by using: data-dojo-attach-event="onclick: myFunction". By doing this, it needs a function called myFunction in your templated widget, provide a default function (in your widget) for example:
myFunction: function() {
/** Stub */
},
And then you can do something like this from outside:
myWidget.set("myFunction", function(evt) {
console.log("Someone clicked on the list item");
});
Because the myFunction event handler is overriden, it will execute the function provided in the setter.
You could also directly access the attach points from outside using:
myWidget.listItemNode
When you have a data-dojo-attach-point="listItemNode". However, I don't think it's recommended to use it this way because now your widget is tightly coupled (you use the internal functionality of the widget).

HTML template:-
<div data-dojo-attach-point="infoDialog" >
</div>
Save this as "Template.html"
---------------------------------------------
load this html file using "dojo\text" plugin in your widget i.e.
"dojo/text!./templates/Template.html",
and store as template in widget main function
-----------------------------------------------
assign it as template string to the widget.
templateString: template,
now this template attachpoint(infoDialog) will be the part of your current widget scope.
-----------------------------------------------
attach event:-
this.infoDialog.onclick=function(){
alert("hello world")
};

Related

Prevent DOM reuse within lit-html/lit-element

I am looking for a way to NOT reuse DOM elements within lit-html/lit-element (yes, I know, I'm turning off one of the prime features). The particular scenario is moving an existing system to lit-element/lit-html that at certain points embeds the trumbowyg WYSIWYG editor. This editor attaches itself to a <div> tag made within lit-element and modifies its own internal DOM, but of course lit-html does not know that this has happened, so it will often reuse the same <div> tag instead of creating a new one. I am looking for something similar to the vue.js key attribute (e.g., preventing Vue from aggresively reusing dom-elements)
I feel like the live() directive in lit-html should be useful for this, but that guards against reuse based on a given attribute, and I want to prevent reuse even if all attributes are identical. Thanks!
I have had similar issues with rich text editors and contenteditable - due to how templates update the DOM you don't want that to be part of a template.
You do this by adding a new element with the non-Lit DOM and then adding that to the DOM that Lit does manage:
class TrumbowygEditor
extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const div = document.createElement('div');
shadow.appendChild(div);
const style = document.createElement('style');
// Add CSS required
shadow.appendChild(style);
$(div).trumbowyg(); //init
}
}
customElements.define('trumbowyg-editor', TrumbowygEditor);
As this is running in a custom element's shadow DOM Lit won't touch it, you can do:
html`
<div>Lit managed DOM</div>
<trumbowyg-editor></trumbowyg-editor>`;
However, you will have to implement properties and events on TrumbowygEditor to add everything you want to pass to or get from the nested jQuery component.
You can add the scripts with import if you can get module versions of jQuery/Trumbowyg (or your build tools support it) or you can add <script> tags to your component, add fallback loading DOM content in the constructor, and then on the load event of the <script> call the $(div).trumbowyg() to init the component.
While messier and more work I'd recommend the latter as both components are large and (thanks to jQuery being built on assumptions that are now 15 years old) need to load synchronously (<script async or <script defer don't work). Especially on slower connections Lit will be ready long before jQuery/Trumbowyg have loaded in, so you want <trumbowyg-editor> to look good (show spinner, layout in the right amount of space etc) while that's happening.
You write that you attach the external library directly to an element managed by lit-html. It sounds like you're doing essentially this:
render(html`<section><div id=target></div></section>`, document.body)
external_lib.render_to(document.querySelector("#target"))
If this is what you do instead try to create your own div, let the external lib render to that div, and finally attach that div to lit-html:
let target_div = document.createElement('div')
render(html`<section>${div}</section>`, document.body)
external_lib.render_to(target_div)
The most up-to-date answer to this problem is to use Lit's built-in keyed directive. This scenario is exactly what it's for:
https://lit.dev/docs/templates/directives/#keyed
Associates a renderable value with a unique key. When the key changes, the previous DOM is removed and disposed before rendering the next value, even if the value—such as a template—is the same.
#customElement('my-element')
class MyElement extends LitElement {
#property()
userId: string = '';
render() {
return html`
<div>
${keyed(this.userId, html`<user-card .userId=${this.userId}></user-card>`)}
</div>`;
}
}

Making small components on runtime

I am having a problem working with JQuery DataTable. I had to use that plugin since I had no other choice allowed due to my project requirements.
So the problem is that, I am adding rows to DataTable and in the row there's a column with button HTML tag. Now I want to bind an on click handler to the button.
dt.Rows.Add({
column_1_data,
column_2_data,
"<button #click='itsVueTime'>MyButton</button>"
});
Here dt is the DataTable's instance. Now the problem is #click won't work. I understand that its not being rendered by Vue thats why its not working.
Is there a way to bind click event in this condition?
Without knowing more context, I would recommend this way of doing it
In your component with the method you want to use, you can expose the component like this. (I use mounted, but you can use other lifecycle methods too like created)
mounted() {
window.app = this;
}
then you can use
<button onclick="app.holler()">Say Hello</button>
you can also expose just the function you want to use like so
mounted() {
window.itsVueTime = this.itsVueTime;
}

How to call gridready event of AG-Grid on a click Handler function

I am working on a project in my organization where I have to implement ag-grid .I know ag-grid has a gridready event function which fires initially and sets the params with all grid api properties . My rowdata is constantly changing which is coming from some other component ,I have added a handler (function) where I want to call gridready event again which sets my rowdata with the latest values . The problem is gridapi properties will be initialized only at 1st time ,I want to access those (gridapi) properties in my handler as well which is becoming undefined now ,how to access gridapi properties even after grid initialization and scope is lost. I don't have the exact code ,I have added some code snapshots which will describe my situation exactly.
grid.component.ts
#Input() gridData
ngOnit() {
}
handler() {
want to call grid ready but params will be undefined ,how to set this gridapi properties.
gridready()
}
gridready(params) {
this.gridapi =params.api;
this.columnapi =params.clumnapi
this.gridapi.setRowData(this.gridData.rowData)
}
According to ag-Grid's documentation, the Grid API (both api and columnApi) will only be available after the gridReady event has been fired.
Here's an alternative way of achieving the same thing if I understood your problem correctly.
You can try adding a local variable, isGridAPIReady, in your own grid.component.ts to track that state.
Set isGridAPIReady to true when ag-grid GridReadyEvent fires.
And eventually set the new rowData using the grid api:
//grid.component.ts
#Input()
rowData: any[];
ngOnChanges(changes: SimpleChanges): void {
if (this.isGridAPIReady && changes['rowData']) {
this.gridOptions.api.setRowData(this.rowData);
}
}
Hope it helps!

Best way of executing js function located in parent page from custom element

Aurelia: I have a custom element that should execute a function that located in the parent page. the custom element doesn't "know" what function it should execute - it depends on the parent page, and currently I send the name of the function as an attribute to the custom element (the attribute - on-focus-out-action-name):
<form-input field-name="firstName" title="First Name" on-focus-out-action-name ="validateInput()" />
I manage to run the function when it has no params, but when I want to send params (simple string type which is also sent as attribute) - no success
Is there a better way to do it ?
The best way was if I could pass the function as an object (Dependency Injection ?)
You should use the call binding command for this. The way to pass parameters to a function when using the call bind is a bit wonky, but once you understand it, it's easy.
In the custom element, you will pass an object to the bound function. Each property of this function will be matched to the named parameters in the binding. Let's look at it in action. In the page VM, I'll have a function:
pageFunction(paramOne, paramtwo) {
//.. stuff happens
}
This function will be called by a custom element. So in the page view, we will write the binding like this:
<my-element some-func.call="pageFunction(paramOne, paramTwo)"></my-element>
Inside my-element's VM, we can call the bound function, and pass the parameters to it like this:
this.someFunc({paramOne: this.someProp, paramTwo: this.otherProp});
I've created a runnable gist example here: https://gist.run/?id=864edc684eb107cdd71c58785b14d2f9
I prefer using a CustomEvent for this, this makes it clear in the template what is going on.
In your component you dispatch the event like this - the "details" can be any object/data you want:
let event = new CustomEvent('on-focus-out-action-name', {
detail: <some-data-you-want-to-send>,
bubbles: true
});
this.element.dispatchEvent(event);
You'll also need to inject the element in the constructor (you'll also need to use autoinject or inject Element manually)
constructor(private element: Element) {}
Your parent template would then look something like this:
<form-input field-name="firstName"
title="First Name"
on-focus-out-action-name.delegate="validateInput($event)" />
And in your parent component, you use the data you sent like this:
validateInput(event) {
let data = event.detail;
// do stuff
}
Check out this blog post for more details https://ilikekillnerds.com/2015/08/aurelia-custom-elements-custom-callback-events-tutorial/
I managed to do it in Aurelia: This is the custom element with the foucusout.trigger which calls to the focusoutAction in the appropriate timing:
<template>
<label>
${title} - ${fieldName}<input title.bind="title"
focusout.trigger="focusoutAction()" focusin.trigger="focusInAction()"
class="${cssClass}" />
</label>
</template>
This is the usage of the custom element in the parent view with the focusout.call attribute:
<form-input field-name="firstName" title="First Name"
place-holder="Enter first name"
on-focusout.call="validateInput(nameOfElement)" />
And this is the relevant code in the view model of the custom control:
#bindable onFocusout;
focusoutAction() {
var args =
{
nameOfElement: this.fieldName
};
this.onFocusout(args);
}

how to code external onclick dojo dialog

How does one code to use an external js function for onclick handler in a button in an dialog with the ability to pass values from dijit.Dialog elements or must it be coded entirely inline?
I'm not entirely sure about what you're trying to say, but if you really want to call an external function, but you have no idea how to pass the parameters, you can still write an inline click handler that passes the arguments to your external function, for example:
registry.byId("myBtn").on("click", function() {
var param1 = myDialog.get("param");
externalFunction(param1);
});