Als Webentwickler war ich es gewöhnt, Dialoge und Inhalte einer Webseite dynamisch via Ajax nachzuladen. Unter Fhem fand ich keine einfache Möglichkeit. Daher hier ein Lösungsansatz. Er ist bewusst flach gehalten und verzichtet auf externe Bibliotheken wir jquery, prototype usw.
Mein HTML ist meist recht komplex. Daher war es sinnvoll HTML und Perl zu trennen. Perl bietet selbst eine nette Templates-Engine.
Bei mir läuft das ganze unter Linux daher auch nur die Anleitung dafür. Das Perl-Modul HTML::Template lädt man wie folgt nach...
- sudo -s
- cpan
- cpan>install HTML::Template
unter
/opt/fhem/www/
habe ich nun einen Ordner tpl
angelegt, wo ich meine Templates ablegen möchte.
Achtung, ich lege alle Dateien in UTF8 ab. Gängige Editoren wie Notepad++ unterstützen diesen Zeichensatz.
Die Datei /opt/fhem/FHEM/99_Utils.pm
eignet sich dafür um eigenen Perl-Funktionen abzulegen.
##############################################
# $Id: ajax_dialoge.html,v 1.7 2015/10/26 15:27:10 pi Exp $
package main;
use strict;
use warnings;
use POSIX;
use Time::Local;
use HTML::Template;
sub
DialogInit
{
my $tpl = HTML::Template->new( filename => '/opt/fhem/www/tpl/dialog_init.tpl' );
return $tpl->output;
}
sub
MeinNeuerAjaxDialog
{
my $tpl = HTML::Template->new( filename => '/opt/fhem/www/tpl/mein_dialog_template.tpl' );
return $tpl->output;
}
In der Perl Funktion DialogInit()
sollen alle notwendigen Javascript, CSS, HTML aus der
Datei /opt/fhem/www/tpl/dialog_init.tpl gerendert werden, damit der Dialog angezeigt
werden kann.
Die Perl Funktion MeinNeuerAjaxDialog()
soll den eigentlichen Dialog
aus Datei /opt/fhem/www/tpl/mein_dialog_template.tpl rendern
Die Datei dialog_init.tpl
enthält alle wichtigen Javascript, CSS und HTML - Kram um den
Dialog anzeigen zu können. Man könnte natürlich alles in separate Dateien auslagern. Der Code soll nur
veranschaulichen wie es funktioniert.
Das div-Element mit der Id idCommonDialog
soll unser Container sein, in der später der Dialog
geladen werden soll. Der spätere Dialog soll einige Ebenen höher als der Floorplan liegen,
daher das CSS-Style mit dem z-index:2000
cDialog.show(url)
lädt nun den Inhalt der angegebenen url in den genannten Container und führt
gefundenes Javascript aus. Der Container wird zum Schluß nur noch sichtbar gemacht. Natürlich könnte man
hier noch Funktionen zum Zentrieren oder Verschieben des Dialogs implementieren.
Doch für den Fall soll es erst ein mal reichen.
<!-- Inhalt von /opt/fhem/www/tpl/dialog_init.tpl -->
<div id="idCommonDialog" style="display:none"></div>
<style type="text/css">
div.dialog
{
z-index:2000;
}
</style>
<script type="text/javascript">
cDialog =
{
show : function(url)
{
// klappt besser mit synchronen Requests, da polling sonst dazwischen funkt
asynchronous = false;
xhr = new XMLHttpRequest();
xhr.open("GET", url, asynchronous );
xhr.setRequestHeader('Content-Type', 'application/html; charset=UTF-8');
//xhr.setRequestHeader('Access-Control-Allow-Credentials', 'true');
//xhr.setRequestHeader('Access-Control-Origin', '*');
if (asynchronous ) xhr.timeout =500;
xhr.onreadystatechange = function ()
{
if (xhr.readyState == 4 && xhr.status == 200)
{
idCommonDialog = document.getElementById('idCommonDialog');
txt = xhr.responseText;
idCommonDialog.innerHTML = txt;
idCommonDialog.style.display = 'block';
var scripts = [], sc_re = /<script[^>]*>((?:.|[\n\r])*?)<\/script>/ig, sc; while ((sc = sc_re.exec(txt)) !== null){ scripts.push(sc[1]); } txt = txt.replace(sc_re, "");
for (var i = 0; i < scripts.length; i++){eval(scripts[i]); }
return -1;
}
else
{
if (xhr.status!=200)
console.log("fehler state " + xhr.readyState +" status "+xhr.status);
}
}
xhr.send();
return -1;
},
hide: function()
{
idCommonDialog = document.getElementById('idCommonDialog');
idCommonDialog.style.display = 'none';
idCommonDialog.innerHTML = '';
}
}
</script>
<!-- Inhalt von /opt/fhem/www/tpl/mein_dialog_template.tpl -->
<div style="display:block;position:absolute;left:346px;top:42px; width:366px; height:410px;" class="dialog">
<b>Mein Dialog</b><br>
Hier kann man reinpacken was man möchte.
Mit dem Javascript-Befehl FW_cmd könnte man auch von hier aus Befehle zum
Fehm-Server schicken.
</div>
Wenn ich einen Dialog in einen Flurplan anzeigen lassen möchte, muss ich sicher stellen, dass
mein Javascript, HTML und CSS auch im Flurplan landet. Das geht wie folgt
(im Beispiel Floorplan All
)
# Initialisieren der Dialog-Funktion im Floorplan All
define DialogInit weblink htmlCode {DialogInit()}
attr DialogInit fp_All 1,1,0,
Hier noch eine kleine Schaltfläche mit der man nun den Dialog öffnen kann
# Knopf zum Dialog im Floorplan All
define KnopfZumDialog weblink htmlCode <div style="height:64px;;width:64px;;background-color:red;z-index:150" onclick="cDialog.show('fhem?cmd={MeinNeuerAjaxDialog()}&XHR=1')"><br></div>
attr KnopfZumDialog fp_All 463,466,0,