(I will left out some of the code in which I'm 100% sure in order to make the code more compact) I have a main view (the main view doesn't have any html-head-body because it is included in Layout)...
<div class="content-wrapper">
<div id="user-files-table-wrapper">
<!-- Partial view for user file management to be rendered there-->
</div>
</div>
<link rel="stylesheet" href="~/css/user-files-table-style.css"/>
#section Scripts
{
<script type="text/javascript" src="~/js/user-file-manager.js"></script>
}
..and a Partial view
<h3>User files ("Delete" and "Download" buttons can be clicked with invalid XML files only)</h3>
<table id="user-devices-table">
<tr>
<th>User file</th>
<th>Assigned email</th>
<th>Is valid</th>
<th>Delete button</th>
<th>Download button</th>
</tr>
#foreach (var userFileViewModel in Model)
{
#foreach (var file in userFileViewModel.UserFiles)
{
if (isValid == false && isXml)
{
<tr class="user-file-row invalid">
<td>#file.FileName</td>
<td>#userFileViewModel.Email</td>
<td>#file.IsValid</td>
<td>
<a class="btn btn-danger" asp-controller="Admin" asp-action="Delete" asp-route-userFileId="#file.UserFileID" asp-route-fileName="#file.FileName">Delete</a>
</td>
<td>
<input type="button" id="#file.FileName" class="btn btn-primary download-button" value="Download" />
<p class="download-result">File has been downloaded successfully</p>
</td>
</tr>
}
}
}
}
</table>
...and some JS:
$(document).ready(function () {
$(document).on("click", ".btn.btn-primary.download-button", function () {
const fileName = $(this).attr("id");
const clickedButtonRowIndex = $("input[type='button']").index(this);
downloadUserFile(fileName, clickedButtonRowIndex);
});
});
function downloadUserFile(fileName, rowIndex) {
$.ajax({
url: "/Admin/Download",
data: { fileName: fileName },
success: function (data) {
processProvidedFile(data, fileName);
showDownloadResult(rowIndex);
},
error: function (error) {
alert(`An error occured: ${error.responseText}`);
}
});
}
function processProvidedFile(data, fileName) {
fetch(data)
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch(() => alert("An error occured"));
}
function showDownloadResult(rowIndex) {
$(".download-result.active").removeClass("active");
$(".download-result").eq(rowIndex).addClass("active");
}
...and some CSS:
.download-result {
display: none;
}
.download-result.active {
display: block;
}
In general, my task is to download the file and show that the file has been successfully downloaded. Files are downloaded normally, no problem with it. My issue is that the showDownloadResult(rowIndex) function not working as expected. Chrome debugger hits it with a correct rowIndex, but neither remove an active class nor adding it. I checked the function in JSFiddle - it works as expected there, but not in my partial view. What am I doing wrong?
Related
I'm trying to load in data from the database dynamically with javascript and this helper function.
The function fetches a URLs which is routed to actions that return the parameters and a message after completion (as a Promise). The message gets displayed to the end user.
parasails register Page:
methods: {
loadData: async function(url) {
let origin = window.location.origin
url = new URL(origin + url);
try{
let res = await parasails.util.backendLoad(url)
console.log(res)
this.categories = res.categories
this.showResponse = res.showResponse
}catch(err){
console.log("Error: " + err)
}
},
destroyOne: async function(id) {
url = "/api/v1/category/" + id + "/destroy"
this.loadData(url);
}
}
parasails utility:
parasails.registerUtility('backendLoad', async function backendLoad(url) {
return new Promise((resolve)=>{
fetch(url)
.then((res)=>{
if (res.ok)return res.json();
throw new Error(res.status);})
.then((data)=>{
console.log(data);
resolve(data);
})
.catch((err)=>{
let resErr = []
switch(err.message){
case "401":
console.log("an error has occured of type 401");
resErr= {showResponse: {type: 'alert-danger', message "Unauthorized"}};
resolve(resErr);
default:
console.log("an error has occured");
resErr= {showResponse: {type: 'alert-danger', message: "Unkownerror"}};
resolve(resErr);
}
})
});
});
EJS:
<div id="indexCategory" v-cloak>
<div class="container my-2">
<div :class="'alert ' + showResponse.type" role="alert" v-if="showResponse">
{{showResponse.message}}
</div>
<h1>Categories</h1>
<hr/>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr scope="row" v-for="c in categories">
<td>
{{ c.name }}
</td>
<td>
<a class="btn btn-outline-secondary btn-sm" #click="destroyOne(c.id)">Delete</a>
</td>
</tr>
</tbody>
</table>
<a type="button" class="btn btn-primary mt-4" href="/category/new">Add</a>
<a type="button" class="btn btn-primary mt-4" href="/dashboard">Back to dashboard</a>
</div>
</div>
The function loadData is called beforeMount and returns all categories as it should.
When destroyOne calls the loadData function this doesnt happen. Neither the categories nor the message get shown. The action returns the parameters as expected (as a Promise).
There are no console outputs either when calling with destroyOne.
What changes do I need to implement, so that the response from destroyOne gets shown?
i am using vs code as ide.
this is the app code.
<template>
<div class="hello">
<!-- Select All records -->
<input type='button' #click='allRecords()' value='Select All users'>
<br><br>
<!-- Select record by ID -->
<input type='text' v-model='userid' placeholder="Enter Userid between 1 - 24">
<input type='button' #click='recordByID()' value='Select user by ID'>
<br><br>
<!-- List records -->
<table border='1' width='80%' style='border-collapse: collapse;'>
<tr>
<th>Username</th>
<th>Name</th>
<th>Email</th>
</tr>
<tr v-for='user in users'>
<td>{{ user.username }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
</tr>
</table>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'HelloWord',
data: {
users:"",
userid: 0
},
methods: {
allRecords: function () {
axios.get('api/ajaxfile.php')
.then(function (response) {
app.users = response.data;
})
.catch(function (error) {
console.log(error);
});
},
recordByID: function () {
if (this.userid > 0) {
axios.get('ajaxfile.php', {
params: {
userid: this.userid
}
})
.then(function (response) {
app.users = response.data;
})
.catch(function (error) {
console.log(error);
});
}
}
}
}
</script>
running the app code, chrome dev tools, like resposnse displays me the source code of the php file (which doesn't run).
The php file in another environment, using html including axios.js vue.js files as src (CDN) script works fine.
where am I wrong or how should I configure the vs code environment?
Because localhost:8080 runs no PHP server you must use a server which is running PHP, either on your local machine (e.g. MAMP) or on your public server. If the project resides under the folder my_project and the PHP file under the subfolder static, the proxyTable must look like:
proxyTable: {
'/static/dserver.php': {
target: 'http://localhost/my_project',
changeOrigin: true
}
which resolves in http://localhost/my_project/static/dserver.php .
I want to display the list of record into webpage . I am using vuejs for front end development and mysql for backend .I created this applications by using LoopBack. I have some list of records inside the mysql database but the problem is when i run the web page , its does not display the records and when i want to insert new records , i am getting errors on this line ..
**(index):96 PUT http://localhost:3000/api/Account/ 400 (Bad Request)
storeAccount # (index):96
submit # VM346:3
invokeWithErrorHandling # vue.js:1863
invoker # vue.js:2188
original._wrapper # vue.js:7541**
When i clicked the index.js ,its showing error in this line
fetch(API, {...
Here is code for server.js file.
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-workspace
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var loopback = require('loopback');
var boot = require('loopback-boot');
var app = module.exports = loopback();
app.start = function() {
// start the web server
return app.listen(function() {
app.emit('started');
var baseUrl = app.get('url').replace(/\/$/, '');
console.log('Web server listening at: %s', baseUrl);
if (app.get('loopback-component-explorer')) {
var explorerPath = app.get('loopback-component-explorer').mountPath;
console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
}
});
};
// Bootstrap the application, configure models, datasources and middleware.
// Sub-apps like REST API are mounted via boot scripts.
boot(app, __dirname, function(err) {
if (err) throw err;
// start the server if `$ node server.js`
if (require.main === module)
app.start();
});
Here is my html code .
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="AccountApp">
<h1>Account List</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Email Address</th>
<th>Created Date</th>
<th>Updated Date</th>
<td> </td>
</tr>
</thead>
<tbody>
<tr v-for="account in accounts">
<td #click="editAccount(account)" class="accountItem" title="Click to Edit">{{account.id}}</td>
<td>{{account.email}}</td>
<td>{{account.createdAt}}</td>
<td>{{account.lastModifiedAt}}</td>
<td #click="deleteAccount(account)" class="deleteAccount" title="Click to Delete">Delete</td>
</tr>
</tbody>
</table>
<form #submit.prevent="storeAccount">
<p>
<label for="email">Email</label>
<input type="text" id="email" v-model="account.email">
</p>
<p>
<label for="createdAt">Created At</label>
<input type="text" id="createdAt" v-model="account.createdAt">
</p>
<p>
<label for="lastModifiedAt">Last Modified At</label>
<input type="text" id="lastModifiedAt" v-model="account.lastModifiedAt">
</p>
<input type="reset" value="Clear" #click="reset">
<input type="submit" value="Save User 🐱">
</form>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<script>
const API = 'http://localhost:3000/api/Account/';
let AccountApp = new Vue({
el: '#AccountApp',
data: {
accounts: [],
account: {
id: '',
email: '',
createdAt: '',
lastModifiedAt: ''
}
},
created: function () {
this.getAccounts();
},
methods: {
getAccounts: function () {
fetch(API)
.then(res => res.json())
.then(res => this.account = res);
},
storeAccount: function () {
let method;
console.log('storeAccount', this.account);
// Handle new vs old
if (this.account.id === '') {
delete this.account.id;
method = 'POST';
} else {
method = 'PUT';
}
fetch(API, {
headers: {
'Content-Type': 'application/json'
},
method: method,
body: JSON.stringify(this.account)
})
.then(res => res.json())
.then(res => {
this.getAccounts();
this.reset();
});
},
deleteAccount: function (c) {
fetch(API + c.id, {
headers: {
'Content-Type': 'application/json'
},
method: 'DELETE'
})
.then(res => res.json())
.then(res => {
this.getAccounts();
});
// call reset cuz the cat could be 'active'
this.reset();
},
editAccount: function (c) {
/*
This line was bad as it made a reference, and as you typed, it updated
the list. A user may think they don't need to click save.
this.cat = c;
*/
this.account.id = c.id;
this.account.email = c.email;
this.account.createdAt = c.createdAt;
this.account.lastModifiedAt = c.lastModifiedAt;
},
reset: function () {
this.account.id = '';
this.account.email = '';
this.account.createdAt = '';
this.account.lastModifiedAt = '';
}
}
});
</script>
Here is the screenshot when i run the applications .
Here is the screenshot on Networks tab ..
I am now working on download buttons for my site, but this download function won't work correctly when I add a template literal to the download link:
const logoData = [
{
name: "Arizona Cardinals",
png: "https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ARI.png",
},
{
name: "Atlanta Falcons",
png: "https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ATL.png",
}
];
function logoTemplate(logo) {
return `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${logo.name}</h5>
<a type="button" class="btn btn-primary" onclick="downloadIt()" href="#">Download PNG</a>
</div>
</div>
`
}
function downloadIt() {
function forceDownload(blob, filename) {
var a = document.createElement('a');
a.download = filename;
a.href = blob;
document.body.appendChild(a);
a.click();
a.remove();
}
function downloadResource(url, filename) {
if (!filename) filename = url.split('\\').pop().split('/').pop();
fetch(url, {
headers: new Headers({
'Origin': location.origin
}),
mode: 'cors'
})
.then(response => response.blob())
.then(blob => {
let blobUrl = window.URL.createObjectURL(blob);
forceDownload(blobUrl, filename);
})
.catch(e => console.error(e));
}
downloadResource(`${logo.png}`);
}
document.getElementById("library").innerHTML = `
<div class="container">
${logoData.map(logoTemplate).join('')}
</div>
`
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<div id="library">
</div>
The download function works when I hard code it something like: downloadResource('https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ARI.png')
but I want the file to change depending on the object it's under.
I want the first button to download this file: https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ARI.png
and I want the second button to download this file: https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ATL.png
Basically, I just want each object to have a button that on click automatically downloads the png value from this data
const logoData = [
{
name: "Arizona Cardinals",
png: "https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ARI.png",
},
{
name: "Atlanta Falcons",
png: "https://raw.githubusercontent.com/moose1645/SLV/master/logos/NFL/png/500/ATL.png",
}
];
.
Instead, I get error. Any advice would be extremely appreciated, thanks in advance.
I use collectionfs for storing files in my application.
I copy+pasted most of the readme code provided with collectionfs into my application and also added the
{{cfsFileUrl "default1"}}
to my file listing. Everything works on my local machine.
The problem arises when I deploy to ???.meteor.com with
mrt deploy ???.meteor.com
I can upload and download images and also a url is displayed from cfsFileUrl,
BUT:
When I access that url, I get Error 404.
My code:
client.html
<body>
{{loginButtons}}
{{>queueControl}}
<br>ta
<br>
{{>fileTable}}
</body>
<template name="queueControl">
<h3>Select file(s) to upload:</h3>
<input name="files" type="file" class="fileUploader" multiple>
</template>
<template name="fileTable">
{{#each files}}
{{cfsDownloadButton "ContactsFS" class="btn btn-primary btn-mini" content=filename}}<br>
<img src="{{cfsFileUrl "default1"}}">
{{/each}}
</template>
client.js
ContactsFS = new CollectionFS('contacts', { autopublish: false });
Deps.autorun(function () {
Meteor.subscribe('myContactsFiles');
});
Template.queueControl.events({
'change .fileUploader': function (e) {
var files = e.target.files;
for (var i = 0, f; f = files[i]; i++) {
ContactsFS.storeFile(f);
}
}
});
Template.fileTable.files = function() {
//show all files that have been published to the client, with most recently uploaded first
return ContactsFS.find({}, { sort: { uploadDate:-1 } });
};
server.js
ContactsFS = new CollectionFS('contacts', { autopublish: false });
Meteor.publish('myContactsFiles', function() {
if (this.userId) {
return ContactsFS.find({ owner: this.userId }, { limit: 30 });
}
});
ContactsFS.allow({
insert: function(userId, file) { return userId && file.owner === userId; }
});
ContactsFS.fileHandlers({
default1: function(options) { // Options contains blob and fileRecord — same is expected in return if should be saved on filesytem, can be modified
console.log('I am handling default1: ' + options.fileRecord.filename);
console.log(options.destination());
return { blob: options.blob, fileRecord: options.fileRecord }; // if no blob then save result in fileHandle (added createdAt)
}
});
I had a similar problem and posted it on collectionfs issue page. Check it out: https://github.com/CollectionFS/Meteor-CollectionFS/issues/85