I am trying to implement drag and drop files from desktop using knockout. The starting code is taken from html5rocks.
I tried to implement this using event binding, so my View looks something like this:
<div class="drop_zone" data-bind="event:{
dragover: function(data, e){ $root.dragover(e);},
drop: function(data, e){ $root.drop(e, $parent);},
dragenter: function(data, e){ $root.dragenter(e);},
dragleave: function(data, e){ $root.dragleave(e);}
}">Drop files here</div>
The $parent parameter was used in attempt to do something similar to my previous question, where parent was able to locate where exactly the element should be removed.
My ViewModel is an observableArray of observableArrays (many dropzones) and looks like this:
this.dropZones = ko.observableArray([{
'elements' : ko.observableArray([])
},{
'elements' : ko.observableArray([])
}]);
The full code can be found in jsFiddle, but the problem is that I can not properly add new files to the files element. Also I can not correctly highlight the element the person is dragEntering/Leaving.
I understand why I can not highlight the proper element (I just select every class, but I can not understand how to select the parent element), I am failing to understand why parent.elements.push(f.name); does not add the name of the file to the right parent.
Can anyone please tell me what is the problem and how can I fix it?
P.S. in jsFiddle I get the error:
TypeError: Cannot read property 'dataTransfer' of undefined
which tells me that I am passing wrong event, but the same code on my local server does not give me this problem. The error which I am getting on localhost is:
Uncaught TypeError: Cannot call method 'push' of undefined
which tells me that my idea of using parent was wrong.
1) { $root.drop(e, $data);} instead of { $root.drop(e, $parent);}
2) var files = e.dataTransfer.files; (without originalEvent)
Fiddle. (I've not fixed problem with css, do it youself :))
You should use $data instead of $parent because of elements is the property of each dropZone element. So, when you iterate with foreach: dropZones you have access to current element by $data, and you should send to $root.drop function current element (not parent) for get access to it elements array.
Update:
Solved CSS problem. (with help of $index() and jQuery .eq())
You could read about binding context (parent, data, index and etc.) here.
Related
In an angular project, I need to test that the displayed table width of the primeng data table is set to the maxWidth value i assign to it. To do so, i want to call the [style] attribute to get the width and see if its equal to my maxWidth. However, i do not know how to call attributes like this. How do i go about this? Currently i have no clue if I'm going in the correct direction.
I have tried several things but I am not sure of the syntax for it.
<p-table class="p-table" ... [style] = "{width: maxWidth}" >
it('should implement maxwidth', () => {
const widthDebug: DebugElement = fixture.debugElement;
const tableWidth = widthDebug.query(By.css('.ui-table .ui-widget'));
const ptable: HTMLElement = tableWidth.nativeElement;
expect(ptable.textContent).toContain("width: " + component.maxWidth);
});
expected: success (ptable.textContent contains "width: component.maxWidth")
actual: TypeError: cannot read property 'nativeElement' of null
I see that it's now two months after you asked your question, so it's probably too late for my answer to help, but I stumbled across this post while looking up something else about PrimeNG, so I might as well give it a shot.
The problem here is that nativeElement is defined on Dialog class instances of the Angular p-table component. It's not defined on any particular DOM element.
By.css('.ui-table .ui-widget') is going to find a DOM element for you, not an Angular class instance. In particular what will be found is a <div> inside the <p-dialog> DOM element, and it's this <div> that receives the style set via [style]=....
As your code is written above tableWidth.style.width would contain (as a string) the value of maxWidth that you're expecting to find.
I am using the library dragula for doing some drag & drop stuff.
Dragula internally uses cloneNode(true) to create a copy of the dragged element that will be appended to the body to show the preview image while dragging.
Unfortunately, if dragging a polymer element, the bound data get's not cloned. By consequence the contents of the dragged element (e.g. <div>[[someString]]</div>) are empty.
Is there a solution for this?
I actually do not need the data to be bound for my element, it is just a "read-only" element that displays some data that does not change after being initialized. Is there maybe a way to somehow "resolve" the strings to the html without being bound anymore?
Thank you already!
Found a solution myself. You have to override the cloneNode method inside the polymer class:
cloneNode(deep) {
let cloned = super.cloneNode(deep);
for (let prop in MyClass.properties) {
cloned[prop] = this[prop];
}
return cloned;
}
I'm following this tutorial about Ionic and directives and everything works fine except when I try to get the FAB element using ElementRef's nativeElement.getElementsByClassName, like this:
this.fab = this.element.nativeElement.getElementsByClassName('fab')[0]
That returns undefined. The problem is when I remove the index and print the whole HTMLCollection using console.log, it shows me a complete list with all the FAB's inside the element.
Running
console.log(this.element.nativeElement.getElementsByClassName('fab'),
this.element.nativeElement.getElementsByClassName('fab')[0]);
on ngOnInit gives the following result:
What am I doing wrong here? Every part of the code related to the problem is equal to the tutorial and it's a quite recent video...
I think the reason here is that those elements are not present while you asking for them with that line:
console.log(this.element.nativeElement.getElementsByClassName('fab'),
this.element.nativeElement.getElementsByClassName('fab')[0]);
There is simple example which shows where problem can be:
console.log(document.getElementsByClassName('fab'), document.getElementsByClassName('fab')[0]);
const el1 = document.createElement('div');
el1.setAttribute('class', 'fab');
const el2 = document.createElement('div');
el2.setAttribute('class', 'fab');
setTimeout(() => {
this.abc.nativeElement.appendChild(el1);
this.abc.nativeElement.appendChild(el2);
}, 2000);
Elements are added after 2 seconds and console log is same like yours, but when you click on HTMLCollection it will evaluate and shows you those elements - of course if you click after 2 seconds(when elements are present).
If those element are really present when you asking for them console log should look more like:
HTMLCollection(2) [div.fab, div.fab]
Also, note that this little i in Google Chrome console inform you that value is evaluted just now - at the moment when you click on it.
The below screenshot shows how my iron-data-table element looks when it first loads. Notice the data is missing and the headers are not fully formatted (the width seems to be too small to show the column header labels).
However, if I enter a character in the filter fields or click one of the sort buttons, the iron-data-table seems to re-draw itself and it looks fine. The columns and headers render properly and the data populates the table as expected.
This problem started when I added some directories to my file structure nested my iron-data-table inside neon-animated-pages. I double checked and all my imports are loading correctly. But I suspect that, somehow, there is a slight delay in fetching the data and somehow the table might be trying to render before the data arrives and not refreshing after the data arrives.
So I tried to add the following function. But to no avail. There is no render() method for iron-data-table.
<iron-data-table id="grid" ...
....
attached: function() {
this.async(function() {
this.$$('#grid').render();
}.bind(this), 500);
},
What could be causing this behavior? And what should I try next?
Edit
This plunk demonstrates the desired iron-data-table behavior.
This plunk demonstrates my problem behavior. (Not finished yet. Currently in progress.)
The configuration is approximately as follows.
parent-el.html
<neon-animated-pages>
...
<container-el...>...</container-el>
</neon-animated-pages>
container-el.html
<child-el ...>...</child-el>
child-el.html
<iron-data-table ...></iron-data-table>
Edit 2
Based on #SauliTähkäpää's comment, I tried the following fix unsuccessfully.
parent-el.html
<neon-animated-pages id="animator" ...>
...
<container-el...>...</container-el>
</neon-animated-pages>
...
attached: function() {
this.async(function() {
this.$.animator.notifyResize();
}.bind(this), 50);
},
Not that familiar with neon-animated-pages but by looking at the docs, it should call notifyResize() on selected pages as long as they extend iron-resizable-behavior.
So I think your options are either to make sure <container-element> and other elements in the tree are extending iron-resizable-behavior OR override the resizerShouldNotify to notify the grid directly. See more here: https://elements.polymer-project.org/elements/neon-animation?active=neon-animated-pages#method-resizerShouldNotify
To explicate for anyone who follows this question later...
Per #SauliTähkäpää's accepted answer, I successfully added the following code to each element in the chain (i.e., beginning with the element containing neon-animated-pages and ending with the element containing iron-data-table — in this example, that would be parent-el, container-el, and child-el).
parent-el.html, container-el.html, child-el.html.
<link rel="import" href="path/to/bower_components/iron-resizable-behavior/iron-resizable-behavior.html">
...
behaviors: [
Polymer.IronResizableBehavior,
],
bangin' my head against this and it's starting to hurt.
I'm having trouble with adding an event to an element.
I'm able to add the event, and then call it immediately with element.fireEvent('click'), but once the element is attached to the DOM, it does not react to the click.
example code:
var el = new Element('strong').setStyle('cursor','pointer');
el.addEvent('click',function () { alert('hi!'); });
el.replaces(old_element); // you can assume old_element exists
el.fireEvent('click'); // alert fires
however, once I attach this to the DOM, the element is not reactive to the click. styles stick (cursor is pointer when I mouseover), but no event fires. tried mouseover as well, to no avail.
any clues here? am I missing something basic? I am doing this all over the place, but in this one instance it doesn't work.
EDIT----------------
ok here's some more code. unfortunately I can't expose the real code, as it's for a project that is still under tight wraps.
basically, the nodes all get picked up as "replaceable", then the json found in the rel="" attribute sets the stage for what it should be replaced by. In this particular instance, the replaced element is a user name that should pop up some info when clicked.
again, if I fire the event directly after attaching it, all is good, but the element does not react to the click once it's attached.
HTML-----------
<p>Example: <span class='_mootpl_' rel="{'text':'foo','tag':'strong','event':'click','action':'MyAction','params':{'var1': 'val1','var2': 'val2'}}"></span></p>
JAVASCRIPT-----
assumptions:
1. below two functions are part of a larger class
2. ROOTELEMENT is set at initialize()
3. MyAction is defined before any parsing takes place (and is properly handled on the .fireEvent() test)
parseTemplate: function() {
this.ROOTELEMENT.getElements('span._mootpl_').each(function(el) {
var _c = JSON.decode(el.get('rel'));
var new_el = this.get_replace_element(_c); // sets up the base element
if (_c.hasOwnProperty('event')) {
new_el = this.attach_event(new_el, _c);
}
});
},
attach_event: function(el, _c) {
el.store(_c.event+'-action',_c.action);
el.store('params',_c.params);
el.addEvent(_c.event, function() {
eval(this.retrieve('click-action') + '(this);');
}).setStyle('cursor','pointer');
return el;
},
Works just fine. Test case: http://jsfiddle.net/2GX66/
debugging this is not easy when you lack content / DOM.
first - do you use event delegation or have event handlers on a parent / the parent element that do event.stop()?
if so, replace with event.preventDefault()
second thing to do. do not replace an element but put it somewhere else in the DOM - like document.body's first node and see if it works there.
if it does work elsewhere, see #1
though I realsie you said 'example code', you should write this as:
new Element('strong', {
styles: {
cursor: "pointer"
},
events: {
click: function(event) {
console.log("hi");
}
}
}).replaces(old_element);
no point in doing 3 separate statements and saving a reference if you are not going to reuse it. you really ought to show the ACTUAL code if you need advice, though. in this snippet you don't even set content text so the element won't show if it's inline. could it be a styling issue, what is the display on the element, inline? inline-block?
can you assign it a class that changes it on a :hover pseudo and see it do it? mind you, you say the cursor sticks which means you can mouseover it - hence css works. this also eliminates the possibility of having any element shims above it / transparent els that can prevent the event from bubbling.
finally. assign it an id in the making. assign the event to a parent element via:
parentEl.addEvent("click:relay(strong#idhere)", fn);
and see if it works that way (you need Element.delegate from mootools-more)
good luck, gotta love the weird problems - makes our job worth doing. it wouldn't be the worst thing to post a url or JSFIDDLE too...