Why do dropzone options fail to load? - asp.net-core-2.0

I have a dropzone on my view and would like to set some options to force the files to be .zip. I also need to allow for these files to be bigger and get some information when it returns. The options don't seem to be loading for me and quecomplete never gets hit.
Edit.cshtml:
<div class="row">
<div class="col-8">
<form asp-action="UploadFiles" class="dropzone" id="versionFiles">
</form>
</div>
</div>
#section Scripts {
<script>
$(document).ready(function () {
Dropzone.options.versionFiles = {
acceptedFiles: ".zip",
maxFileSize: 2048,
timeout: 600000,
init: function () {
this.on("queuecomplete", function (file, response) {
console.log(file);
})
}
};
})
</script>
}
Edit: Fixed timeout

Avoid configuring options for Dropzone inside document.ready(function(){ /* ... */ }).
To fix the issue, change your code as below :
$(document).ready(function () {
Dropzone.options.versionFiles = {
acceptedFiles: ".zip",
maxFileSize: 2048,
timeout: 600000,
init: function () {
this.on("queuecomplete", function (file, response) {
console.log("ssssssssssssss",file);
})
}
};
});
[Edit] :
The reason is the Dropzone.js will automatically discover all form elements with the class dropzone and automatically attach itself to it. If you configure the options by document.ready(function(){/.../}), you cannot guarantee the options is set before dropzone takes effects.
If you do need to trigger after document is ready, you could use a programmatical way to assure the sequence:
Dropzone.autoDiscover = false; // disable auto discover
$(document).ready(function () {
Dropzone.options.versionFiles = {
acceptedFiles: ".zip",
maxFileSize: 2048,
timeout: 600000,
init: function () {
this.on("queuecomplete", function (file, response) {
console.log("xyz...",file);
})
}
};
$("#versionFiles").dropzone({ }); // trigger it
});

Related

How to get the path to the directory of the opened file?

I'm Kenyon Bowers.
I have some code that opens a open file dialog. It opens .DSCProj (which are specific to my project), and I am going to run some terminal commands in the directory that the opened file is in.
I have no idea how to do that.
preload.ts:
import { ipcRenderer, contextBridge } from "electron";
import { dialog } from '#electron/remote'
contextBridge.exposeInMainWorld("api", {
showOpenFileDialog: () => dialog.showOpenDialogSync({
properties: ["openFile"],
filters: [
{
name: "DSC Projects",
extensions: ["DSCProj"],
},
],
})
});
NewProject.ts:
declare var api: any;
function OpenProject(): void {
const file = api.showOpenFileDialog();
console.log("Done")
if(file != null){
localStorage.setItem('DirPath', file);
location.href='./views/projectOpen.html'
}
}
(() => {
document.querySelector('#btn-open-project')?.addEventListener('click', () => {
OpenProject();
}),
document.querySelector('#btn-new-project')?.addEventListener('click', () => {
location.href='./views/projectNew.html'
})
})()
As you can see on line 7, I'm setting local storage to the file's path. But I need to set it to the path of the directory that the file is in.
Whilst use of #electron/remote is great and all, you may be better served by implementing certain Electron modules within the main thread instead of the render thread(s). This is primarily for security but as a strong second reason, it keeps your code separated. IE: Separation of concerns.
Unlike vanilla Javascript, node.js has a simple function path.parse(path).dir to easily remove the file name (and extension) from the file path without needing to worry about which OS (IE: Directory separator) you are using. This would also be implemented within your main thread. Implementing something like this within your render thread would take a lot more work with vanilla Javascript to be OS proof.
Lastly, in the code below I will use a preload.js script that only deals with the movement of messages and their data between the main thread and render thread(s). I do not believe that the concrete implementation of functions in your preload.js script(s) is the right approach (though others may argue).
Note: I am not using typescript in the below code, but you should get the general idea.
Let's use the channel name getPath within the invoke method.
preload.js (main thread)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'getPath'
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Now, in the main thread, let's listen for a call on the getPath channel. When called, open up the dialog and upon the return of a path, process it with Node's path.parse(path).dir function to remove the file name (and extension). Lastly, return the modified path.
main.js (main thread)
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// -----
// Let's listen for a call on the 'getPath' channel
electronIpcMain.handle('getPath', async () => {
// Dialog options.
const options = {
properties: ["openFile"],
filters: [
{
name: "DSC Projects",
extensions: ["DSCProj"],
}
]
}
// When available, return the modified path back to the render thread via IPC
return await openDialog(window, options)
.then((result) => {
// User cancelled the dialog
if (result.canceled === true) { return; }
// Modify and return the path
let path = result.filePaths[0];
let modifiedPath = nodePath.parse(path).dir; // Here's the magic.
console.log(modifiedPath); // Testing
return modifiedPath;
})
})
// Create an open dialog
function openDialog(parentWindow, options) {
return electronDialog.showOpenDialog(parentWindow, options)
.then((result) => { if (result) { return result; } })
.catch((error) => { console.error('Show open dialog error: ' + error); });
}
Here you will get the general idea about how to use the returned result.
index.html (render thread)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Open Project Test</title>
</head>
<body>
<input type="button" id="btn-open-project" value="Open Project">
</body>
<script>
document.getElementById('btn-open-project').addEventListener('click', () => {
openProject();
});
function openProject() {
window.ipcRender.invoke('getPath')
.then((path) => {
// As we are using "invoke" a response will be returned, even if undefined.
if (path === undefined) { return; } // When user cancels dialog.
console.log(path); // Testing.
// window.localStorage.setItem('DirPath', path);
// location.href='./views/projectOpen.html';
});
}
</script>
</html>

AJAX call is broken, NodeJS, Express, Handlebars

I've been working on this for a couple of days. I'm certain its something really stupid, but I'm at the end of my sanity.
The public files are set up properly.
Error Message:
Uncaught ReferenceError: togDefine is not defined
Front End HTML:
<li class="list-group-item list-group-item-dark">
<div class="row">
<div class="col-md-4"><strong>Heating:</strong> {{#if heating}} {{this.heating}} {{else}} N/A {{/if}}</div>
<div class="col-md-4"><strong>Cooling:</strong> {{#if cooling}} {{this.cooling}} {{else}} N/A {{/if}}</div>
<div class="col-md-4">
<input type="checkbox" id="pvt{{this.id}}" checked="{{this.private}}" onchange="togDefine({{this.id}}, {{this.private}});" data-toggle="toggle" data-on="Private" data-off="Public" data-onstyle="success" data-offstyle="danger" />
</div>
</div>
AJAX Call:
$(function() {
// Private-Public toggle
let togDefine = (id, pvt) => {
$.ajax({
type: "POST",
url: "/api/pvtToggle",
data: {
id: id,
newState: (pvt === 'true') ? false : true
},
success: function(text) {
if (text === 'ok') {
pvtSuccess(id, pvt);
} else {
console.log('updatePvt failed');
}
}
});
};
let pvtSuccess = (id, pvt) => {
$('#pvt' + id).attr('checked', (pvt === 'true') ? 'false' : 'true');
};
});
Back End:
//TOGGLE Private vs Public PROPERTY
app.put('/api/pvtToggle/', isAuthenticated, function(request, response) {
db.Prop.update({
private: request.params.newState
}, {
where: {
id: request.params.id
}
}).then(data => {
response.send('ok');
}).catch(error => {
console.log(error);
});
});
Please help me figure out why the request isn't working properly. :D
Your function togDefine() is defined inside this block:
$(function() { /* in here */ })
Therefore, that function name is only available inside that block and is not available to your HTML. Since just defining a function doesn't actually execute anything, there is really no reason to define a function inside of that kind of block unless you ONLY want the symbol available inside that block.
Since you explicitly don't want that here, just move the definition of togDefine() outside that block.

How to Read Excel file in a web-worker in a VueJS Project

Currently i have a Excel file having 150,000+ rows and multiple sheets, and i am using read-excel-file library to read files synchronous by which the browser stuck! so i want to push that load on a different thread so the main thread can render UI flawlessly and thats why i want to use Web-Worker for that and currently i am in a VueJs project and i find a library vue-worker for the web worker but got this error and cant find any solution.
Error: Failed to execute 'postMessage' on 'Worker': function readXlsxFile(file) { ...
Here is a mini example of it:
URL: https://codesandbox.io/embed/vue-template-u8swi?fontsize=14
<template>
<div id="app">
<input type="file" #change="onFIleChange">
</div>
</template>
<script>
import readXlsxFile from "read-excel-file";
export default {
name: "App",
components: {},
data: () => {
return {
file: null,
rows: null
};
},
methods: {
onFIleChange(e) {
this.file = e.target.files[0];
this.$worker
.run(
(file, readXlsxFile) => {
return readXlsxFile(file);
},
[this.file, readXlsxFile]
)
.then(rows => {
this.rows = rows;
console.log(this.rows);
})
.catch(e => {
console.error(e);
});
}
}
};
</script>
<style>
</style>

How to implement a queue using RxJS?

I'm designing an API (in NodeJS) which receives a list of user ids, for instance, and access Google Firebase's API to send a notification to those users.
Considering Firebase having a quota limit on how many requests you can concurrently send to, I buffered the Ids and send the requests one by one to Firebase with a delay of two seconds. Here's some simplified sample code for explanation:
app.post('/send-request', (req, res, next) =>{
const userIds = req.body.userIds;
‎const streams = userIds.map((userId) => {
‎ return Observable
‎ .fromPromise(fetch(`firebase url`, {
‎ method: 'POST',
‎ headers: ...,
body: ...
‎ }))
‎ .delay(2000)
‎ .retryWhen(attempts => {
‎ return attempts.zip(Rx.Observable.range(1, 4))
.mergeMap(([error, i]) => {
if (i > 3) {
return Rx.Observable.throw(error);
}
console.log(`Wait ${i} seconds, then retry!`);
return Rx.Observable.timer(i * 1000);
});
‎ });
});
const stream = Observable.merge(...streams);
‎ stream.subscribe();
});
This can handle the case when a single request consists of a lot of users. However, if my API receives similar requests concurrently, it's bound to fail.
Therefore, I want to buffer all these user ids in a queue where this queue can keep receiving more and more user ids an buffer them, and at the same time 'top' the queue by sending requests to Firebase at a steady rate. However, I don't know how to do it with RxJS. Do I have to use an Scheduler? Or is there actually a better solution than using Rx?
Note: I understand Javascript is single-threaded so it's not exactly concurrency, I only used this word so you can get the idea better.
I think I managed to come up with something, the key part is to use Subject to publish values, then use the zip operator to emit value on a regular interval. It'd be even better if I can take the values on demand, but the current solution is already much better than my original method.
const subject = new Rx.Subject();
const stream = subject
.zip(Rx.Observable.interval(3000), function(a, b) { return a; });
stream.subscribe(
(x) => { console.log(`onNext: ${val}`); },
(e) => { console.log(`onError: ${e}`); },
() => { console.log('onCompleted'); });
I used VueJS to build a simple web page for a little demo.
const app = new Vue({
el: '#app',
data: {
subject: undefined,
stream: undefined,
count: 0,
emitHistory: [],
disableBtn: true
},
created() {
console.log('created');
this.subject = new Rx.Subject();
this.stream = this.subject
.zip(Rx.Observable.interval(3000), function(a, b) { return a; })
// .observeOn(Rx.Scheduler.queue); // not working
this.stream.subscribe(
(val) => {
console.log(`onNext: ${val}`);
this.emitHistory.push(val);
if (val === this.count) {
this.disableBtn = false;
} else {
this.disableBtn = true;
}
},
(e) => { console.log(`onError: ${e}`); },
() => { console.log('onCompleted'); });
},
methods: {
clickHandler() {
this.count++;
this.subject.onNext(this.count);
},
clear() {
this.count = 0;
this.emitHistory = [];
}
}
});
/*
Rx.Observable
.fromArray([1,2,3])
.zip(Rx.Observable.interval(500), function(a, b) { return a; })
.subscribe(
function(x) { document.write(x + '<br \>'); },
null,
function() { document.write("complete"); });
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.js"></script>
<div id="app">
<button #click="clickHandler()">Click me</button>
<button #click="clear()" v-bind:disabled="disableBtn">Clear</button>
<div>
<h5>Count: {{count}}</h5>
</div>
<div>
<ul>
<li v-for="(item, idx) in emitHistory" v-bind:key="idx">{{item}}</li>
</ul>
</div>
</div>

Cannot Get Typeahead.js Working with MVC 5 Over Remote

I have no idea what I'm doing wrong, but I cannot get typeahead working in my MVC 5 application. I installed everything via NuGet and my view includes #Scripts.Render("~/bundles/typeahead"), which is rendering properly when viewing the source of the view. So the issue isn't that the dependencies are missing.
I am not seeing any drop down appear when I start typing, and using Fiddler I do not see any calls being made out to the remote that I setup that pulls the data.
Here's the line in my view that typeahead is being attached:
#Html.TextBoxFor(m => m.MainInfo.CompanyName,
new { #class = "form-control typeahead", id = "comp-name", autocomplete="off" })
Here's the portion of my script that configures typeahead and bloodhound:
$(document).ready(function() {
var clients = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "/info/client?like=%QUERY",
wildcard: '%QUERY',
filter: function (clients) {
return $.map(clients, function (client) {
return {
value: client.Name,
clientId: client.Identifier
};
});
}
}
});
clients.initialize();
$('#comp-name').typeahead(null,
{
display: 'value',
minLength: 1,
source: clients.ttAdapter(),
templates: {
empty: "Looks like a new client...",
suggestion: Handlebars.compile("<p><b>{{value}}</b> - {{clientId}}</p>")
}
});
});
Is there something that I've configured wrong in my javascript? I've used a few tutorials as well as their own documentation, but I cannot figure out what I'm doing wrong here. It almost feels like it's not properly initialized, but there are no errors being thrown.
NOTE: Just as an FYI I'm using Bootstrap 3 as well in case that changes anything.
EDIT: Here's my #section Scripts:
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/typeahead")
<script src="#Url.Content("~/Scripts/handlebars.min.js")"></script>
<script src="#Url.Content("~/Scripts/ProjectSetupFormScripts.js")"></script> <-- this is where typeahead is set up
This did the trick for me:
JS
#section Scripts {
<script type="text/javascript">
$(function () {
SetupTipeahead();
});
function SetupTipeahead() {
var engine = new Bloodhound({
remote: {
url: '/Employees/AllEmployees',
ajax: {
type: 'GET'
}
},
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.FullName);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
engine.initialize();
$('#FullName').typeahead(null, {
displayKey: 'FullName',
source: engine.ttAdapter(),
templates: {
empty: [
'<div class="empty-message">',
'No match',
'</div>'
].join('\n'),
suggestion: function (data) {
return '<p class="">' + data.FullName + '</p><p class="">' + data.ManNumber + '</p>';
}
}
});
}
</script>
EmployeesController has the following JsonResult
public JsonResult AllEmployees()
{
return Json(db.Employees.ToList(),JsonRequestBehavior.AllowGet);
}
Hello try to wrap your script in #section scripts {} this will place the script at the bottom just before the </body> tag and make sure you are not calling the function before your bundles load.
#section scripts {
<script>
$(document).ready(function() {
var clients = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "/info/client?like=%QUERY",
wildcard: '%QUERY',
filter: function (clients) {
return $.map(clients, function (client) {
return {
value: client.Name,
clientId: client.Identifier
};
});
}
}
});
clients.initialize();
$('#comp-name').typeahead(null,
{
display: 'value',
minLength: 1,
source: clients.ttAdapter(),
templates: {
empty: "Looks like a new client...",
suggestion: Handlebars.compile("<p><b>{{value}}</b> - {{clientId}}</p>")
}
});
});
</script>
}

Resources