angular-file-upload - how to make drop zone clickable? - file-upload

Using nv-file-upload (https://github.com/nervgh/angular-file-upload) how can I make the drop zone act also as a clickable element to select files? Adding {{nv-file-select}} does not seem to work.

The answer is that YOU CANT, there is no way to do this inside that plugin but i use a simple solution for this kind of problems. Add a ng-click inside your dragNdrop tag and call your function:
<div nv-file-drop="" uploader="upload" ng-click="launchFilePicker()">
<div class="drop-box" ng-show="upload.isHTML5" uploader="upload" nv-file-over="" over-class="dragover" filter="image/*,application/pdf">
Drag a file here.
</div>
</div>
<div ng-hide="upload.isHTML5"> <input id="fileDialog" type="file" nv-file-select uploader="upload"/><br/></div>
And inside your controller you do this:
$scope.launchFilePicker = function () {
//$('#fileDialog').click(); //not angular way
angular.element('#fileDialog').trigger('click'); //angular way
};
I hope this help.

Related

Using dynamic IDs in a string in a VueJS

I'm using a UIKit library for a tab component that listens to a uk-tab property that targets an id. The problem with this, is that it creates the same ID for every tabbed component. I like the UI, but whoever thought of this, didn't think too far into it. I could fix it by making the id dynamic but I am having trouble calling it in the uk-tab property because it is rendering a string. Coming from a react background, I would do a string literal and some JSX, something like #item-${_id}to show #item-12, #item-13....and so on. But That's not working. How can I do this in Vue?
Here is an example of how it works
<div class="mytrigger">
<ul uk-tab="connect: #component-tab-left; animation: uk-animation-fade">
</div>
<div class="mytargetedtab">
<ul id="component-tab-left" class="uk-switcher">
</div>
Here is an example of how what I need
<div class="mytrigger">
<ul uk-tab="connect: #_uid+'switcher'; animation: uk-animation-fade">
</div>
<div class="mytargetedtab">
<ul :id="_uid+'switcher'" class="uk-switcher">
</div>
Check out the dev tools. It should be 810switcher, but instead is taking it as a string
Any ideas? Thanks
I believe what you need is:
<ul :uk-tab="`connect: #${_uid}switcher; animation: uk-animation-fade`">
Or if you prefer not to use backticks:
<ul :uk-tab="'connect: #' + _uid + 'switcher; animation: uk-animation-fade'">
The output will be:
<ul uk-tab="connect: #22switcher; animation: uk-animation-fade">
A few notes:
Using a : is short for v-bind: but don't let the name confuse you. v-bind doesn't necessarily bind anything, it just makes the attribute value a JavaScript expression.
I'd avoid using numbers at the start of element ids, I've seen that cause problems in the past. It'd be better to put the numbers at the end.
The underscore at the start of _uid indicates that it's private to Vue. There are no guarantees about what form it will take or whether it will even exist going forward.
Use data-uk-tab instead of uk-tab like below.
<div class="mytrigger">
<ul data-uk-tab="{connect: `#${_uid}switcher`, animation: 'uk-animation-fade'}">
</div>
<div class="mytargetedtab">
<ul :id="_uid+'switcher'" class="uk-switcher">
</div>
For more information => Switcher with tabs
You can use any javascript expression in a data binding in vue. So, if you bind a string template to the attribute, it'll populate what you expect.
<ul :uk-tab="`connect: #${uid}switcher`'; animation: uk-animation-fade">

Add dynamic html without using compose inside the View

I need to add an html div element inside a custom component(footable).
Since the component is constructed at run time, I am not able to use compose anywhere in the html, therefor I must add my elements using jQuery.
Any ideas how can I add the below and bind to a ViewModel's method?
footable event:
'postinit.ft.table':function(e,ft){
$('.footable-filtering .form-inline').append('<div class="form-group"><a
href="#" click.delegate="refreshNow()" class="btn btn-primary"><i class="fa
fa-refresh"></i></a></div>');
}
I'd probably come at it a different way. I'd use jQuery's .on handler to attach a listener to the .footable-filtering element, seeing as it seems that's present from the start;
'postinit.ft.table':function(e,ft){
$('.footable-filtering .form-inline').append('<div class="form-group"><a
href="#" click.delegate="refreshNow()" class="btn btn-primary refreshButton"><i
class="fa fa-refresh"></i></a></div>');
}
$('.footable-filtering').on('click', '.refreshButton', (e) => {
e.preventDefault();
this.refreshNow();
}
This is untested - but should work.
Edt - It's also worth saying that combining Aurelia and jQuery in this way isn't ideal - but as you're using jQuery anyway to create the element it's use is passable.

Change element type at runtime

Is it possible to dynamically define the type of an element inside a custom components template at runtime?
I'd like to avoid duplication of the inner contents of the button and a element in the following example:
<template>
<button if.bind="!isLinkBtn">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</button>
<a if.bind="isLinkBtn">
<!--
The content is a 1:1 duplicate of the button above which should be prevented
somehow in order to keep the view DRY
-->
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</a>
</template>
Is it possible to write something like this:
<template>
<!--
The type of element should be defined at runtime and can be a standard HTML "button"
or an anchor "a"
-->
<element type.bind="${isLinkBtn ? 'a' : 'button'}">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</element>
</template>
I'm aware of dynamic composition with <compose view="${widget.type}-view.html"></compose> but as far as I know, this won't allow me to create default HTML elements but only custom components, correct?
I've asked this question on the Aurelia Gitter where Erik Lieben suggested to use a #processContent(function) decorator, replace the content within the given function and return true to let Aurelia process it.
Unfortunately I don't know how to actually apply those instructions and am hoping for some alternative approaches here or some details about how to actually accomplish this.
Edit
I've created a corresponding feature request. Even though possible solutions have been provided, I'd love to see some simpler way to solve this ;)
When you want to reuse HTML snippets, use compose. Doing so does not create a new custom element. It simply includes the HTML at the location of each compose element. As such, the view-model for the included HTML is the same as for the element into which it is composed.
Take a look at this GistRun: https://gist.run/?id=36cf2435d39910ff709de05e5e1bedaf
custom-link.html
<template>
<button if.bind="!isLinkBtn">
<compose view="./custom-link-icon-and-text.html"></compose>
</button>
<a if.bind="isLinkBtn" href="#">
<compose view="./custom-link-icon-and-text.html"></compose>
</a>
</template>
custom-link.js
import {bindable} from 'aurelia-framework';
export class CustomLink {
#bindable() contentText;
#bindable() icon;
#bindable() isLinkBtn;
}
custom-link-icon-and-text.html
<template>
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</template>
consumer.html
<template>
<require from="./custom-link"></require>
<custom-link content-text="Here is a button"></custom-link>
<custom-link is-link-btn.bind="true" content-text="Here is a link"></custom-link>
</template>
You may want to split these into separate elements, like <custom-button> and <custom-link> instead of controlling their presentation using an is-link-btn attribute. You can use the same technique to reuse common HTML parts and composition with decorators to reuse the common code.
See this GistRun: https://gist.run/?id=e9572ad27cb61f16c529fb9425107a10
Response to your "less verbose" comment
You can get it down to one file and avoid compose using the techniques in the above GistRun and the inlineView decorator:
See this GistRun: https://gist.run/?id=4e325771c63d752ef1712c6d949313ce
All you would need is this one file:
custom-links.js
import {bindable, inlineView} from 'aurelia-framework';
function customLinkElement() {
return function(target) {
bindable('contentText')(target);
bindable('icon')(target);
}
}
const tagTypes = {button: 'button', link: 'a'};
#inlineView(viewHtml(tagTypes.button))
#customLinkElement()
export class CustomButton {
}
#inlineView(viewHtml(tagTypes.link))
#customLinkElement()
export class CustomLink {
}
function viewHtml(tagType) {
let result = `
<template>
<${tagType}${tagType === tagTypes.link ? ' href="#"' : ''}>
<span class="btn-icon">\${icon}</span>
<span class="btn-text">\${contentText}</span>
</${tagType}>
</template>
`;
return result;
}
Sorry, I was doing 2 things at once while looking at gitter, which I am not good at apparently :-)
For the thing you wanted to accomplish in the end, could this also work?
I am not an a11y expert or have a lot of knowledge on that area, but from what I understand, this will accomplish what you want. The browser will look at the role attribute and handle it as a link or button and ignores the actual element type itself / won't care if it is button or anchor it will act as if it is of the type defined in role.
Then you can style it like a button or link tag with css.
<a role.bind="type"><span>x</span><span>y</span></a>
where type is either link or button, see this: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role

Upload file to hidden input using protractor and selenium

I've got a hidden file input field like this:
<input type="file" id="fileToUpload-1827" multiple="" onchange="angular.element(this).scope().setFiles(this)" data-upload-id="1827" class="hidden-uploader">
I'd like to be able to upload files to this. The normal way to do this in protractor would be to do:
ptor.findElement(protractor.By.css('.file-upload-form input')).sendKeys('/path/to/file')
But because the input element isn't visible, I get an error.
I tried:
ptor.driver.executeScript("return $('.file-upload-form input')[0].removeClass('hidden-uploader');").then(function () {
ptor.findElement(protractor.By.css('.file-upload-form input')).sendKeys('hello');
})
But got the error
UnknownError: $(...)[0].removeClass is not a function
It seems ridiculous to have to use executeScript to make an element visible so that I can upload a file, is there a better way? If not, how do I unhide the element?
The full html for the input form is:
<form class="file-upload-form ng-scope ng-pristine ng-valid" ng-if="ajaxUploadSupported">
<strong>Drag files here to upload</strong> or
<label for="fileToUpload-1953">
<div class="btn btn-info select-file-btn">
Click to Select
</div>
</label>
<div>
<input type="file" id="fileToUpload-1953" multiple="" onchange="angular.element(this).scope().setFiles(this)" data-upload-id="1953" class="hidden-uploader">
</div>
</form>
The only way I could find to do this in the end was to use javascript to make the input element visible.
So I have a function unhideFileInputs:
var unhideFileInputs = function () {
var makeInputVisible = function () {
$('input[type="file"]').removeClass('hidden-uploader');
};
ptor.driver.executeScript(makeInputVisible);
}
This contains the function 'makeInputVisible' which is executed in the browser when I call ptor.driver.executeScript(makeInputVisible). Because I know my page contains jQuery I can use the jQuery removeClass method to unhide my file input element.
To see more on how to execute javascript in the browser using webdriver, see the answer to this question (although the answer uses executeAsyncScript rather than executeScript).
To add on user2355213s answer for the more current releases of protractor. ptor is obsolote and instead browser should be used. Also, executeScript() expects a string as parameter. So I ended up using
browser.executeScript('$(\'input[type="file"]\').attr("style", "");');
as my visibility setting was directly applied to the element. Of course, you can also use
browser.executeScript('$(\'input[type="file"]\').removeClass("hidden-uploader");');
depending on your HTML/CSS.

JavaScript .innerHTMLworking only when called manually

I've got a very simple function, of replacing the innerHTML of a element. I've been trying to debug this for hours but simply can't, and it's infuriating.
When called from a button press the JavaScript (as follows) works well, but when called from another function it doesn't work. I am totally lost as to why this might be, and its a fairly core part of my app
// This loaded function in my actual code is a document listener
// checking for when Cordova is loaded which then calls the loaded function
loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
Button press and HTML to replace
<div id="main">
<input type='button' onclick='changeText()' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
It is also here in full on JSFiddle
You are already changed the innerHTML by calling the function loaded(); on onLoad.
Put this in an empty file and same as .html and open with browser and try. I have commented the function loaded();. Now it will be changed in onclick.
<div id="main">
<input type='button' onclick='changeText();' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
<script>
//loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
</script>
The problem here is, that the element you're trying to manipulate is not yet existing when you are calling the changeText() function.
To ensure that the code is only executed after the page has finished loading (and all elements are in place) you can use the onload handler on the body element like this:
<body onload="loaded();">
Additionally you should know, that it's very bad practice to manipulate values by using the innerHTML property. The correct way is to use DOM Manipulations, maybe this can help you.
You script loads before the element (boldStuff) is loaded,
Test Link - 1 - Put the js in a seperate file
Test Link - 2 - put the js at the very end, before closing the <body>