Knockout loses bindings when Google Maps API v3 info window is closed - google-maps-api-3

I'm data binding to a div which I am then setting as the content of an InfoWindow. When markers on the map are clicked I'm changing the bound observable, which updates the content in the info window. All of this works fine until the info window is closed. Google Maps removes the info window from the DOM, along with my div which had the bindings on it. Re-opening the info window results in its contents being frozen in the state it was in at its closing.
Any changes to the observable no longer update the ui, including using valueHasMutated. I've tried just resetting the content of the info window and rebinding, but the JQuery element still exists and I get duplicated content. I've also tried using cleanNode and rebinding but also get duplicate content with that.
The div which I'm binding too:
<div id="placeTmpl" data-bind="with: place">
<h3>
<a data-bind="text: name, attr: { 'href': detailsUrl($data) }"></a>
</h3>
</div>
The Google Maps InfoWindow:
window.infoWindow = new window.google.maps.InfoWindow({
content: ''
});
window.infoWindow.setContent($('#placeTmpl')[0]);
Event listener and updating observable
window.google.maps.event.addListener(marker, "click", function() {
window.viewModel.openInfoWindow(marker, data);
});
self.openInfoWindow = function (marker, data) {
for (var i = 0; i < self.places().length; i++) {
if (self.places()[i].placeId == data.PlaceId) {
self.place(self.places()[i]);
}
}
window.infoWindow.open(map, marker);
};
Like I said, this all works great until the info window is closed. I'm just looking for a way to force knockout to start updating the ui again or to clear and rebind when the info window is closed.

I was able to go around this by setting the InfoWindow content as an html string rather than a DOM node.
i.e:
window.infoWindow.setContent($('#placeTmpl').html());
rather than:
window.infoWindow.setContent($('#placeTmpl')[0]);
By doing this the html with the knockout bindings remains in place, rather than being transferred into the info window where it was subsequently being destroyed on close. Knockout now updates the bounded DOM elements as usual and I just update the info window with the html string on each click.
If you try to put knockout bindings in a Google Maps InfoWindow, you're gonna' have a bad time.

I was able to solve this without having to resort to using strings when calling setContent. In my case, the info window had dynamic elements that would update while the info window was open, so the string solution would not have taken this into account.
I added a closeclick handler in which I keep track of the original DOM element I passed to the info window and just add the element back to the body after the info window took it out. This keeps knockout happy and the info window doesn't care.
var $node = $('#placeTmpl');
var infoWindow = new google.maps.InfoWindow({
content: $node[0]
});
google.maps.event.addListener(infoWindow, "closeclick", function () {
//google maps will destroy this node and knockout will stop updating it
//add it back to the body so knockout will take care of it
$("body").append($node);
});

Related

Ready event for templates

In JsViews i can bind events in the following way:
<li id="myElement" data-link="{on 'click' eventHandler}">Some Content</li>
This will execute the method "eventHandler" after a click.
But I need an event which will be fired when the template is loaded. I tried "ready" or "show", but nothings works. Is there a event which can handle this?
The {on xxx eventHandler} handles events on HTML elements, such as mouse events, submit, change, blur, etc.
With JsViews, the loading of the template happens directly as a result of your own code calling the link method. So elements in the rendered template will have been rendered during that call, and immediately after you can run whatever code you want to call after rendering and linking, such as using jQuery to find your <li> element, and act on the element
JsViews also provides many life-cycle events on tags, so if you want you can create a custom tag just for handling those events:
For example, try running the following code:
<span id="result"></span>
<script>
var data = {};
$.views.tags("test", {
attr:"none",
render: function(data) {
debugger;
},
onBind: function(tagCtx, linkCtx) {
var elem = this.parentElem;
elem.textContent += " added text";
}
});
var myTmpl = $.templates('<ul><li id="myElement" data-link="{test}">Some Content</li></ul>');
myTmpl.link("#result", data);
$("#myElement").css('color', 'red');
</script>
You could use an onload event:-
https://www.w3schools.com/jsref/event_onload.asp
and attach that to the template itself. If you're limited in your options or need to do it in a specific way, explain the use case and why you want to do it a certain way and we'll try to help.
All the best,
Phil

Get DOM Reference of a SAPUI5 View/Control

I want show the info window for google maps. But the data or view is a ui5 view as below,
var oView = sap.ui.view({
type : sap.ui.core.mvc.ViewType.XML,
viewName :"com.example.view.ListView"
});
And this view is perfect.
Mainly I have the google info window and I need to place this inside that info window as follows,
var infowindow = new google.maps.InfoWindow({
content: view, //Here I am getting below error
position: coordinate
});
infowindow.open(map);
So i am getting
InvalidValueError: setContent: not a string; and Element-
In this I know I can place a domNode or string inside the google info window's content. So I need to know how can we convert this ui5 view to domNode or any other solution.
You can get the DOM Node of a SAPUI5 View or Control by using the method getDomRef
var infowindow = new google.maps.InfoWindow({
content: view.getDomRef(),
position : coordinate
});
infowindow.open(map);
Please be aware that a SAPUI5 View/Control only has a DOM Reference after it was first rendered. Furthermore after a potential rerendering you might have to re-apply the above code.
BR Chris

How to Print a Join Form?

Having difficulty figuring out how to print the contents of a Join form in BMC Remedy 9.0. Remedy's docs only explain printing Reports, not Join forms. I would like to either be able to print using Ctrl-P or by an internal Remedy process/action link. My Join form contains mostly character fields. Print preview truncates at the first ~20 px of height, despite a page width: 915 px and height: 1000 px. Does anyone know how I can print forms in the browser?
Figured out how to this - if you place all the content inside a Remedy panel object via the WYSIWYG, then you can add a script in the Web Footer to set the document.body.innerHTML equal to the panel's innerHTML. This little trick organizes the elements in a way that makes the page printable using window.print() or Ctrl-P. Be warned, though, that this innerHTML assignment often corrupts or loses properties like child textarea or input values. So you must scrape for these values and append them back to the page before printing.
<div>
<script>
function myPrint()
{
var idTexts = [];
var textareas = document.querySelectorAll('textarea[id][readonly]');
for(var i = 0; i < textareas.length; i++)
{
idTexts.push(textareas[i].id);
idTexts.push(textareas[i].value);
}
var inputs = document.querySelectorAll('input[id][readonly]');
for(var i = 0; i < inputs.length; i++)
{
idTexts.push(inputs[i].id);
idTexts.push(inputs[i].value);
}
//Assume there is only one panel object, so only one .pnl class on the page
var printContents = document.querySelector('.pnl').innerHTML;
document.body.innerHTML = printContents;
for(var i = 0; i < idTexts.length; i++)
{
if(document.getElementById(idTexts[i]))
{
document.getElementById(idTexts[i]).value = idTexts[i+1];
}
}
window.print();
}
/*On page load, I noticed the click event fired for a visible button
without the user actually needing to click.
Will not work unless the button's visibility is set to true (Remedy is a weird creature).
Used the setTimeout to allow time for the initial page load, as the onload event fired too early.
If you don't want this button to appear on the page when it is printed
overlay a transparent image on the button's display properties.*/
document.querySelector('a[ardbn="btnPrintMe"]').onclick = setTimeout(function(){ myPrint(); }, 500);
</script>
</div>
If you still have problems printing, make sure this btnPrintMe button has at least the correct 6 privileges: 1101/-1101, 1102/-1102, 1103/-1103 and the user you are testing this with has the appropriate privileges as well.

Event listeners on the IFrame with ExtJS4.1.2

I am using 'uxiframe' component to load a separate application into a modal window (ExtJS4.1x).
Cross domain issues clearly do not allow me to access any part of the IFrame contents. However same domain app's Document in the IFrame is accessible from the parent via iframe.getDoc()
The question is this: Is there a way for me to setup DOM listeners on the elements inside the IFrame from the parent modal window?
Thanks and a simple example would be appreciated.
Update Thanks to #lontiviero for a tip to get me started. Here is what I ended up with:
var bodyEl=Ext.get(iframe.getDoc().body); //this gives me an Ext.dom.Element object
bodyEl.on('click',
function(event, el,opts){
console.log("<p> clicked");
},
this, //scope
{delegate:'p'} //options
);
With this code i was able to attach an event to the iframeĀ“s body element.
Ext.EventManager.on(iframe.contentDocument.body, 'mouseover', function() {
alert('hello');
});
Take a look at it in jsfiddle: http://jsfiddle.net/XApQU/

Jquery code on load not firing

I have the following JQuery code in a external JS file linked into a
usercontrol in .Net 1.1 webapp.
The usercontrol is a timesheet.
When the page loads it calls MonthChange and works fine in one page.
But now I want to load the timesheet/usercontrol into aother
webpage that pops up a in a new browser window for printing.
Problem is my MonthChange is not firing.
Any ideas why???
$(function() {
MonthChange();
//TestData();
$('[class^=TSGridTB]').blur(function() {
var day = GetDay($(this).attr('id'));
var date = GetRowDate(day);
var bgcolor = GetInputFieldColor(date, false);
$(this).css("background-color", bgcolor);
$(this).parent().css("background-color", bgcolor);
//CalcHours($(this).get(0));
});
$('[class^=TSGridTB]').focus(function() {
var day = GetDay($(this).attr('id'));
var date = GetRowDate(day);
var bgcolor = GetInputFieldColor(date, true);
$(this).css("background-color", bgcolor);
$(this).parent().css("background-color", bgcolor);
});
$('[id$=lstMonth]').change(function() {
MonthChange();
});
});
without seeing further code, ensure that the selector is correct for the control in the new page.
The problem may be that the DOM has changed for the new page/window and JQuery does not yet know about it.
The change event
fires when a control loses the input
focus and its value has been modified
since gaining focus.
You might want to use the live event:
Binds a handler to an event (like
click) for all current - and future -
matched element.
When you bind a "live" event it will
bind to all current and future
elements on the page (using event
delegation). For example if you bound
a live click to all "li" elements on
the page then added another li at a
later time - that click event would
continue to work for the new element
(this is not the case with bind which
must be re-bound on all new elements).
Did you make sure that the new web page has jQuery script includes?
ensure you're using:
$(document).ready(
);
around your entire code block. The $ alone often does not do the trick.

Resources