Attach a context menu (context.js) to a node in cytoscape.js - javascript

Since the nodes in a cytoscape.js graph are on a canvas I'm not sure how to go about marrying the two together.
I guess a generic version of the question is, how can jQuery find a node?
I've tried the cytoscape.js-cxtmenu circular context menu. And while it's very cool, we'd like a more traditional popup menu. In another SO question (which is closed) context.js was recommended.
Reusing the demo.js from that page it seems line I need to modify this line
context.attach($("#layer2-node").get(0))
and replace it with something like
context.attach(document.getElementById('cy').elements())
however, that doesn't seem to work.
thoughts?

Generally, you have to create dummy DOM elements per node as needed to call a DOM library. This is the approach the qtip extension uses, creating divs on demand.
If you can specify positions/offsets in the lib, then you could alternatively just use your container DOM element.

Related

Angular: best way to access component properties & functions in click handler of child element of <svg>

I am having the same error as this person:
Add onclick event to SVG element
But I am having it in Angular which makes it even harder to deal with.
I want single SVGPathElement to be clickable & access component properties and functions in this click handler. But this is giving me the error 'property undefined' or 'function doesn't exist'.
According to this answer:
Add onclick event to SVG element
It's because all of the JavaScript must be included inside of the SVG for it to run.
But I cannot even do what the mentioned answer Add onclick event to SVG element suggested, since I get an error probably because Angular cannot create the component when parsing the html.
I also looked at http://xn--dahlstrm-t4a.net/svg/html/from-svg-to-parent-html-script.html (mentioned in the comments of the above answer) but that gave me the same error as in the picture above.
Now the second requirement to the solution to my question:
I am planning on having many different svgs and interacting differently with them. So manually pasting some <script> into the svg won't be a scalable nor maintainable solution for me. So actually I would prefer not doing this over the html with <script> tags at all.
If anyone has a good solution to solve both of my inquries in angular I would be greatful.
Additional Info:
I am using the ionic framework but I don't think it matters in this case
One possible alternative solution:
One could use ReactJS instead of Angular.
SVGPathElement<->Component interaction works in ReactJS without doing anything manually. I have no idea why but there is no such errors maybe because the component is not a class but rather a function. This would be great to know why exactly! I don't want to use ReactJS since I am not familiar with it. But maybe it's my best option to just switch frameworks since SvgPathElement<->Component interaction is a key part of my mobile app (it's like the seterra mobile app (https://play.google.com/store/apps/details?id=com.seterra&hl=en&gl=US)).
I found the culprit:
Don't do this (Angular doesn't allow it for some reason):
ngOnInit() {
document.querySelectorAll('.area').forEach((element: Element) => {
element.addEventListener('click', this.clickHandler);
}
);
}
Instead you have to add the click handlers manually like this:
<svg id="svgroot">
<path ... (click)="clickHandler()">
If anyone knows how to make this work by adding click handlers programmatically then this would make the solution a lot more automated/scalable/maintainable.
Adding click handlers programmatically to the SvgPathElement works in ReactJS for some reason, probably due to some internal differences between how the frameworks generate components.

How to programmatically insert an Ember component into a div?

I am trying to figure out a way to use Ember components as a view template for tooltips. Let me explain this:
I am supposed to create a library to show tooltips in Ember. The content of this tooltip is unknown. It might be very complex or it might be a simple text. The developer is the one who will decide it but the library must offer a way to do it easily. Also, I want to offer this solution in the format of an Ember modifier so that the developer would code it something like:
<div {{tooltip foo bar}}>
Hello World
</div>
As the modifier offers a reference to its element it is easy to use the good old JS to create elements and append them to this element. For a simple text it works like a charm and I was able to do it already as shown in the example below, but for complex component it's better to create a component and show it inside the tooltip's container.
// tooltip.js
import { modifier } from 'ember-modifier';
import { isPresent } from '#ember/utils'
export default modifier(function tooltip(element) {
const content = document.createElement('span')
content.append('Hi, I am the tooltip\'s text')
element.append(content)
});
The problem starts when you want to build a complex view, specially if it's supposed to contain some logic associated.
What I thought is that I could programmatically insert Ember components into the element that is passed as the argument of the modifier function. I can't use the dynamic component helper ({{component}}) as it infers that I have to mess with .hbs files more than what I am doing already and I can't approach it using the tooltip as a component; I need it to be a modifier.
I looked into this solution here but it doesn't seem to work in Ember 3.8.
Can anybody give me a clue on how do make it happen?
I am using Ember 3.8 and Ember Modifier#1.0.5
You could use the same technique that either of these use:
https://github.com/NullVoxPopuli/ember-popperjs
https://github.com/CrowdStrike/ember-velcro
They allow for "any content tooltips" by using an external library (popper or floating-ui, depending -- these are important though, because positioning is hard).
The gist is the following:
modifier on the "reference" / "hook" element (what you hover over)
modifier on the "target" / "popover" / "loop" element
some code that communicates between the two modifiers to wait until both are present before rendering the tooltip in the correct location / position.
The key part missing from your original code is that you need two modifiers -- tooltips with complex content are not possible with a single modifier (unless you manually manage element references).
For ember 3.8, I don't know how much you'll be able to do.
Ember 3.28 is the oldest LTS supported now and is passed its last bugfixes date, and it will step receiving security patches in January -- see: https://emberjs.com/releases/lts/
you may be able to use 2 global modifiers and a service to communicate between them -- but I don't know how that would work when you attempt to have multiple tooltips / popovers on the screen at the same time.
The minimum ember version you need to use the two addons linked above is 3.27.
Personally, it's well worth the upgrade, as staying up to date is generally easier than leap frogging years at a time (because you have the community doing upgrades with you) -- and shiny stuff is fun :)

How to re-render hyperHTML to the same element after content change

I am trying to support the same type of thing as React.Children
My code looks like
const elem = document.getElementById("profile")
const render = hyperHTML.bind(elem);
const name = elem.textContent
render`<b>Hi ${name}</b>`
So the API looks like
<div id="profile">alax</div> 🢂 <div id="profile"><b>Hi alax</b></div>
and I am using MutationObserver to rerender on content change
But if the content is changed. hyperHTML says its rending to the right element.. but the element keeps its innerHtml(No update)
I can see the <!--_hyper: -2001947635;--> is removed then the content is set but setting up the render & hyperHTML.bind again does nothing
Any thoughts would be great! Thx
Update
The fix to the above problem is to call hyperHTML.bind`` then your normal render using hyperHTML will work
Context -
I am using hyperHTML to create a custom element library(hyper-element)
My use case: I work in a mix-tech project (some people use jQuery)
Side note, on the why. I want to support something like partial templates
Example of a partial template:
<user-list data="[{name:'ann',url:''},{name:'bob',url:''}]">
<div>{#name}</div>
</user-list>
Output:
<user-list data="[{name:'ann',url:''},{name:'bob',url:''}]">
<div>ann</div>
<div>bob</div>
</user-list>
This is one use of setting custom content in an element you control
At the moment I have the setting of the content by 3-party working/re-rending
https://jsfiddle.net/k25e6ufv/16/
My problem is now: it is rending another custom element and getting the pass content to child element
It looks like hyperHTML is setting the child element's content in front to the element and creating the element without setting the content
Scroll down to bottom of source to see implementation!
https://jsfiddle.net/k25e6ufv/14/
Rending crazy-cats:
Html`
xxx: ${this.wrapedContent} zzzz
`
Current output:
wrapedContent: ppp time:11:35:48 ~ crazy-cats: **Party 11:35:48** xxx: zzzz
<crazy-cats>Party 11:37:21 xxx: <!--_hyper: -362006176;--> zzzz </crazy-cats>
Desired output:
wrapedContent: ppp time:11:35:48 ~ crazy-cats: xxx: **Party 11:35:48** zzzz
<crazy-cats> xxx: Party 11:37:21 zzzz </crazy-cats>
I will try to answer as best as I can, but I'll start saying that when asking for help, it'd be much easier/better to show the simplest use case you are trying to solve.
There is a lot of "surrounding" code in your fiddles so that I'll try to answer only to hyperHTML related bits.
hyper-element ?
I am not sure what's the goal of the library but hyperHTML exposes hyper.Component, and there's also an official HyperHTMLElement class to extend, which does most of the things you manually implement in your examples.
I'll keep answering your questions but please consider trying, at least, the official alternative and maybe push some change there if needed.
partial templates
hyperHTML pattern and strength is the Template Literal standard. Accordingly, to generate TL from the DOM would require either parsing of the content or code evaluation. Both solutions aren't the way to go.
Custom Elements require JavaScript to work, and without JS your partial template is useless and also potentially confusing for the user/consumer.
You don't want to define what to do with the data in the layout, you want to define a Custom Element behavior within the class that defines it.
That means: get rid of old-style in-DOM output, and simply use the Custom Element class to define its content. You maintain the related class only instead of maintaining a layout that has no knowledge about how the CE should represent that data.
TL;DR the following is a bad hyperHTML pattern:
<user-list data="[{name:'ann',url:''},{name:'bob',url:''}]">
<div>{#name}</div>
</user-list>
all you want to do is to write this:
<user-list data="[{name:'ann',url:''},{name:'bob',url:''}]"></user-list>
but be careful, the data attribute in hyperHTML is special only if passed through the template literal. If you want to pass JSON to the component, call the attribute differently.
// hyperHTML data is special, no need to use JSON
render`<c-e data=${{as: 'it is'}}></c-e>`
Above snippet is different from having JSON as data attribute text so your example should use data-json name, and the class should remember to JSON.parse(this.dataset.json) in its constructor (or have an attribute observer that does that for you)
hyperHTML owns elements
When you write:
it looks like hyperHTML is setting the child element's content in front to the element and creating the element without setting the content
you are assuming you should care at all what hyperHTML does: you shouldn't.
The only thing you should understand is that hyperHTML owns the node it handles. If you trash those nodes via different libraries or manually, you are doing something wrong.
hyperHTML(document.body)`<p>hello ${'world'}</p>`;
// obtrusive libraries ... later on ...
document.body.textContent = 'bye bye';
// hyperHTML still owns the body content
hyperHTML(document.body)`<p>hello ${'world'}</p>`;
Above snippet is perfectly fine and totally wrong at the same time.
You don't update the body content manually, you don't interfere with its content via jQuery or other libraries, and you should never trash the content at all.
Once you chose hyperHTML to handle a bound context, that's it, you've made your choice.
This is true for pretty much every library on this world. If you use Angular to create something and you mess it all via jQuery, that breaks. If you write backbone templates and you mess later on with their content manually, that breaks.
If you bind an element to hyperHTML and you mess it up with other libraries, that breaks.
The only thing that won't break are wires, meaning the moment you create a wire, you can append it directly and that's actually a DOM node so it will be there, and it will be handled by hyperHTML.
Yet you should use hyperHTML to handle those changes, never jQuery or JS itself.
The output is right
When you say that the output should not contain the comment you are assuming you should care what output is produced via hyperHTML: you shouldn't!
hyperHTML uses comments as delimiters and these are absolutely fine for both performance, being unaffected by repaint and reflows, and for partial changes like the following one:
hyperHTML(document.body)`<p>${'a'} b ${'c'}</p>`
Both a and c will have a comment as anchor node to be able to update their content with anything later on.
hyperHTML(document.body)`<p>${[list, of, nodes]} b ${otherThing}</p>`
You change interpolations? All good, hyperHTML knows what to replace and where.
force-own the content
If you use a different template literal to re-populate a bound node you are trashing the cache and creating new content.
At that point you are better off with innerHTML because all the features of hyperHTML will be gone.
To start with, if your content can change so much, use an array.
hyper(document.body)`${['text']}`;
// you can clean up the text through empty array
hyper(document.body)`${[]}`;
// re-populate it with new content
hyper(document.body)`${['a', 'b', 'c']}`;
Above example is still better than changing template because all the optimizations for the content will be already there.
However, if you want to be sure the node the initial one created via hyperHTML, assuming no third parts script mutate/trash that node, you can use a wire.
const body = hyper()`<p>my ${'content'}</p>`;
document.body.textContent = '';
document.body.appendChild(body);
It's a bit extreme but at least faster.
As Summary
It looks like you are trying to sneak in hyperHTML into an application that trashes layout all the time through different third parts libraries.
Unless you create a closed Shadow DOM reference and you drop partial template through layout, you'll always have issues with libraries based on side effects with DOM content, libraries that mutates elements they don't own.
In hyperHTML the ownership concept is key, like in React you cannot change at runtime the defined JSX for the component, you should never try to change at runtime the defined template literal for hyperHTML.
Now, as much as I'd like to solve all your issues, I feel like it's right to ask you: are you sure hyperHTML is really the solution for your current app? It looks like surrounding side-effects caused by third parts libraries would constantly break your expectations if you don't use closed mode Shadow DOM and hyperHTML only to update your DOM.

Use ExtJs and JQuery together in one application?

we have a big ExtJS (still ExtJs 2) application, which provides windows explorer like functionality on a Java EE server.
We now evaluate implementing a new functionality; we could base this functionality on a jQuery plug in.
Is it recommended to use jQuery and ExtJs in one application?
Or will I have to deal with side effects occurring?
You will not have to deal with any side effect. The only problems I ever ran into when I did this is if I tried to operate on elements that Extjs relied on being left alone. Just make sure that when you start operating using jQuery that you're working inside a div that extjs doesn't plan to mess with. The best way to do this is to inherit from Ext.Component and then operate inside the div it provides. Ext doesn't do anything inside this div so you should be ok. You can also tap into the resize functionality if you need to, though this is not really necessary.
My company does so with no problems on a fairly large Apache/Catalyst site. We do use jQuery() rather than $() due to some old Prototype stuff on our site.
More: Blueprint CSS Extjs and JQuery in the same page, best way to make them coexist without conflict
keep jQuery within the jQuery namespace http://api.jquery.com/jQuery.noConflict/
Use it like so: $.noConflict();
jQuery only adds a single object (jQuery) to the window's context so you won't have any conflicts.
PROBLEM: Some third party libraries for jquery may rely on static webpage organization. As ExtJS components can be added to webpage dynamically - they will have no functions from third party libraries.
SOLUTION: You will have to run this library's script after component is added (use afterrender event, for example). Then you can call these functions on this ext component.

Is $.empty() enough for big ajaxy apps?

Been working on an App and since it's getting a bit too big I've thinking of ways to improve memory management since the app runs mostly on Javascipt. So every time a navigation item is clicked I would call the jquery empty then show the html via ajax. ex:
//$.ajaxSetup(); called before this
//$this is the attached element
$.ajax({success:function(data){
$this.empty().html(data.output).fadeIn(400);
//more javascript stuff like loading tinymce or jquery ui
}});
is this enough to prevent memory leaks? I'm not entirely sure what empty does but I'm assuming it removes all DOM elements within that div along with any other objects and events? btw. You can find the app here http://webproposalgenerator.com/ and http://webproposalgenerator.com/demo.
any tips on improving the performance/security or any feedback at all would be greatly appreciated.
$.fn.empty should be enough, it deletes all data and events associated to the elements and then deletes the elements. It also calls .widget("destroy") on all jquery-ui widget.js based widgets that are defined on those elements.
It is also important to note that jquery's $.fn.html method calls $.fn.empty() on the given element before appending html, therefore, if you are using $.fn.html, you don't have to call $.fn.empty
actually my guess was that .html implies .empty anyway, also I'm not sure that's true. for the perforamnce part: according to jqfundamentals excelent book it is a recommanded best practice to add content while the element is in .detach() from the DOM. tried to lock at the code for advice but didn't find it. nice site btw

Categories