Blog: Latest Entries (15):



React mit Hooks.. echt praktisch

Manchmal hat man bei React trotz besserem Wissens sich Strukturen gebaut, die es schwierig machen, den Datenfluss sauber abzubilden. So passierte es plötzlich, dass in einer Child-Component etwas geändert wurde und diese Änderung es verlangte, dass in einer Geschwister Child-Component eine Liste neu geladen werden sollte. Diese Component lud die Liste von einer REST-API selber und bekam die Liste nicht von der Parent-Component. Doof. Per Callback wurde die Parent-Component von der Änderung unterrichtet, aber diese konnte das Neuladen ja nicht so einfach anstoßen, weil es an sich keine State-Änderung gab.

Man braucht ein Flag, dass sich ändert, wenn ein Refresh der Child-Component nötig wurde. Da reicht ein bool-Flag, das bei jeder Änderung seinen Zustand ändert.

Hier wird es einmal sehr gut erklärt, aber mit den neuen Hooks von React geht es sehr elegant umzusetzen.

Hier der Code aus der Parent-Component:

const[trigger,setTrigger] = useState(false);

return (
<>
<child1 onSubmit={() => {setTrigger(!trigger)}}/>
<child2 refresh={trigger}/>
</>
);


Damit child2 nun was laden kann muss man nicht mehr groß selbst prev-props mit next-props vergleichen sondern kann useEffect() verwenden:


useEffect(() => {reloadList()}, props.refresh);


Schön einfach und kompakt. Das man mit useEffect() direkt auf einzelne props/Änderungen reagieren kann, macht mir React schon sehr viel sympathischer. Erinnert mich an Watcher und Proxies aus Vue, nur sehr viel einfacher zu verwenden.

Minimal Vue + Bootrstrap Boilerplate

Für einfaches und schnelles Prototyping ohne viel Struktur oder Komponenten. Z. B. zum Testen von Logik oder REST-API Abfragen.


<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Minimal Vue.js + Bootstrap 4 Boilerplate</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>
<body>
<div id="app">
<h1 class="text-center">{{ message }}</h1>
</div>
<script>
var app = new Vue({
el: '#app',
created: function () {

},
beforeDestroy: function () {

},
data: {
message: "Hello Web!"
},
methods: {

}
});
</script>
</body>
</html>


An sich für genau das wo für ich immer AngularJS verwendet habe. Werde ich hoffentlich demnächst für PWA erweitern.

Form Generatoren in den verschiedenen Frameworks

Ich hatte in den letzten Tagen etwas mehr mit verschiedenen Formular-Generatoren in verschiedenen JS-Frameworks zu tun. Vorraussetzung bei allen was, dass die JSON-Schema als Schema-Sprache unterstützten. Bis jetzt war keiner dabei der wirklich fehlerlos funktionierte. Ich kann jetzt nicht sagen, ob es nicht doch irgendwie an mir lag, aber falls jemand bestätigen kann, das ein Problem kein generelles Problem dabei ist, wäre ich doch sehr glücklich und würde mich noch mal genauer damit beschäftigen.

Vue:
FormSchema Native: lief erstmal ganz gut, aber wenn sich das Schema oder das Model im State geändert hat, würde die Form nicht neu gerendert. Wenn man eine Form-Component schreibt in die verschiedene Schemas rein gericht werden können zur Laufzeit ist das echt doof. Ansonsten machte es nämlich einen guten Eindruck.

React:
react-jsonschema-form: bis jetzt das Beste nur gibt es leichte Probleme wenn man $schema mit etwas anderen als dem default-Schema rein reicht. Aber ansonsten funktioniert bis jetzt am Besten. Auch mit 3 Forms auf der Seite.

AngularJS
Angular Schema Form: Wenn man nur eine Form auf der Seite braucht funktioniert es perfekt. Wenn ich aber zwei Forms mit zwei unterschiedlichen Schemas verwende, wollte die zweite Form nicht mehr rendern. Aber eine Form allein funktionierte echt super. Leider ist AngularJS aber ja nicht mehr wirklich supported und ich hätte lieber etwas für Vue gehabt, was genau so gut funktioniert.. eben dann auch mit 2-3 Form pro Seite.

Meine Suche geht weiter, weil ich Vue doch lieber mag als React. Oder am Ende doch selber schreiben oder ein andere auf JSON-Schema anpassen?




c't webdev 2019

bbcode-image


Es waren 3 lange und anstrengende Tage. Aber es gab viel neu oder erneut zu entdeckten. Vieles was man schon kannte und einges was einfach interessant war.

bbcode-image


Gerade die eher nicht technischen Themen wie Mob-Programming bei sipgate, "A Brief History Of the Internet" und UX waren sehr interessant und auch motivierend, selbst mal wieder mehr nach vorne zu gehen. Auch wurde sehr oft erwähnt, dass die Menschen, die Standards, Frameworks und eben die Dinge machen, mit denen man täglich arbeitet, auch nur ganz normale Entwickler sind und auch über jede Idee/Bugifx oder Mithilfe sehr dankbar sind.

bbcode-image


Und man solle nicht alte Fehler wiederholen sondern neue machen. Mehr Zeit für Experimente und keine Idee ist schlecht zu der Zeit der sie entwickelt wird, weil sie immer ein Problem löst. Ob sich die Lösung später als eher schlecht heraus stellt (wie Flash oder Java-Applets) muss sich erst noch zeigen.

Das mit wichtigste Thema war PWA (an sich kaum unterschiedlich zu den OWAs auf Firefox OS nur jetzt mit breiter Unterstützung), um Websites direkt zu Apps machen zu können und eine Interaktion mit dem Device zu erlauben.

Daher wird mein neues Experiment:
- PWA
- Vue.js für das Frontend
- Bootstrap 4 als CSS-Framework
- Fetch-API anstelle von Axios
- Daten werden nur von den momentan Teilnehmenden Devices bereit gestellt
- Die Plattform selbst hat keine klassische Persistenzschicht wie eine DB sondern bildet nur einen State für alle Devices ab
- also gibt es nur Daten-Caching aber keine Langzeitspeicherung
- JWT für den Login auf der Singlepage-App
- Backend mit Meecrowave oder Spring Boot.. da bin ich mir noch unsicher.. aber auf jeden Fall Java

Mal gucken was dabei raus kommt.

Hoffentlich dann nächstes wieder dort hin!

Event/Action driven State-Changes

Nach dem Ich mich etwas mit Redux beschäftigt habe, habe ich mir mal einen eher aktuellen Spiele-Prototypen von mir vorgenommen und geguckt, ob man diesen auf ein ähnliches Konzept umbauen kann. Also dass alles per Actions gesteuert wird. Alles was man macht wird per Action rein gereicht, in einer Loop(Interval) von einem Handler verarbeitet und dann gerendert, wenn mindestens eine Action ausgeführt wurde.

bbcode-image


Nach einer schnellen Betrachtung wurde aber klar, dass ich da nicht viel umbauen konnte, weil die Engine schon rein der Logik wegen so implementiert wurde. Ich hab es einfach so schon gemacht ohne groß drüber nachzudenken. Ich hatte zusätzlich noch Action-Groups eingeführt, die dafür sorgen, dass immer nur die erste Action einer Gruppe ausgeführt wird (um Actors sich über die Map bewegen zu lassen, wobei alle Schritte vorberechnet sind und nicht vor dem Schritt dieser erst berechnet werden muss). Es gibt zwei Lanes: fast und slow. "slow" für das Bewegen von Actors auf der Map und "fast" für Eingaben und Dinge die an sich in Echtzeit da sein müssen. Ein kleine Verzögerung ist aber ok.


addIntervalEvent: function(eventType, eventArgs, group, lane){
this.interval.queue.push({lane: lane ? lane : 'slow',
type: eventType, args: eventArgs, parent: this.interval,
group: group});
},
performIntervallTick: function(lane){
//filter to perform events of a lane and remove this performed events
let filterCheck = [];
let render = false;
let newQueue = this.interval.queue.filter(item => {
let res = true;
if(item.lane == lane){
if(this.eventHandling[item.type] && (item.group === null || !filterCheck[item.group])){
this.eventHandling[item.type](item.args, item.parent, this);
filterCheck[item.group] = item.group;
res = false; //was performed and is removed from the queue
render = true;
}
}
return res;
});
this.interval.queue = newQueue;

//using the same method to trigger field-events
if(render){
this.globalKeyListener({keyCode: 0}, true);
this.renderViewport();
}
},


Hier bei ist der Controller der State und wird durch die Actions verändert. Eine Action wird mit einer Veränderung des State gleichgesetzt, da einmal oder zwei Rendern ohne das es nötig gewesen wäre in der Gesamtheit nichts ausmacht.

Was ich dabei gelernt habe ist, dass es schwer ist in JavaScript anders zu arbeiten, wenn man die Verarbeitung und die Ausgabe von einander entkoppelt, was gerade bei Anwendungen mit Grafik schwer ist nicht zu tun.

States, Redux... ein Selbstversuch

Heute saß ich vor der Dokumentation zu den neuen Hooks bei React und las mit durch welche Probleme die alle lösen sollen. Indirekt wurde gesagt, dass sie Redux unnötig machen sollen.. also war für mich die Zeit gekommen mir wirklich mal Redux anzusehen. Erstmal zu verstehen, was es tut, ist nicht ganz so einfach, wenn man sich deren Doku durchliest. Was gefühlt aber nur an deren Doku liegt, die viel zu zerstückelt ist, um einen schnellen Überblick zu bekommen.

Ich habe mit Hilfe von anderen Seiten dann heraus bekommen. Dass Redux einen State pro Reducer hält und der alle Actions bekommt und dann mit eigener Logik, die für ihn interessanten Actions ab arbeitet. Alter State geht rein und neuer geht raus. Eine Action hat einen Typ und ein Payload. Wenn sich der State ändert, wird etwas getriggert. Aber ich fand, dass doch für einfache Beispiele zu viel Code da war um es zu verstehen.

Um also das Prinzip von Redux zu verstehen habe ich dann mal 20min investiert und mir ein ähnliches Kontrukt geschaffen. Etwas Topic orientierter mit den States, so dass man die Reducer pro Action hat, aber vom Prinzip ist es alles relativ einfach und das Bootstrapping wurde daruch auch übersichtlicher.

Die meisten Beispiele zu Redux bestehen sowie so zu 50% aus React. Aber ich wollte ja Redux verstehen und nicht Redux+React (ja.. die gibt es wirklich auf getrennt von einander!)

Hier das Ergebnis meines Selbstversuches:


const StateStore = {
states: {},
actions: {},
createState: function(name){
this.states[name] = {
data: null,
workers: [],
listeners: []
}
},
registerWorker: function(statename, action, worker){
if(this.states[statename]){
if(!this.states[statename].workers[action]){
this.states[statename].workers[action] = []
}
this.states[statename].workers[action].push(worker);

if(!this.actions[action]){
this.actions[action] = [];
}
this.actions[action].push(statename);
}
},
registerListener: function(statename, listener){
if(this.states[statename]){
this.states[statename].listeners.push(listener);
}
},
action: function(action, payload){
if(this.actions[action]){
this.actions[action].forEach((statename) => {
let changes = false;

this.states[statename].workers[action].forEach((worker) => {
const stateCopy = JSON.parse(JSON.stringify(this.states[statename].data));
let newState = worker(this.states[statename].data, payload);
if(newState != stateCopy){
changes = true;
}
this.states[statename].data = newState;
});

if(changes){
this.states[statename].listeners.forEach((func) => {func(this.states[statename].data)});
}
});
}
}
};


Dazu mein kleiner Test:

StateStore.createState("test");
StateStore.registerWorker("test", "inc", (state, payload) => {
if(state === null){
return 0;
}
else {
return ++state;
}
});
StateStore.registerWorker("test", "dec", (state, payload) => {
if(state === null){
return 0;
}
else {
return --state;
}
});
StateStore.registerWorker("test", "dn", (state, payload) => {
console.log("(" + state + ") do nothing");
//change-listeners are not triggert
return state;
});
StateStore.registerListener("test", (state) => {console.log(state)});

StateStore.action("inc", null); //0
StateStore.action("inc", null); //1
StateStore.action("inc", null); //2
StateStore.action("dn", null); //(2) do nothing
StateStore.action("inc", null); //3
StateStore.action("dec", null); //2


bbcode-image


Lief erstaunlicher Weise direkt wie es sollte (nach dem ein kleiner Fehler bei einer falschen Variablen behoben war).

Damit sollte man durch aus das gesamte Statemanagement einer Componente abbilden können. Wenn man nun noch Events verwenden würde, geht es schon stark in Richtung Reactive-Programming.

https://github.com/annonyme/jsstatestore

References vs Callback-Functions in Vue und React

Bei Vue.js werden bei den Props, die an eine Unterkomponente übergeben werden, diese als Referenz übergeben. Wenn ich in der Unterkomponente etwas Ändere, werden diese Änderungen im selben Object durchgeführt, dass sich auch in der Oberkomponente befindet und ich kann diese Änderungen direkt dort verwenden. Ein Watcher sollte auch Änderungen erkennen können.
Bei React müßte ich eine callback-funktion mit in die Unterkomponente reinreichen und bei der Änderung diese antriggern, damit die Oberkomponente die geänderten Daten in denen eigenen State wieder übernehmen kann. Also wird in dem Sinne jede Reference, wie sie in Vue.js existiert hätte, durch eine selbst zu implementierende callback-Funktion ersetzt?

Macht das Sinn? Erzeugt es nicht nur mehr Arbeit und Code? Wo genau ist da der Vorteil? Oder habe ich da was falsch verstanden bei React?

Irgendwie werde ich mit React nicht wirklich warm. Es sind so Kleinigkeiten, die mich da stören oder mir das Gefühl geben, einiges wäre unnötig kompliziert in React.

PHP: Smarty 3 und nested-Blocks

Ein Problem bei Smarty 3 ist, dass man zwar sehr schön Blöcke überschreiben oder erweitern kann, es aber keine Möglichkeit gibt, die im Block vorhanden anderen Blöcke dan unangetastet zu lassen. Man muss in so einem Fall die nested Blöcke immer mit kopieren und mit dem Parent-Template immer wieder bei Änderungen abgleichen. Wenn z.B. ein Template einen Block mit einer <form> hat und darin dann die ganzen Eingabe-Möglichkeiten auch als Blöcke ausgelegt sind, muss man wenn man die Form-Action ändern will zwingend auch das ganze Form-Content Templating mit übernehmen.

Eine Möglichkeit das zu umgehen ist den Block rendern zu lassen und dann per String-Operationen das Ergebnis anzupassen. Primitiv aber auch sehr effektiv!

https://stackoverflow.com/questions/38913099/extending-nested-blocks-keep-children

Beispiel:

{extends file='parent:frontend/forms/form-elements.tpl'}

{block name='frontend_forms_form_elements'}
{capture name="frontend_forms_form_elements"}
{$smarty.block.parent}
{/capture}
{$smarty.capture.frontend_forms_form_elements|replace:'register/index':'forms/index'}
{/block}


Falls man Formular von Shopware in anderen Contexts als dem forms-Controller einsetzt, kann es nötig sein, dass Template so anzupassen, damit es noch auf den forms-Controller zeigt und nicht auf den Controller in desen Context man gerade arbeitet.


Shopware: Eine Static-Content/Seiten Gruppe anlegen

Eine neue Seiten-Gruppe im Backend anlegen und dabei einen Key wie "myGroup" vergeben.

Der Key, den man dort verwendet, findet man in $sMenu wieder.


<ul class="group--mygroup">
{for $sMenu.myGroup as $item}
<li>
<a href="{if $item.link}{$item.link}{else}{url controller='custom' sCustom=$item.id title=$item.description}{/if}">
{$item.description}
</a>
</li>
{/for}
</ul>


Damit ist es auch schon erledigt und man kann anfangen Seiten der Gruppe zu zuordnen und sie werden entsprechend gerendert. Unterseiten und ähnliches sind auch möglich, wie frontend/index/footer-navigation.tpl zeigt. Die Gruppen findet man in der Datenbank in der Table s_cms_static_groups.

Optionale Parameter in Spring Controllern

An sich ist das hier vollkommen logisch und man wundert sich warum man diesen Fehler überhaupt gemacht hat.. weil man den vorher nicht gemacht hat. Deswegen sollte man im Kopf behalten, dass wenn man optionale Parameter im Methodenaufruf im REST-Controller in Spring hat, diese null als Wert haben können müssen.

Einfach gesagt Integer verwenden und nicht int, weil das natürlich Probleme geben würde, wenn Spring null in einen int füllen möchte.


getList(@RequestParam(value = "typeId", required = false) Integer typeId){}

Older posts:

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?