Opening UI5 Quickview by non-SAPUI5 control - d3.js

I'm trying to integrate UI5 with other libraries(namely D3) and am unable to open a UI5 Popover(or QuickView) by my controls.
The only method I can call to open the popover is .openBy(control).
According to the UI5 documentation: The Control = This is the control to which the popover will be placed. It can be not only a UI5 control, but also an existing DOM reference.
I've tried multiple things, but am unable to get the popover to open successfully. I continue to get errors in sap-ui-core.js.
Does anyone have any ideas on how to properly pass the DOM reference of my non-UI5 control?
Here is a code snippet showing what I'm trying to accomplish:
// circleClicked is the SVG element clicked on the map
function openQuickView(circleClicked) {
// quickView controls are UI5 and were created before this function
quickViewPage.setHeader(circleClicked.created_by);
// error
quickView.openBy(d3.select(circleClicked));
};

Everything you describe seems to be correct.
According to the documentation of sap.m.Popover you can also call openBy with a DOM reference as parameter.
What you are passing in your code snippet is not a DOM reference however, it is a d3 selection. To get the required DOM reference from the selection you have to add [0][0](see this answer).
function openQuickView(circleClicked) {
quickViewPage.setHeader(circleClicked.created_by);
quickView.openBy(d3.select(circleClicked)[0][0]);
};
EDIT: After playing around with the provided fiddle I found the problem.
The parameter to the function is the datum of the clicked object, not the DOM.
You should change your click handler and the function like this:
svg.selectAll("circle")
// ...
.on("click", function(d) {
openQuickView(this, d);
});
function openQuickView(circleClicked, circleData) {
quickViewPage.setHeader(circleData.created_by);
quickView.openBy(circleClicked);
}

Related

Ajaxinate Endless scolling has stopped product Quick View from working

I am using Shopify "Streamline Theme" with quick product view and I recently added infinite scroll to products on each collection using Ajaxinate.js.
When I open a collection page it loads with some products which is supposed to do, The products already there work fine with quick view and quick add to cart and also.
The Infinite scroll works fine and it loads new product fine but the problem is raised when the new products loaded through AJAX call doesn't have work with the quick view function.
I have tried to create a callback function to activate the quick view with no success, using the theme initialisation code with no success.
function callBack(){
theme.init();
theme.initQuickShop();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "scroll",
loadingText: 'Loading...',
callback: callBack
});
});
Edit -------
My problem, is that when the page is loaded only the initial loaded products quickview elements are loaded in the DOM. When the scroll more button is clicked, the newly loaded products are loaded without their respective quickview elements. Hence why the quickview does't work for them. The theme.js file comes with this initialisation code:
theme.reinitProductGridItem = function($scope) {
if (AOS) {
AOS.refreshHard();
}
if (theme.settings.currenciesEnabled) {
theme.currencySwitcher.ajaxrefresh();
}
// Reload quick shop buttons
theme.initQuickShop(true);
// Refresh reviews app
if (window.SPR) {
SPR.initDomEls();SPR.loadBadges();
}
// Re-register product templates in quick view modals.
// Will not double-register.
sections.register('product-template', theme.Product, $scope);
// Re-hook up collapsible box triggers
theme.collapsibles.init();
};
I have tried to integrate this into a callback but no success, the quickview modal doesn't seem to load for the newly loaded products:
function callBack(){
ReloadSmartWishlist();
var $container = $('#CollectionSection');
theme.reinitProductGridItem($container);
// I have tried the following init qith no success:
// theme.init();
// theme.initQuickShop(true);
// theme.initQuickShop();
// sections.register('product-template', theme.Product, $container);
// AOS.refreshHard();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "click",
loadingText: 'Loading...',
offset: 0,
callback: callBack
});
});
I am missing something but what? :/
Note for other things like loading products images with the callback and the wishlist app, it works as intended...
When you load elements via AJAX and if the events are not attached to a parent element that is not removed from the DOM, those elements will not have an attached event to them.
The term used here is event delegation.
Here is an example of non-delegated event:
document.querySelectorAll('a').addEventListener('click', function(){
// Do something
})
Since you are attaching the event to the existing "a" elements if you add new 'a' via AJAX those elements will not have the event since Javascript already attached all the events and it will not reattach them if you don't specifically recall them again.
Here is an example of a delegated event:
document.querySelector('body').addEventListener('click', function(target){
let target = event.target;
if (target.tagName === 'A'){
// Do something here
}
})
Where we attach the event to the body tag ( where it's a better idea to attach it to a closer none-modified parent element of the ajax items ) and once we click we check if our target tag is an "a" and do something then.
So long story short, you will need to delegate the quick cart link so that it works after you load the items via AJAX.
Drip is correct you need to delegate your event, but for people like me it's hard to completely understand how to do that.
I'm not sure how your quickview is structured, but if you open it with a .click function and can use jquery use the [.on() function][1].
For example: I use a quickview that opens on a button click. My button is attached to my product-grid-item.liquid with this bit of code:
<div class="quick-view-button">
<a class="quick-view" data-handle="{{ product.handle }}" href="javascript:void(0);">Quick View</a>
</div>
My quickview function originally looked like this:
function quickView() {
$(".quick-view").click(function () {
//all of the quickview code
What happens is exactly like you described. The event listeners only loaded on the first product load but nothing after an AJAX load.
Using jquery's .on() binds the event listener to the element meaning when it's loaded in later it'll still have the event. Here's an example of what my code looks like after using .on()
function quickView() {
$('body').on('click','.quick-view',function(){
I really hope this helps you or someone else with this problem.
[1]: http://api.jquery.com/on/

d3 trying to open html in div or iframe on click

I have a d3 bar chart and I would like to have it so when a bar is clicked on the chart, HTML is loaded in another section of the page. As I've been trying to research how to do this, I am becoming more and more confused. Should I use a <div> or an <iframe>? I have this which works as far as a clicking event goes:
.on("click", function() { alert("Hello world"); })
But I don't think I can use it since I want different bars to open different content. So I also need to figure out how to tie which bar is clicked to which file is opened. Can anyone point me in the right direction? Thanks.
Add an onclick event which passes clicked bar reference to the function which loads html.
.on('click', function(d) {loadHtml(d)});
Then create a loadHtml function
function loadHtml(clickedBar)
{
if (clickedBar[0] = "foo")
{
$('#DivForLoadingHtml').load("http://mydomain.xyz/foo.htm");
}
if (clickedBar[0] = "bar")
{
$('#DivForLoadingHtml').load("http://mydomain.xyz/bar.htm");
}
}

changing d3 class on mouseover

I'm using d3 v4 and trying to apply a style to an element on mouseover.
I'm wondering if it's an API change with V4 but I can't seem to get at the node
I have code:
.on('mouseover', () => {
let self = d3.select(this);
let c = self.attr('class');
but this gives an error
Cannot read property 'getAttribute' of null
So the d3.select() doesn't seem to work...
The mouseover IS firing however.
I can use
.on('mouseover', (elem) => {
console.log('elem', elem);
Which will give me some type of D3 object, but not a DOM node.
I can't use any D3 methods on this object
elem.classed("hilite", true);
elem.attr("class", "hilite");
Neither of those methods exist on a d3 returned object.
So how do I do this super basic operation in d3?
related to
Change class of one element when hover over another element d3
You can use arrow functions (if this an arrow function), if you don't need access to this of the current element. In this case you do need, because D3 calls event handlers setting this to the element triggering this event.
See: Using arrow functions with d3

AlloyUI Diagram Builder Read-only

I am using the Alloy Diagram Builder to create and display network topology.
I would like to remove default click and drag events attached to each nodes, so viewers would not have the ability "build" diagrams but only view diagrams that I have generated.
http://alloyui.com/examples/diagram-builder/real-world/
I have tried these but it does not work.
// detach click event to all nodes with class aui-diagram-node.
Y.all('.aui-diagram-node').detach("click");
// unbind
$(".aui-diagram-node").each(function(){
$(this).unbind();
});
I believe the event is attached to the container .aui-diagram-builder-drop-container via delegate() and the event would be mousedown.
Merely by accident I found a hack that might work for this. I was adding tooltips to my page on which I had a diagram builder, well apparently the tooltips layer a div over the page and simply set the opacity on it to be clear and the object still resides. After a tooltip had come up i was unable to interact with the piece of the diagram builder the tooltip had popped up over.
So based of this concept, why not try overlaying a div over the entire canvas of the diagram and give it a high z-index so that it sits on top. It should effectively not allow interaction with the canvas.
Yes it's a kludge but it just may work.
To make a DiagramBuilder read-only, you can detach() events from all of its children recursively:
/*
* Readonly the diagram
*/
function ReadonlyDiagram(diagram) {
function detachRecursively(node) {
node.get('children').each(detachRecursively);
// You may also want to set the cursor to the default since it will
// change based on which elements the mouse is over.
// node.setStyle('cursor', 'auto');
// You may want to detach specific events such as 'click' or
// 'mousedown' if you do not want to disable all events.
node.detach();
};
diagram.on('render', function (event) {
detachRecursively(diagram.get('boundingBox'));
});
}
Now, you must be post diagramBuilder object to ReadonlyDiagram function like below codes:
YUI().use('aui-diagram-builder', function (y) {
var diagram = new y.DiagramBuilder(
{
availableFields: data,
boundingBox: '#' + containerId,
fields: nodes,
srcNode: '#' + builderId
}).render();
diagram.connectAll(connections);
if (callBackDiagram !== undefined) callBackDiagram(diagram);
if(isReadonly === true) ReadonlyDiagram(diagram);
});
});
Reference

Loading a hidden div into an AJAX jQuery UI tab (future DOM element)

I have been trying to manipulate content that is loaded into jQuery UI tabs via AJAX.
As you can imagine, these elements are "future" DOM elements and aren't manipulated by normal $(".someClass")functions.
I've read using .live() for event handling is now deprecated using jQuery 1.7+ and is replaced by the new .on() method.
My issue is that the div I want to hide, when it loads in an AJAX tab, must be manipulated after the initial DOM load and is not bound to a click event at first.
My functions, which are currently wrapped in $() are below.
I think I have the syntax correct for links that use a click handler, but I'm not sure of the correct way to ".hide()" my "hiddenStory" div at load.
I also think that the functions themselves shouldn't be wrapped in an overall $()?
Any help or advice would be greatly appreciated.
$(function(){
// this .hiddenStory div below is what I want to hide on AJAX load
// need syntax and/or new methods for writing this function
$(".hiddenStory").hide();
// this is a function that allows me to toggle a "read more/read less" area
// on the "hiddenStory" div
$(".showMoreOrLess").on('click', (function() {
if (this.className.indexOf('clicked') != -1 ) {
$(this).removeClass('clicked');
$(this).prev().slideUp(500);
$(this).html("Read More" + "<span class='moreUiIcon'></span>");
}
else {
$(this).addClass('clicked');
$(this).prev().slideDown(500);
$(this).html("See Less" + "<span class='lessUiIcon'></span>");
}
}));
});
// prevents default link behavior
// on BBQ history stated tab panes with
// "showMoreOrLess" links
$('.showMoreOrLess').click(function (event)
{
event.preventDefault();
// here you can also do all sort of things
});
// /prevents default behavior on "showMoreOrLess" links
Could you set the display: none via CSS and override it when you wanted to show the element's content? Another option, if you have to do it this way would be to add the `$(".hiddenStory").hide() in the callback from the AJAX load that is populating the element. For example:
$(".hiddenStory").load("http://myurl.com", function(){
$(".hiddenStory").hide();
}
);
If you aren't using the .load method, you should have some sort of call back to tie into (e.g. success if using $.ajax...)

Resources