How to make a parent invisible with Ajax in Wicket? - wicket

Last cause: The component(s) below failed to render. Possible reasons could be that:
1) you have added a component in code but forgot to reference it in the markup (thus the component will never be rendered),
2) if your components were added in a parent container then make sure the markup for the child container includes them in <wicket:extend>.
1. [WebMarkupContainer [Component id = child, page = com.test.TestPage, path = parent:child, type = org.apache.wicket.markup.html.WebMarkupContainer, isVisible = true, isVersioned = true]]
I'm trying to hava a WebMarkupContainer that can be made invisible by clicking their child, which is also a WebMarkupContainer. I get the exception above and I don't understand what to change to make it work.
My code is the following:
#MountPath("test")
public class TestPage extends WebPage {
public TestPage(PageParameters parameters) {
super(parameters);
IModel<Boolean> isVisible = Model.of(false);
WebMarkupContainer parent = new WebMarkupContainer("parent"){
#Override
public boolean isVisible() {
return !isVisible.getObject();
}
};
parent.setOutputMarkupId(true);
add(parent);
WebMarkupContainer child = new WebMarkupContainer("child");
child.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
isVisible.setObject(true);
target.add(parent);
}
});
parent.add(child);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head>
</head>
<body>
<wicket:extend>
<div wicket:id="parent"
style="height: 300px; width: 100%; background-color: mediumvioletred; padding: 10px; margin: 70px;">
<div wicket:id="child"
style="height: 200px; width: 200px; color: cadetblue; background-color: rebeccapurple">
click me
</div>
</div>
</wicket:extend>
</body>
</html>

I don't know exactly what's going on with the isVisible override, but it's causing the odd behavior.
Try this instead:
#Override
protected void onConfigure() {
super.onConfigure();
setVisible(isVisible.getObject());
}
This will be called only once per lifecycle, while isVisible is called multiple times.

You need to remove <wicket:extend> from the HTML! Your page does not extend from another page that uses <wicket:child/>.
Extra notes:
It is not very clear what you want to do. You say want to make visible but it starts as visible and click on the child will make it invisible due to the negation in
public boolean isVisible() {
return !isVisible.getObject();
}
Does it fail to render the initial page or after the Ajax click ?
Important: when changing visibility with Ajax always use parent.setOutputMarkupPlaceholderTag(true); instead of parent.setOutputMarkupId(true); because otherwise you won't be able to make it visible again - there won't be an HTML DOM element to be replaced with the given id.

Related

Global footer in Ionic

It seems impossible to create a global footer in an Ionic app.
Within a single page, you can use <ion-footer> and in turn, the <ion-content> component resizes itself using its resize() function, accounting for contained headers and footers, and global <ion-tab> allowing for tabs.
Having a <ion-footer> in app.html will not resize the content accordingly, causing for the footer to overlay the content.
Before I submit a pull request to the Ionic Framework's Content.resize method, does anyone know of a way to achieve a global footer?
If you know the height of the footer, you can style the .ion-page height to calc(100% - #{$global-footer-height}).
Example where global footer can be toggled on/off:
app.component.ts
#HostBinding('class.has-global-footer') public globalFooterEnabled: boolean = true;
constructor(platform: Platform, statusBar: StatusBar) {
// You can toggle the footer from a Service or something.
setTimeout(() => this.globalFooterEnabled = false, 5000);
// myService.somethingHappened$
.subscribe((toggle) => this.globalFooterEnabled = toggle);
}
app.html at the end:
<ion-footer class="global-footer" *ngIf="globalFooterEnabled">
Hello World!
</ion-footer>
app.scss
$global-footer-height: 50px;
.has-global-footer .ion-page {
height: calc(100% - #{$global-footer-height});
}
.global-footer {
height: $global-footer-height;
}

Resetting forms in Polymer 2.x

I am trying to reset my form. What am I doing wrong? What is best-practice?
Here is my Plunk demo.
My problem on the demo is that connectedCallback() appears to fire continually (not just on initial load), thereby losing the value of savedItem by updating it to newItem on each update.
Here is the same issue on Github.
https://plnkr.co/edit/wRdXXws2UXl3VXrycqua?p=preview
my-demo.html
<base href="https://polygit.org/polymer+v2.0.0/shadycss+webcomponents+1.0.0/components/">
<link rel="import" href="polymer/polymer-element.html">
<link rel="import" href="paper-toggle-button/paper-toggle-button.html">
<dom-module id="my-demo">
<template>
<style>
:host > * {
margin-top: 40px;
font-size: 18px;
}
button.save {
color: white;
background-color: blue;
}
</style>
<paper-toggle-button checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button checked="{{item.dave}}">Dave</paper-toggle-button>
<button>Reset</button>
<button class="save" on-tap="_reset">Save</button>
</template>
<script>
class MyDemo extends Polymer.Element {
static get is() {
return 'my-demo';
}
static get properties() {
return {
item: {
type: Object,
notify: true,
value: () => {
return {
alice: false,
bob: false,
charlie: false,
dave: true,
};
},
},
savedItem: {
type: Object,
notify: true,
},
};
}
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
static get observers() {
return [
'_itemChanged(item.*)',
];
}
_itemChanged(newItem) {
console.log('saved-item', this.savedItem);
console.log('new-item', newItem);
}
_reset() {
this.set('item', this.savedItem);
}
}
window.customElements.define(MyDemo.is, MyDemo);
</script>
</dom-module>
Edit
Steps to recreate the problem
Open the demo here.
Open your console.
Navigate in the Plunker to my-demo.html
Click one of the toggle switches.
Notice in the console, the savedItem property updates to the current item property.
Notice, this appears to be the result of the following code block.
connectedCallback() {
super.connectedCallback();
this.set('savedItem', this.item);
}
But how can this be? Because I thought connectedCallback() only fired once at initialization time?
tldr; The connectedCallback() isn't actually being called more than once in this case. savedItem and item are always the same object in your code because JavaScript passes objects by reference.
Object references
In the following:
connectedCallback() {
this.set('savedItem', this.item);
}
_reset() {
this.set('item', this.savedItem);
}
savedItem and item are both references to the same object. Calling this.set() does not automatically clone the operand (nor does the = operator).
One solution is to clone the object before assignment (using ES2017 object-spread operator):
connectedCallback() {
this.savedItem = {...this.item};
}
_reset() {
this.item = {...this.savedItem};
}
updated plunker
Best practice (or simpler reset method)
A simpler way to reset the form is to let iron-form handle the form's reset event, where it resets the form's named inputs to their initial values. This saves you from having to declare savedItem and no extra JavaScript to manage it.
To accomplish this, wrap the <paper-toggle-button>'s in an <iron-form>, and add name attributes to them. Then, insert an <input type="reset"> in the form, which serves as the reset button.
<iron-form>
<form>
<paper-toggle-button name="alice" checked="{{item.alice}}">Alice</paper-toggle-button>
<paper-toggle-button name="bob" checked="{{item.bob}}">Bob</paper-toggle-button>
<paper-toggle-button name="charlie" checked="{{item.charlie}}">Charlie</paper-toggle-button>
<paper-toggle-button name="dave" checked="{{item.dave}}">Dave</paper-toggle-button>
<input type="reset" class="save">
</form>
</iron-form>
demo

Detect scrollHeight change with MutationObserver?

How can I detect when scrollHeight changes on a DOM element using MutationObserver? It's not an attribute and it isn't data either.
Background: I need to detect when a scrollbar appears on my content element, the overflow-y of which is set to auto. I figured that the instant the scrollbar appears the value of scrollHeight jumps from 0 to, say, 500, so the idea was to set up a MutationObserver to detect a change in this property.
What I've got so far:
HTML
<div class="body" #body>
CSS
.body {
overflow-y: auto;
}
TypeScript
export class MyWatchedContent implements AfterViewInit, OnDestroy {
#ViewChild('body', { read: ElementRef })
private body: ElementRef;
private observer: MutationObserver;
public ngAfterViewInit() {
this.observer = new MutationObserver(this.observerChanges);
this.observer.observe(this.body.nativeElement, {
attributes: true,
});
}
public ngOnDestroy() {
this.observer.disconnect();
}
private observerChanges(records: MutationRecord[], observer: MutationObserver) {
console.log('##### MUTATION');
records.forEach((_record) => {
console.log(_record);
});
}
}
If I, for example, change the background color in the developer window I can see the observer firing
MUTATION
my-content-watcher.component.ts?d0f4:233 MutationRecord {type: "attributes", target: div.body, addedNodes: NodeList(0), removedNodes: NodeList(0), previousSibling: null…}
If, however, I change the window size to make the scrollbar appear there's no mutation detected. Is this doable with MutationObserver at all and if so, how?
Here's the answer, for anyone still looking for the solution:
As of today, it's not possible to directly monitor scrollHeight changes of an element.
The MutationObserver detects changes in the DOM tree, which could indicate a scrollHeight change, but that's a wild guess.
The ResizeObserver detects changes in the outer height of an element, but not the scrollHeight (i.e. "inner" height).
There is no ScrollHeight-Observer (yet).
BUT the solution is very close:
The Solution
The ResizeObserver detects changes in the outer height of an element...
There's no point in observing the scroll-container because its outer height does not change. The element that changes their out height is any CHILD node of the container!
Once the height of a child node changes, it means, that the scrollHeight of the parent container changed.
Vanilla JS version
const container = document.querySelector('.scrollable-container');
const observer = new ResizeObserver(function() {
console.log('New scrollHeight', container.scrollHeight);
});
// This is the critical part: We observe the size of all children!
for (var i = 0; i < container.children.length; i++) {
observer.observe(container.children[i]);
})
jQuery version
const container = $('.scrollable-container');
const observer = new ResizeObserver(function() {
console.log('New scrollHeight', container[0].scrollHeight);
});
container.children().each(function(index, child) {
observer.observe(child);
});
Further steps
When children are added dynamically, you could add a MutationObserver to add new children to the ResizeObserver once they were added.
You can emulate this behavior by adding an internal wrapping element on the contenteditable element (eg a span) and then add the ResizeObserver listener on the internal element. The internal span has to be display:block otherwise it wont trigger the ResizeObserver.
HTML
<div contenteditable id="input"><span id="content">Some content</span></div>
CSS
#input {
max-height: 100px;
overflow: scroll;
white-space: pre-wrap;
}
#content {
display: block;
white-space: pre-wrap;
}
JS
const content = document.getElementById("content");
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
console.warn(entry);
}
});
observer.observe(content);

Does codemirror provide Cut, Copy and Paste API?

From http://codemirror.net/doc/manual.html, I only find getRange(),
undo(), redo() etc, and I can't find cut(), copy() and paste API,
and more when I try to run editor.execCommand("cut"), I get the error.
Could you help me? Thanks!
Using clipboard.js, you can define the text() function to grab the value of the CodeMirror's inner document.
Store a reference to the (<textarea>) editor's selector for convenience.
var editorSelector = '#editor' // or '#editor + .CodeMirror';
Instantiate a new ClipBoard object with reference to your button.
new Clipboard('.clip-btn-native', {
text: function(trigger) {
return getCodeMirrorNative(editorSelector).getDoc().getValue();
}
});
Retrieve a CodeMirror Instance via native JavaScript.
function getCodeMirrorNative(target) {
var _target = target;
if (typeof _target === 'string') {
_target = document.querySelector(_target);
}
if (_target === null || !_target.tagName === undefined) {
throw new Error('Element does not reference a CodeMirror instance.');
}
if (_target.className.indexOf('CodeMirror') > -1) {
return _target.CodeMirror;
}
if (_target.tagName === 'TEXTAREA') {
return _target.nextSibling.CodeMirror;
}
return null;
};
Demo
Please see complete; in-depth demo over at JSFiddle.
There are no CodeMirror APIs for cut/copy/paste because browser security restrictions forbid JavaScript from accessing the clipboard programmatically. Paste could be used to steal private data and Cut/Copy can be used as a more elaborate attack vector.
The browser's own native code handles user gestures that access the clipboard (keyboard shortcuts and context menu items), based solely on the currently selected text or focused text field.
This SO thread has a good summary of attempts to work around these restrictions. CodeMirror's approach is the first bullet: it uses a hidden textarea to ensure that user clipboard gestures work, but that still doesn't support programmatic APIs.
But there is a partial workaround: use a small Flash widget (this is the 2nd bullet in the thread above). Flash relaxes the restrictions on Copy/Cut (but not Paste) a bit. It still has to be triggered by some user event, but it could be something like clicking a button in your HTML UI. Wrappers like ZeroClipboard and Clippy make it simple to access to these capabilities without needing to know Flash. You'd need to write a little glue code to pull the appropriate string from CodeMirror when copying, but it shouldn't be too bad.
Add a hidden contenteditable div to your textarea editor wrapper. Contenteditable divs respect new lines and tabs, which we need when copying code.
Here is my CodePen demo
var content = $('.content');
var toCopy = content.find('.copy-this');
// initialize the editor
var editorOptions = {
autoRefresh: true,
firstLineNumber: 1,
lineNumbers: true,
smartIndent: true,
lineWrapping: true,
indentWithTabs: true,
refresh: true,
mode: 'javascript'
};
var editor = CodeMirror.fromTextArea(content.find(".editor")[0], editorOptions);
content[0].editor = editor;
editor.save();
// set editors value from the textarea
var text = content.find('.editor').text();
editor.setValue(text);
// setting with editor.getValue() so that it respects \n and \t
toCopy.text(editor.getValue());
$(document).on('click', '.copy-code', function() {
var content = $(this).closest('.content');
var editor = content[0].editor;
var toCopy = content.find('.copy-this')[0];
var innerText = toCopy.innerText // using innerText here because it preserves newlines
// write the text to the clipboard
navigator.clipboard.writeText(innerText);
});
.content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.CodeMirror {
height: fit-content !important;
}
.copy-code {
background: #339af0;
width: fit-content;
cursor: pointer;
}
<!-- resources -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.css" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.0/mode/javascript/javascript.min.js"></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<!-- button to copy the editor -->
<div class="copy-code" title="copy code">copy</div>
<!-- add contenteditable div as it respects new lines when copying unlike textarea -->
<div class="copy-this" contenteditable style="display: none"></div>
<textarea class="editor" style="display: none;">// here is a comment
// here is another comment
</textarea>
</div>

iPhone Unlock Slider With Mootools?

I try to make a slider similar to the iPhone unlock Slider, that forwards to a linked site, when complete, and returns to its initial position when the slider wasn't dragged to the end.
This is not meant to be a iPhone webapp, i just want to put this to a general website as a effect.
So far I've tried those two tests, but i'm stuck on both.
The first is:
// First Example
var el = $('slideOne'),
// Create the new slider instance
var sliderOne = new Slider(el, el.getElement('.slider'), {
steps: 20, // There are 35 steps
range: [8], // Minimum value is 8
}).set(20);
Problem here is that i can't fire an event on (0) not on (20) aswell, i tried onComplete but this fires the function immediatly after the page is load!?
The second
$$('.textslider').setStyle('opacity', 0.8);
$('slider1').makeDraggable({
snap: 0,
container: 'slideOne',
droppables: '.slidedrop1',
onDrop: function(element, droppable, event){
if (!droppable);
else console.log(element, 'dropped on', droppable, location = 'index.php/kredite.html', event);
},
});
Problem here is , that the droppable don't work as fine as i hoped, sometimes i move the the slider on the invisible droppable, which indicates if the slider is dragged to the end, and nothing happens, sometimes it works fine, i think this may be due the different position of the cursor on the slider. and i can't get it done that it is only possible to slide horizontal , since it is that drag not the slider function, so i think this won be the proper way.
On both Tests, i didn't figured out who i could return the slider back to its initial position, with a slide Effect.
Are there some mootools cracks around who maybe could help me with this? Thanks already for the great ideas of y'all.
<html>
<head>
<script type="text/javascript"
src="http://demos.mootools.net/demos/mootools.js"></script>
<script type="text/javascript">
window.addEvent('domready', function(){
var el = $('slideOne');
var debug = $('debug');
var ACTIVATE_VALUE = 20;
var mySlider = new Slider(el, el.getElement('.knob'), {
range: [0], // Minimum value
steps: ACTIVATE_VALUE, // Number of steps
onChange: function(value){
if (value == ACTIVATE_VALUE) {
debug.setStyles({color:'green'});
// go to url after delay
(function(){
location.href='http://joecrawford.com/';
}).delay(500);
} else {
debug.setStyles({color:'red'});
// if not activated, reset after 2 second delay
(function(){
value = 0;
mySlider.set(value);
}).delay(3000);
}
debug.set('text', value);
}
});
mySlider.set(0);
});
</script>
<style type="text/css">
div.slider {
width: 200px;
height: 50px;
background: #eee;
}
div.slider div.knob {
background: #000;
width: 50px;
height: 50px;
}
div#debug {
font-family: sans-serif;
font-size: xx-large;
}
</style>
<title>Slider that resets if it does not reach the end</title>
</head>
<body>
<div id="slideOne" class="slider">
<div class="knob"></div>
</div>
<div id="debug">-</div>
</body>
</html>