.net core 2 upload multiple images appended to div - asp.net

So I have created a website where you can upload images.
Here's the problem:
I'm using hidden field of input type=file
And I have a designed button which trigger this input.
Now, I have a div that displays those images..
Problem is that I get only the last selected images from the user at my controller.
What happens if the user wants to upload from different directories?
I couldn't find answer after searching, Also I can't show the code right now,
I'm using really simple code tho,
Input of file (multiple)
Button that triggers it
Div that shows the pictures (appended with jquery)

This is a simplified example:
Html:
<button id="inputMask">Upload Image</button>
<div id="ImageHolder">
</div>
<form id="holder" method="post" enctype="multipart/form-data">
<h1>Form</h1>
<input type="submit" />
</form>
JS:
$(document).ready(() => {
let inputs = [];
//Model binding name
let name = "file";
let loadImagesFromInputs = () => {
$("#ImageHolder").empty();
//Lets load those images
if (!FileReader) {
console.log("Cant load files in the navigator");
return;
}
//For each input
inputs.forEach(i => {
//for each file in each input
Array.from(i.files).forEach(f => {
//prepare the file reader
let fr = new FileReader();
//this will run when conversion is finished
fr.onload = function () {
$("#ImageHolder").append($("<img>", {
src: fr.result
}));
}
//convert file to url
fr.readAsDataURL(f);
});
});
}
$("#inputMask").click(() => {
//Create file input
let newInput = $("<input>", {
type: "file",
name: name,
id: name,
multiple: true,
accept: "image/x-png,image/gif,image/jpeg"
}).css({ width: "1", position: "absolute", opacity: "0" });
//update the list of images on change
newInput.change((e) => { loadImagesFromInputs() });
//Add input to list of inputs
inputs.push(newInput[0]);
//Add input to form
$("#holder").append(newInput);
//Click input
newInput.click();
});
$("#holder").submit((e) => {
e.preventDefault();
e.target.submit();
})
});
.Net Core 2 Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MultipleFileDirectoriesDemo
{
[Route("[controller]")]
public class FileController : Controller
{
// GET: /<controller>/
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(List<IFormFile> file)
{
//Do something with the list of files
return View();
}
}
}

The built in multi-file up-loaders on most browsers are pretty bad.
Consider looking at something like jQuery.Fileuploader
This will have the added benefit of being relatively consistent across devices and browsers.

Related

Editing a viewmodel's member via button without submit

I'm using Asp Net Core 3.1 and am working on developing admin controls to approve and delete submitted images that are awaiting approval. The functionality that I am developing and am stuck on is as follows: I have created a grid of images waiting approval (using a loop in razor) and would like to click a button to "approve" that image via the logic I have written in my controller. How would I pass that data to the controller without refreshing the page?
View Model
public class ImageManagerViewModel
{
public List<ListingImages> Images;
public List<Tuple<long, string>> ListingNames;
}
Controller
public class AdminController : Controller
{
public ActionResult ApproveImage(int listingID, long imageID, bool isFeatured)
{
....
}
}
Client-side
#foreach (ListingImages row in Model.Images)
{
....
<div class="d-flex card-footer">
<a a class="btn btn-success btn-space"
href="#Url.Action("ApproveImage", "Admin", new { listingID = row.ListingId, imageID = row.ImageId, isFeatured = false})" role="button">Approve</a>
}
As VDWWD described, you wanna use ajax to achieve this behavior.
I made a quick sample for your code (I didn't have the ability to test it atm though).
Your loop (you can also use hidden input fields to track the ids of every single item):
#foreach (ListingImages row in Model.Images)
{
...
<span class="imageId">#(row.ImageId)</span>
<span class="listingId">#(row.ListingId)</span>
<input type="button" class="btn btn-success approveBtn">Approve</button>
}
JQuery code in the script section:
<script>
$(document).on("click",
".approveBtn",
function () {
var imageId = $(this).closest(".imageId").text();
var listingId = $(this).closest(".listingId").text();
$.ajax({
url: "/Admin/ApproveImage",
type: "POST",
data: {
__RequestVerificationToken: $('input[name=__RequestVerificationToken]').val(),
listingID : listingId,
imageID: imageId,
isFeatured: false
},
timeout: 5000,
success: function(results) {
// result action
},
contentType: "application/x-www-form-urlencoded; charset=utf-8"
})
.fail(function (xhr, textStatus, errorThrown) {
// error handling
});
});
</script>
Hints:
If you use one, include the antiforgery token in the request as shown in the sample.
You can also send the payload as JSON. You then need to edit the content type and use JSON.stringify in the data section to convert the payload.

Aurelia: How to handle a async request in a view?

I have a dotnet core api that returns a FileContentResult..
return new FileContentResult(bytes, contentType)
{
FileDownloadName = Path.GetFileName(request.Filename)
};
Via postman I can read out the image perfectly fine. Now I want to read the image, via the aurelia fetch client, and show it in my html view. This is my function to retrieve the image from the api.
public image(filename: string) {
return this.http.fetch(AppConfiguration.base_url + 'assets/image',
{
method: 'post',
body: json({
filename: filename
})
});
}
I've tried to convert the blob in the response with this value converter. But I can't get that to work
Converter:
export class BlobToUrlValueConverter {
public toView(blob) {
return URL.createObjectURL(blob);
}
}
Viewmodel:
export class Dashboard {
public blob: any;
constructor(
public assets_service: AssetsService
) { }
async attached() {
let response = await this.assets_service.image('test.png');
this.blob = response.blob();
}
}
View
<div if.bind="blob">
${ blob | blobToUrl }
</div>
I'm not sure this is the right approach. Also not sure how handle the async request part of it either. What is the best way to get that image response to show in the html view? Lets say via a img tag?
I was close. Here is how I got the image to show.
Viewmodel:
export class Dashboard {
public url: string;
constructor(
public assets_service: AssetsService
) { }
async attached() {
let blob = await this.assets_service.image('test.png')
.then(response => response.blob());
this.url = URL.createObjectURL(blob);
}
}
View:
<div if.bind="url">
<img src.bind="url">
</div>
EDIT:
Found a better solution using parts written above:
The exported function that does the call (for reusability on both ts and html sides):
export function image_request(filename: string): Promise<Response> {
let http = new Http();
return http.fetch(<your-url-that-fetches-the-image>,
{
method: 'post',
body: json({
filename: filename
})
});
}
Value converter that uses above function
import { image_request } from './AssetsRequests';
export class ImageRequestValueConverter {
public toView(filename: string) {
return image_request(filename);
}
}
The important and most awesome part of the solution. Many thanks to http://www.sobell.net/aurelia-async-bindings/
for getting me on my way. You can override the binding behaviour. You can use this override to process async
Promise in a view in combination with a value converter.
export class AsyncImageBindingBehavior {
public bind(binding, source): void {
binding.originalupdateTarget = binding.updateTarget;
binding.updateTarget = (target) => {
// When we have a promise
if (typeof target.then === 'function') {
// Set temp value to loading so we know its loading
binding.originalupdateTarget('Loading...');
// Process the promise
target
.then(response => response.blob())
.then(blob => binding.originalupdateTarget(
URL.createObjectURL(blob)
));
}
else {
binding.originalupdateTarget(target);
}
};
}
unbind(binding) {
binding.updateTarget = binding.originalupdateTarget;
binding.originalupdateTarget = null;
}
}
Finally the view is very simple
<img src="${ 'test.png' | imageRequest & asyncImage }">

Issue Angular-Meteor Meteor.publishComposite

When running helper brings values are stored in the variable verCandidatos.postulados.
Once I get me the information I need to get a document that is linked (using the function ng-init="candidato = la postulado.candidato()) wich runs on the helper from file: collection.js.
Sometimes the html shows the properties: {{candidato.nombre}}, {{candidato.apellidos}} and {{candidato.sexo}} correctly, and sometimes appear empty, why?
Is very strange, like a bug or something. How is possible that behavior?
The information is being obtained, because the ng-repeat works and shows elements.
Below is the publishComposite(), collection.js, html and js client
html client
my-app/imports/ui/components/vacantes/verCandidatos/ verCandidatos.html
<div ng-repeat="postulado in verCandidatos.postulados">
<div ng-init="candidato = postulado.candidato();">
{{candidato.nombre}}
{{candidato.apellidos}}
{{candidato.sexo}}
</div>
</div>
js in client
my-app/imports/ui/components/vacantes/verCandidatos/ verCandidatos.js
imports ...
class VerCandidatos {
constructor($scope, $reactive, $stateParams) {
'ngInject';
$reactive(this).attach($scope);
this.vacanteId = $stateParams.vacanteId;
this.subscribe('vacantes.candidatosOseleccionados', ()=> [{vacanteId: this.vacanteId}, {estado: 1}]);
this.helpers({
postulados (){
return Postulaciones.find();
}
});
}
}
collection.js
my-app/imports/api/postulaciones/ collection.js
imports...
export const Postulaciones = new Mongo.Collection('postulaciones');
Postulaciones.deny({...});
Postulaciones.helpers({
candidato(){
return Candidatos.findOne({_id: this.candidatoId});
}
});
publish.js:
my-app/imports/api/vacantes/server/ publish.js
imports...
if (Meteor.isServer) {
Meteor.publishComposite('vacantes.candidatosOseleccionados', function (vacanteId, estado) {
const selector = {$and: [estado, vacanteId]};
return {
find: function () {
return Postulaciones.find(selector);
},
children: [
{
find: function (postulacion) {
return Candidatos.find({_id: postulacion.candidatoId}, {
fields: {
nombre: 1,
apellidos: 1,
sexo: 1,
}
});
}
}
]
};
});
}
Any ideas?
- Thanks,
The ISSUE was in html
The solution was deteted ng-init and call directly the helpers inside collection.js, the other files (js in client, collection.js, publish.js) aren't modify.
The html file is as follows:
<div ng-repeat="postulado in verCandidatos.postulados">
{{postulado.candidato().nombre}}
{{postulado.candidato().apellidos}}
{{postulado.candidato().sexo}}
</div>
Thanks for read.
And I hope you will be useful.

Meteor Angular 2 - autobind not working in the tutorial

I am following the Meteor - Angular2 tutorial and things work fine.
The only point not working is the automatic binding with Angular2 UI for the 'details view'. For instance, if I navigate to the details view of Party1 the data of Party1 is correctly loaded and made visible on the Angular2 'details view'. If, afterwards, the data of Party1 is changed (e.g. via Mongo shell) such change is sent to the browser (via WebSockets) where 'details view' is displayed, but the new data is not shown on the view.
Here is the code of the PartyDetailsComponent class.
export class PartyDetailsComponent extends MeteorComponent implements OnInit, CanActivate {
partyId: string;
party: Party;
constructor(private route: ActivatedRoute, private ngZone: NgZone) {
super();
}
ngOnInit() {
this.route.params
.map(params => params['partyId'])
.subscribe(partyId => {
this.partyId = partyId;
this.subscribe('party', this.partyId, () => {
this.party = Parties.findOne(this.partyId);
}, true);
});
}
saveParty() {
Parties.update(this.party._id, {
$set: {
name: this.party.name,
description: this.party.description,
location: this.party.location
}
});
}
canActivate() {
const party = Parties.findOne(this.partyId);
console.log(party);
return (party && party.owner == Meteor.userId());
}
}
Here is the template of of PartyDetailsComponent
<form *ngIf="party" (submit)="saveParty()">
<label>Name</label>
<input type="text" [(ngModel)]="party.name" name="name">
<label>Description</label>
<input type="text" [(ngModel)]="party.description" name="description">
<label>Location</label>
<input type="text" [(ngModel)]="party.location" name="location">
<button type="submit">Save</button>
<a [routerLink]="['/']">Cancel</a>
</form>
Thanks in advance for any help
I actually found the answer to my questions just reading more of the Tutorial.
I can get automatic update ofthe UI once the underlying Mongo doc changes just adding Meteo autorun() method appropriately in the subscription code.
Here is the code that works
ngOnInit() {
this.route.params
.map(params => params['partyId'])
.subscribe(partyId => {
this.partyId = partyId;
this.subscribe('party', this.partyId, () => {
this.autorun(() => {
this.party = Parties.findOne(this.partyId);
}, true);
}, true);
});
}
What is not totally clear to me is why if you use directly the Meteo Mongo cursors (e.g. via *ngFor in the template) autorun is not needed.

Pop-up dialog using jQuery issue

Hi I have a problem with my page. I have one view page and 2 forms in the same page.
The problem is that I have a main form and another form which is a shown by JQuery. My Problem is when I open the dialog box, submit its form and return the view, the dialog box diappears. I don't know how to return a result which will still show the opened the dialog box.
I need your help on this please!
Below are the codes I used in my application.
.CSTHML Forms
#using (Html.BeginForm("Login2", "Account", FormMethod.Post, new { #id = "loginForm" }))
{
<a id="submitlink" href="#" class="button button-large">Sign In</a>
}
// This is the pop-up dialog box
#using (Html.BeginForm("TroubleSubmit", "Account", FormMethod.Post, new { #id = "troubleSubmitForm" }))
{
<a id="troubleSubmitLink" href="#" class="button">OK</a>
}
JQUERY
$('a#submitlink').click(function () {
$('#loginForm').submit();
});
$('a#troubleSubmitlink').click(function () {
$('#troubleSubmitForm').submit();
});
Below is the code of my controller action to handle the dialog form submit:
public ActionResult SignInTrouble(some parameter...)
{
// some operation and validation here
return View(); // ??? What view will I return? When I return "Login" it will reload the page and close the dialog box.
}
Again, how do I return the View that will still display the dialog box
You're submitting your form in a traditional (non-AJAX) manner so the page will reload. So you'll need to post in an AJAX way.
$("#submitlink").on("click", function () {
var loginForm = $("#loginForm");
$.ajax({
url: loginForm.attr("action"),
type: "post",
data: loginForm.serialize(),
})
.done(function(result) {
// do something with the returned response
});
});
The successful response is handled with .done() but what you return and how you handle the result is up to you. Simplest option is to return a partial view so it's just a html fragment to insert into an existing div container.
.done(function(result) {
$("#someDiv").html(result);
});
I often return JSON with the view rendered as an html string { status: 0, message: "Success", html: "<div>..." }. You could omit the html fragment from the JSON if you just need a simple YES/NO.
public ActionResult SignInTrouble(some parameter...)
{
// some operation and validation here
return Json({
status: 1,
message: "Validation Error"
});
}
Then you get a few more possibilities with your response
.done(function(result) {
var status = result.status;
var message = result.message;
if (status == 0) {
// OK
} else {
// ERROR
}
$("#messageDiv").text(message);
}
It simply due to the page is reloaded, you must use ajax form in this case, so it'll only process the action of ajax form and then return the result to the form without reload the page
#Ajax.BeginForm("TroubleSubmit","Account", new AjaxOptions(){ ...... })

Resources