Add tinymce editor to element object instead of selector - javascript

I have a custom-element (Aurelia equivelent of a web component) that creates a tinymce editor. There is no way to select the textarea by using a selector (because there can exist any number of these custom-elements on a page). I need some way of initializing the tinymce instance by passing it the element object. Is there such a possibility? I haven't been able to find this functionality anywhere...
Thanks in advance.

Sorry that I'm a bit late. I had this exact same problem. I used an Angular directive, and I wanted to initialize TinyMCE on $element. It turns out you can use this syntax:
var element = getYourHTMLElementSomehow();
//...
tinymce.init({
target: element
});
So you don't use selector at all, and instead use target.
I had to look in the source code for this, because it doesn't seem to be explicitly documented anywhere.

Since TinyMCE seems to require you to use a selector and won't let you simply pass an element instance (and the developer doesn't seem to grasp the utility of this use-case, based on his forum responses), your best bet would be to do something like this:
View
<template>
<textarea id.one-time="uniqueId" ...other bindings go here...></textarea>
</template>
ViewModel
export class TinyMceCustomElement {
constructor() {
this.uniqueId = generateUUID();
}
attached() {
tinymce.init({
selector: `#${this.uniqueId}`,
inline: true,
menubar: false,
toolbar: 'undo redo'
});
}
}
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
My UUID function comes from here: Create GUID / UUID in JavaScript?

Related

Typescript type of MaterialTextfield

I am creating my first react typescript app.
I found a some sample in pure javascript and wan't to write sth similar in typescript.
In firebase javascript sample we can see
addCommentForm.onsubmit = function(e) {
e.preventDefault();
createNewComment(postId, firebase.auth().currentUser.displayName, uid, commentInput.value);
commentInput.value = '';
commentInput.parentElement.MaterialTextfield.boundUpdateClassesHandler();
};
I would like to do sth similar in typescript, I would like to create a function like below
function resetMaterialTextfield(element: HTMLInputElement) {
element.value = '';
element.parentNode.MaterialTextfield.boundUpdateClassesHandler();
}
My question is:
How should I declare element, with what type, so that it had parentNode, parentElement attribute, and that parent had MaterialTextfield on which I could call boundUpdateClassesHandler()?
From which library/framework does MaterialTextfield come from? What type is it? Where can I find documentation or some tutorial for this class and library?
Use type assertion.
(element.parentNode as TypeYouWant).MaterialTextfield.boundUpdateClassesHandler();

Extended custom elements not working in Angular 2+

I can't get Angular (2+, not AngularJS) to play nice with my extended custom element, which is defined like so:
class FancyButton extends HTMLButtonElement {
connectedCallback() {
this.innerText = `I'm a fancy-button!`;
this.style.backgroundColor = 'tomato';
}
}
customElements.define("fancy-button", FancyButton, {
extends: "button"
});
And used like so:
<button is="fancy-button">Fancy button here</button>
The definition is fully compliant to web standards according to this Google Developer resource:
https://developers.google.com/web/fundamentals/web-components/customelements#extend
It's working fine in a vanilla web setup and in React, but Angular ignores it and shows a standard button, apparently ignoring the is="fancy-button" attribute.
Here is a stackblitz showing this in action.
One fancy-button is outside the Angular scope (index.html) and is working fine.
The other button is inside the Angular scope (app.component.html) and is NOT working.
Why oh why?
There are two types of Custom Elements:
Autonomous Custom Elements, which are classes that extend HTMLElement
Customized Built-in Elements, which are classes that extend a
specific type of element, such as extend HTMLButtonElement.
Customized Built-in Elements are not supported in Angular (more details
on this below). They are also still not supported in Safari (as of
Sep 2020: https://caniuse.com/#search=custom%20elements
).
Your example is a Customized Built-in Element. The workaround that has
worked for me is to rewrite any Customized Built-in Element as an Autonomous
Custom Element wrapped around a Built-in Element. This gives me the
encapsulation I want, it gives me a way to customize the built-in, and it works with Angular and Safari.
For your example above, the translation is:
class FancyButtonToo extends HTMLElement {
connectedCallback() {
const buttonElement = document.createElement('button');
this.appendChild(buttonElement);
buttonElement.innerText = "Fancy button #2 here!";
buttonElement.style.backgroundColor = 'tomato';
}
}
customElements.define("fancy-button-too", FancyButtonToo);
(The project also needed schemas: [ CUSTOM_ELEMENT_SCHEMAS] added
to app.module.ts). Full code here: stackblitz,
and it renders like this (original "fancy-button" left in for comparison):
Additional Info
Q: Are we certain Angular cannot support Customized Built-in Elements (as
opposed to, say, there being some obscure configuration we are not aware of)?
A: We are certain: The spec document at https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example
goes into deep detail on the differences between Autonomous Custom Elements
and Customized Built-in Elements. One important difference is in
programmatic construction of elements:
// Built-in Elements and Autonomous Custom Elements are created like this:
el = createElement(name);
// examples
el = createElement("button"); // normal built-in button
el = createElement("fancy-text"); // a custom element
// Customized Built-in Elements are created like this:
el = createElement(built-in-name, { is: custom-built-in-name });
// example
el = createElement("button", { is: "fancy-button" });
The relevant Angular template-rendering code is found at
https://github.com/angular/angular/blob/master/packages/platform-browser/src/dom/dom_renderer.ts
and as of the current version of Angular, you will find:
class DefaultDomRenderer2 implements Renderer2 {
/* ... */
createElement(name: string, namespace?: string): any {
if (namespace) {
return document.createElementNS(NAMESPACE_URIS[namespace] || namespace, name);
}
return document.createElement(name);
}
/* ... */
}
The Angular renderer currently does not have the extra code
needed to pass in the 2nd argument to createElement(); it cannot
create Custom Built-in Elements.
class DefaultDomRenderer2 implements Renderer2 {
// ...
create(name: string, namespaceOrOptions?: string | ElementCreationOptions) {
if (namespaceOrOptions && typeof namespaceOrOptions === 'string') {
// In cases where Ivy (not ViewEngine) is giving us the actual namespace, the look up by key
// will result in undefined, so we just return the namespace here.
return document.createElementNS(NAMESPACE_URIS[namespaceOrOptions] || namespaceOrOptions, name);
}
if (namespaceOrOptions && namespaceOrOptions.hasOwnProperty('is')) {
return document.createElement(name, namespaceOrOptions as ElementCreationOptions);
}
return document.createElement(name)
}
}
You can add the polyfill javascript file with your project and then it should work:
https://github.com/ungap/custom-elements

Convert a minified jQuery plugin into a plain JavaScript plugin

I'm running a project where people can find doctors on the map and And book Online , ...
Previously I decided to use Angularjs and change the whole project, so I had to forget about some jQuery plugins which I've used before.
**Problem ** :
I'm using a jQuery plugin that works awesome with Google map API (Landcarte) , and I haven't find anything else to compare with this plugin in AngularJS.
So I couldn't do anything but to use both jquery and angular and this plugin in my site , But I dont know , I feel that its wrong to use both jquery and angular because I think that makes my firstload heavy.
**Questions : **
1- Is this possible to convert this plugin into a normal Javascript so I can omit the Jquery in my site ?
2- If not , What can I do ?
3- Can I use RequireJS to load jquery and this plugin later in my site ? (I dont know how to :()
I don't know about the Landcarte plugin so I can't help you with question 1.
If you want to initialize a jquery plugin but it's not working, a common cause of the problem is that the DOM is not ready yet.
To solve this, there are three options:
Method 1 Initialize the plugin inside of the link property of your directive. Within the link function, the children of the directive element have already been compiled and linked. If your plugin relies only on the children of the element being DOM ready, then this option is suitable.
app.directive('myDirective', function(){
return {
link: function(scope, element,attr){
element.plugin();
}
}
});
Method 2 Using $evalAsyc which runs after the compile and link phase but before the Render phase. Use this method if your plugin relies on the entire page being DOM ready, but it is not important that expressions have been rendered.
app.directive('myDirective', function(){
return {
link: function(scope, element,attr){
scope.$evalAsync(function(scope){
element.plugin();
});
}
}
});
Method 3 Using $timeout which runs after the render phase. Use this method if your plugin relies on the entire page being DOM ready, and all the expressions have been rendered.
app.directive('myDirective', function($timeout){
return {
link: function(scope, element,attr){
$timeout(function(){
element.plugin();
});
}
}
});
Depending on the plugin, one of these options should work for you. Prefer one that meets the need of the plugin minimally - meaning prefer option 1, over option 2, over option 3, but ultimately go with the one that works.
To turn min.js file into normal.js you can use this
but it just set tabulations and spaces and make script readable.
For example this script:
var a={log:function(){try{var e=Array.prototype.slice.call(arguments);if(typeof e[0]==="string"){if(/[\{\}]/.test(e[0])&&e.length>1){var t=e.length>2?e.slice(1):e[1];return console.log(this.text(e[0],t))}}return console.log.apply(console,e)}catch(n){}}}
will be:
var a = {
log: function () {
try {
var e = Array.prototype.slice.call(arguments);
if (typeof e[0] === "string") {
if (/[\{\}]/.test(e[0]) && e.length > 1) {
var t = e.length > 2 ? e.slice(1) : e[1];
return console.log(this.text(e[0], t))
}
}
return console.log.apply(console, e)
} catch (n) {}
}
}
Landcarte can be used in a pure JS code without jQuery as well. A map can be initialized by an explicit call of the at.geo.Map class constructor:
var container = document.getElementById("map");
var map = new at.geo.Map(container);
This class is mentioned in the reference.

Prototype "classes" vs Jquery

I currently work with prototype, so for instance if i want to create a class or something i would do something like (using it with asp.net)
function MyTestClass()
{
this.MyHtmlDiv = $("myHtmlDiv");
this.MyButton = $("myButton");
this.init();
}
MyTestClass.prototype.init = function()
{
Event.Observe(...some code here ..);
Event.Observe(...more code...);
}
So you get the idea. I like the way it is organized but I have read here in other posts that jquery is better. I would like to start using it but.. is it possible to create "classes" like this, neat?. I usually have a separate .js file for each aspx page. How can I create a class like this with JQuery?
Any pointers, links, ideas or suggestions would be appreciated. When I google this, I see some jquery functions but havent really found a nice template that i can follow to maintain it like above if i was to move to jquery.
jQuery has no confliction with the style you use now, you just need to change your mind of how jQuery selector work and how to bind event handler etc.
function MyTestClass() {
// note jQuery's selector is different from Prototype
this.MyHtmlDiv = $("#myHtmlDiv");
this.MyButton = $("#myButton");
this.init();
}
MyTestClass.prototype.init = function() {
this.MyHtmlDiv.on('event_type', function() {
// some code
});
this.MyButton.on('event_type', function() {
// some code
});
}

How to tell if tinyMCE has been initated?

I initiate the tinyMCE like this in multiple tabs of JQuery:Tab. But I find to init tinyMCE multiple times yields readonly text areas. Thus I wish to check if tinyMCE is already initated. Is there a method like isInitated() or something similarly convenient there?
tinyMCE.init({
mode : "textareas",
theme : "simple",
width : "500",
height : "300"
});
You can use tinymce.editors.length to see if there is already an editor instance initalized (tinymce.editors.length > 0).
I know this question is old, but...in case someone is still looking for the holy grail:
in tinymce 4, you can pass a callback to tinyMCE.init like so:
tinyMCE.init({
//your regular parameters here...
setup: function(editor) {
editor.on('init', function() {
//all your after init logics here.
});
}
});
You can add init_instance_callback to init() parameters. This callback will be invoked when the tinymce instance is already inited.
I am using tincyMCE 4.7.2
I tried the answer of #Thariama and it did not work for me, I guess because his answer is valid for the older versions of the tinyMCE.
here is what worked for me (again, according to the version you are working on, this could not be helpful for you)
if (tinymce.initialized === true)
To check if "tinyMCE" is set just use this:
if(typeof(tinyMCE) != "undefined") {}
Try this:
if (typeof(tinymce.activeEditor.contentDocument) !== "undefined") {
// initialized
}
I found other solution for this.
Let's say that You've got element
<textarea id="tinymce0"></textarea>
Now let's say that You initialize it:
let config = { selector : '#tinymce0'};
tinymce.init(config);
After that You can make this:
let tinmyMceInstance = tinymce.get('tinymce0');
if( tinmyMceInstance === null ){
// Your code if not initialized
}
Keep in mind:
this works only with id, seems like using classname for get() won't work
Works in:
{
releaseDate: "2019-05-09",
majorVersion: "5",
minorVersion: "0.5",
}
So it's probably: 5.5

Categories