I'm trying to create an upload preview for the image the user is uploading. I've gotten most of what I want done (thanks to stackoverflow... a lot of copying pasting and some tweaking) but since I don't know JavaScript, I can't get it to work exactly.
function uploadPreview(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
if (e.target.result.trim().length == 0) {
$('#serviceImage').attr('src', 'http://catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png');
} else {
$('#serviceImage').attr('src', e.target.result);
}
}
reader.readAsDataURL(input.files[0]);
}
}
function uploadPreviewExt(input) {
if (input.trim().length == 0) {
var previewSrc = '{{CSS_URL}}/images/placeholder.jpg';
} else {
var previewSrc = input;
}
$('#serviceImage').attr('src', previewSrc);
}
function resetUploadPreview() {
if (!$('#previousImage').val()) {
$('#serviceImage').attr('src', 'http://catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png');
} else {
$('#serviceImage').attr('src', '{{PROD_IMG}}/'.$('#previousImage').val());
}
}
$("#mainImage").change(function() {
uploadPreview(this);
});
$('[name="mainImgExt"]').on('change', function() {
uploadPreviewExt(this.value);
});
$('#clearUpload').on('click', function() {
$('#mainImage').val('');
if ($('#imgExt').val().trim().length == 0) {
resetUploadPreview();
} else {
uploadPreviewExt($('#imgExt').val());
}
});
$('#clearUploadExt').on('click', function() {
$('#imgExt').val('');
if (!$('#mainImage').val()) {
resetUploadPreview();
} else {
uploadPreview($('#mainImage'));
}
});
pointer {
cursor: pointer;
}
img.placeholder {
width: 100px;
height: 100px;
}
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="//unpkg.com/popper.js#1.12.9/dist/umd/popper.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<div class="form-group p-3 border p-2 rounded m-3">
<div class="row">
<div class="col-sm-3 text-center pt-2 border-right">
<label for="mainImage"><img src="http://catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png" alt="Placeholder" id="serviceImage" class="placeholder img-thumbnail" aria-describedby="imagePreviewHelp" /></label>
<small id="imagePreviewHelp" class="form-text text-muted">Preview could appear stretched</small>
</div>
<div class="col">
<label for="mainImage">Image</label>
<div class="input-group mb-3">
<input type="file" class="form-control border p-1" id="mainImage" name="mainImage" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUpload">X</span>
</div>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" id="imgExt" name="mainImgExt" placeholder="Externel Image" aria-describedby="imageHelp" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUploadExt">X</span>
</div>
</div>
<small id="imageHelp" class="form-text text-muted">Upload a local image <strong>or</strong> link to an external image.</small>
</div>
</div>
</div>
View on JSFiddle
There is a lot of JavaScript involved (more then my comfort zone since I don't know it at all).
There are two versions of HTML that the JavaScript could come across. It depends whether the user is adding a service, or editing one. The form is generated dynamically with the help of TWIG.
In JSFiddle, there is one version of the image uploading part of the form (if it's adding a service) below is the version for editing a service. (The hidden input field "previousImage" is added and the preview is the current image rather then the placeholder)
<div class="form-group p-3 border p-2 rounded">
<div class="row">
<div class="col-sm-3 text-center pt-2 border-right">
<label for="mainImage"><img src="https://logismarketmx.cdnwm.com/ip/eve-maquinaria-mantenimiento-para-gruas-mantenimiento-de-gruas-987423-FGR.jpg" alt="Vinyl" id="serviceImage" class="placeholder img-thumbnail" aria-describedby="imagePreviewHelp" /></label>
<input type="hidden" name="previousImage" id="previousImage" value="eve-maquinaria-mantenimiento-para-gruas-mantenimiento-de-gruas-987423-FGR.jpg" />
<small id="imagePreviewHelp" class="form-text text-muted">Preview could appear stretched</small>
</div>
<div class="col">
<label for="mainImage">Image</label>
<div class="input-group mb-3">
<input type="file" class="form-control border p-1" id="mainImage" name="mainImage" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUpload">X</span>
</div>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" id="imgExt" name="mainImgExt" placeholder="Externel Image" aria-describedby="imageHelp" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUploadExt">X</span>
</div>
</div>
<small id="imageHelp" class="form-text text-muted">Upload a local image <strong>or</strong> link to an external image.</small>
</div>
</div>
</div>
That's the backstory of the problem. Here is what I'm struggling with.
I want it when the user clicks on the red 'X' to clear the field to it's immediate left. It clears the field, but I also want to reset the upload preview image.
The reason the second version of the form matters is because if the user resets and is adding, then the image changes to the placeholder (which is what happens for the local upload but not the external URL); but if the user is editing a service then the upload preview would reset to the previous image that was set to the service.
I have the javascript to what I think should be working, but it doesn't.
function resetUploadPreview()
{
if(!$('#previousImage').val())
{
$('#serviceImage').attr('src', '{{CSS_URL}}/images/placeholder.jpg');
}
else
{
$('#serviceImage').attr('src', '{{PROD_IMG}}/' . $('#previousImage').val());
}
}
The logic behind that is if input with the ID 'previousImage' is empty (or nonexistent) it would display the placeholder but if it does have a value than we use it's value (the filename) to display this image.
But it doesn't do that, clearing the local file upload resets the image to the placeholder no matter if previousImage is filled or not, and the external url doesn't clear at all.
Another feature I'd like implemented is if both fields are filled in (local and external) then clearing one would display the other image in the preview.
Sorry if this seems simple, the logic seems simple to me but I can't seem to get it to work and I have no idea what I am missing... seems to me like everything is where it should work, but it just doesn't.
When editing it doesn't reset to the previous image... it doesn't reset at all. Console is giving me the following error
TypeError: "../design/variant/productImages/".$ is not a function
The line of code that it leads to is:
function resetUploadPreview()
{
if(!$('#previousImage').val())
{
$('#serviceImage').attr('src', '{{CSS_URL}}/images/placeholder.jpg');
}
else
{
// This is the line that makes that error
$('#serviceImage').attr('src', '{{PROD_IMG}}/' . $('#previousImage').val());
}
}
When adding a new service if you add a local image first and then an external image it shows the external image preview just like it should. But once you clear the external image it should display the local image (since it is filled in) but it doesn't change the preview at all. And console isn't showing any warnings/errors.
What am I doing wrong?
Nice work!
I made two adjustments:
In JavaScript, string concatenation is performed by using the + sign, as opposed to the . sign. (Hence the TypeError message you referenced.) I changed the code in resetUploadPreview() accordingly.
The uploadPreview() function does not expect a jQuery object, but a jQuery object is passed to restore the main image when the external image field is cleared. I changed it to pass the DOM element instead: $('#mainImage')[0] instead of $('#mainImage'). For more info, see What does $(selector)[0] mean in jQuery?
function uploadPreview(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
if (e.target.result.trim().length == 0) {
$('#serviceImage').attr('src', '//catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png');
} else {
$('#serviceImage').attr('src', e.target.result);
}
}
reader.readAsDataURL(input.files[0]);
}
}
function uploadPreviewExt(input) {
if (input.trim().length == 0) {
var previewSrc = '{{CSS_URL}}/images/placeholder.jpg';
} else {
var previewSrc = input;
}
$('#serviceImage').attr('src', previewSrc);
}
function resetUploadPreview() {
if (!$('#previousImage').val()) {
$('#serviceImage').attr('src', 'http://catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png');
} else {
$('#serviceImage').attr('src', '{{PROD_IMG}}/' + $('#previousImage').val());
}
}
$("#mainImage").change(function() {
uploadPreview(this);
});
$('[name="mainImgExt"]').on('change', function() {
uploadPreviewExt(this.value);
});
$('#clearUpload').on('click', function() {
$('#mainImage').val('');
if ($('#imgExt').val().trim().length == 0) {
resetUploadPreview();
} else {
uploadPreviewExt($('#imgExt').val());
}
});
$('#clearUploadExt').on('click', function() {
$('#imgExt').val('');
if (!$('#mainImage').val()) {
resetUploadPreview();
} else {
uploadPreview($('#mainImage')[0]);
}
});
pointer {
cursor: pointer;
}
img.placeholder {
width: 100px;
height: 100px;
}
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="//unpkg.com/popper.js#1.12.9/dist/umd/popper.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<div class="form-group p-3 border p-2 rounded m-3">
<div class="row">
<div class="col-sm-3 text-center pt-2 border-right">
<label for="mainImage"><img src="http://catawbabrewing.com/wp-content/themes/catawba/images/placeholder.png" alt="Placeholder" id="serviceImage" class="placeholder img-thumbnail" aria-describedby="imagePreviewHelp" /></label>
<small id="imagePreviewHelp" class="form-text text-muted">Preview could appear stretched</small>
</div>
<div class="col">
<label for="mainImage">Image</label>
<div class="input-group mb-3">
<input type="file" class="form-control border p-1" id="mainImage" name="mainImage" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUpload">X</span>
</div>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" id="imgExt" name="mainImgExt" placeholder="Externel Image" aria-describedby="imageHelp" />
<div class="input-group-append">
<span class="input-group-text bg-warning text-danger pointer" id="clearUploadExt">X</span>
</div>
</div>
<small id="imageHelp" class="form-text text-muted">Upload a local image <strong>or</strong> link to an external image.</small>
</div>
</div>
</div>
Related
I'm using the Cropper JS library and then class.upload.php on an image upload project. I've got everything working except when the PHP script processes the upload, it saves the uncropped version of the image. If I 'inspect' the file input in Chrome after I've performed the crop, the src is the cropped image, so I can't figure out for the life of me why it's receiving the original uncropped version.
The HTML looks like this. It uses Bootstrap & JQuery, but I'm omitting that to avoid bloat.
<input type="file" class="upload-input" name="imageFile" accept="image/*" style="display:none;" id="input-1">
<div class="input-group mb-3">
<input type="text" class="form-control" id="filename-1" readonly placeholder="Upload your show image (Required)" style="width:100%;">
<div class="input-group-btn">
<button class="btn btn-outline-secondary upload-button" type="button" data-ratio="1/1">Select</button>
</div>
</div>
<!-- Modal For Cropper -->
<div class="modal fade photo-cropper" tabindex="-1" aria-labelledby="cropperModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="cropperModalLabel">Crop your image</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="upload-preview">
<img class="upload-image-preview" style="display: block; max-width: 100%;">
<div class="photo-cropper-controls">
<i class="fa fa-check-square" id="crop-confirm" aria-hidden="true"></i>
<i class="fa fa-window-close" id="crop-cancel" aria-hidden="true"></i>
</div>
</div>
</div>
</div>
</div>
</div>
Here's the JS that controls Cropper
$(document).on('click', '.upload-button', function(e) {
var ratio = $(this).data('ratio');
var activeInput = $('#input-1');
activeInput.click();
$(activeInput).change(function() {
if(this.files && this.files[0]) {
var imageFile = this.files[0];
var reader = new FileReader();
reader.onload = function (e) {
$('.photo-cropper').modal('show');
$(document).on('shown.bs.modal', function (f) {
$('.upload-image-preview').attr('src', e.target.result);
$('.photo-cropper-controls').show();
$('.upload-image-preview').data('target', '#input-1');
$('.upload-image-preview').cropper('destroy');
$('.upload-image-preview').cropper({
aspectRatio: eval(ratio),
viewMode: 1
});
});
}
reader.readAsDataURL(imageFile);
}
});
});
$(document).on('click', '#crop-confirm', function() {
var target = $('.upload-image-preview').data('target');
var imageData = $('.upload-image-preview').cropper('getCroppedCanvas', {maxWidth: 990}).toDataURL('image/jpeg');
$('.photo-cropper').modal('hide');
$(document).on('hidden.bs.modal', function (f) {
$('.upload-image-preview').cropper('destroy');
});
$(target).attr('src', imageData);
var filename = $(target).val().split('\\').pop();
$('#filename-1').val(filename);
});
And finally, the PHP that deals with the posted form
if(!empty($_FILES['imageFile'])) {
require_once('assets/plugins/class.upload.php');
$handle = new upload($_FILES['imageFile']);
if ($handle->uploaded) {
$handle->file_safe_name = true;
$handle->allowed = array('image/*');
$handle->image_ratio_y = true;
$handle->image_x = 800;
$handle->process('/upload/path/');
if ($handle->processed) {
$image = $handle->file_dst_name;
}
}
}
The basic method is click the .upload-button which clicks the hiding file input, then opens a Bootstrap modal and initialises Cropper with the selected image. When you've finished cropping, clicking #crop-confirm creates the DataURL, closes the modal, and puts the imageData in the file input. Submitting the form sends the data to the PHP with the image in the imageFile field. All ok so far? So why is the file it saves the original uncropped image?
I'm clearly missing something really obvious...
I am receiving a bunch of ERR_HTTP2_PROTOCOL_ERROR messages in dev tools after 1 of the pages submits a form to a controller and then the controller displays a different view.
This is the sequence of events:
OnlineEnrollmentController presents Index.cshtml.
The View Index.cshtml displays text to the user and the user clicks a button to submit a form.
The form is sent to OnlineEnrollmentController/Agreement where a View is returned.
The View Agreement.cshtml is presented to the user and the user clicks a button to submit a form.
The form is sent to OnlineEnrollmentController/Profile where the ActionResult Profile reads data from a database, populates a model and returns the model to the View Profile.cshtml.
The View Profile.cshtml is presented to the user and the user clicks a button to submit a form.
The form is sent to OnlineEnrollmentController/Rate where the ActionResult Rate reads data from a database, populates a model and returns the model to the View Rate.cshtml.
The View Rate.cshtml is presented to the user and asks the user to enter a rate into a text box. The user enters a rate and clicks on the button to submit the form.
The form is sent to OnlineEnrollmentController/Election where the ActionResult Election gets the rate that was populated and stores in a TempData field. A new list of object is created where each object contains 3 fields and is returned to the View Election.cshtml.
The View Election.cshtml uses a foreach loop to display the contents of the list of object.
All this logic works except now I am receiving this ERR_HTTP2_PROTOCOL_ERROR when the View Election.cshtml is loaded.
Here are the specifics:
The application has a shared view called _NoMenu.cshtml. This is the contents of the file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>#ViewData["Title"]</title>
<link rel="stylesheet" href="~/css/site.css" />
<script src="https://use.fontawesome.com/54ec0da54f.js"></script>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous" />
<!-- font awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous" />
<style>
/* Style for showing in progress bar
--------------------------------------------------*/
.submit-progress {
position: fixed;
top: 30%;
left: 50%;
height: 6em;
padding-top: 2.3em;
/* The following rules are the
ones most likely to change */
width: 20em;
/* Set 'margin-left' to a negative number
that is 1/2 of 'width' */
margin-left: -10em;
padding-left: 2.1em;
background-color: #17a2b8a8;
color: black;
-webkit-border-radius: 0.4em;
-moz-border-radius: 0.4em;
border-radius: 0.4em;
box-shadow: 0.4em 0.4em rgba(0,0,0,0.6);
-webkit-box-shadow: 0.4em 0.4em rgba(0,0,0,0.6);
-moz-box-shadow: 0.4em 0.4em rgba(0,0,0,0.6);
}
.hidden {
visibility: hidden;
}
.submit-progress i {
margin-right: 0.5em;
}
.mb-5, .my-5 {
margin-bottom: 1.5rem !important;
}
.scrollHeight {
height: 85vh;
}
.hideit {
display: none;
}
.width95 {
width: 95%;
}
.myDIV {
height: 75%;
overflow: auto;
}
.content500 {
height: 500px;
}
.content600 {
height: 600px;
}
.content800 {
height: 800px;
}
</style>
</head>
<body>
<div>
#RenderBody()
</div>
</body>
</html>
I use this for any page in my application where I dont want to show a standard bootstrap menu structure.
Here is the contents of the view Rate.schtml:
#model AccuRecordV3.Models.OnlineEnrollment_Indicative_Verify
#{
ViewData["Title"] = "Online Enrollment - Rate";
Layout = "~/Views/Shared/_NoMenu.cshtml";
}
#using (Html.BeginForm("Election", "OnlineEnrollment", FormMethod.Post))
{
<section id="rate" class="vh-100" style="background-color: #508bfc;">
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col-12 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-2-strong" style="border-radius: 1rem;">
<div class="card-body p-5 text-center scrollHeight">
<h3 class="mb-5">Online Enrollment</h3>
<h5 class="mb-5">Step 1 of 3</h5>
<div id="rateDIV" class="myDIV">
<div id="ratecontent" class="content500">
<div class="form-outline mb-4">
<label class="form-label text-left width95">
#Model.messageLiteral
</label>
</div>
<div class="form-outline mb-4">
<label class="form-label text-left width95">
The minimum contribution rate allowed by your plan is #Model.outputMinPct and the
maximum contribution rate allowed by your plan is #Model.outputMaxPct
</label>
</div>
<div class="form-outline mb-4">
<label class="form-label text-left">
How much would you like to contribute?
</label>
</div>
<div class="form-outline mb-4">
<input type="number" id="electedRate" asp-for="electedRate" class="form-control form-control-lg rate" value="#Model.electedRate" placeholder="0" min="#Model.ss_CtrbKMinPct" step="1" max="#Model.ss_CtrbKMaxPct" />
<label class="form-label text-danger" for="electedRate" id="rateerrorLabel"> </label>
</div>
<button class="btn btn-primary btn-lg btn-block" type="button" id="rateButton" disabled onclick="return DisplayProgressMessage(this, 'rate');">Next</button>
<button class="btn btn-outline-primary btn-lg btn-block" type="button" id="cancelRateButton" onclick="return Cancel(this, 'rate');">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
}
<script>
$(function () {
var rate = 0;
$('.rate').each(function () {
rate += parseInt($(this).val());
});
var minRate = parseInt("#Model.ss_CtrbKMinPct");
var maxRate = parseInt("#Model.ss_CtrbKMaxPct");
if (rate < minRate || rate > maxRate) {
$("#rateButton").prop("disabled", true);
} else {
$("#rateButton").prop("disabled", false);
}
$('.rate').change(function () {
var rate = 0;
$('.rate').each(function () {
rate += parseInt($(this).val());
});
var minRate = parseInt("#Model.ss_CtrbKMinPct");
var maxRate = parseInt("#Model.ss_CtrbKMaxPct");
if (rate < minRate || rate > maxRate) {
$("#rateButton").prop("disabled", true);
} else {
$("#rateButton").prop("disabled", false);
}
})
})
function DisplayProgressMessage(ctl, msg) {
$(ctl).prop("disabled", true).text(msg);
$("#submitButton").prop("disabled", true);
$(".submit-progress").removeClass("hidden");
const myTimeout = setTimeout(function () {
$("form").submit();
}, 5);
}
</script>
The view Rate.schtml works correctly, using jquery to validate the rate the user enters and then submits the form back to the controller.
This is the contents of Election.cshtml:
#model AccuRecordV3.Models.OnlineEnrollment_Indicative_Verify
#{
ViewData["Title"] = "Online Enrollment - Rate";
Layout = "~/Views/Shared/_NoMenu.cshtml";
}
#using (Html.BeginForm("Summary", "OnlineEnrollment", FormMethod.Post))
{
<section id="elections" class="vh-100" style="background-color: #508bfc;">
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col-12 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-2-strong" style="border-radius: 1rem;">
<div class="card-body p-3 text-center scrollHeight">
<h3 class="mb-5">Online Enrollment</h3>
<h5 class="mb-5">Step 2 of 3</h5>
<div id="electionsDIV" class="myDIV">
<div id="electionscontent" class="content600">
<div class="form-outline mb-4">
<label class="form-label text-left width95">
These are the funds that are available to you. You can allocate your contribution to any/all of these funds. Remember, you wont be able to submit your elections all your total elections added together equal 100%.
</label>
</div>
#foreach (var localRecord in Model.electionData)
{
<div class="row width95">
<label for="NewPct" class="col-sm-9 col-form-label text-left">#localRecord.FundName</label>
<div class="col-sm-3">
<input type="number" class="form-control election" id="NewPct" name="NewPct" min="#localRecord.MinPct" max="#localRecord.MaxPct" value="0">
<input type="number" id="HostFID" name="HostFID" hidden value="#localRecord.HostFID" />
</div>
</div>
}
<div class="row width95">
<label class="col-sm-9 col-form-label text-left">Total</label>
<label id="ElectionTotal" name="ElectionTotal" class="col-sm-3 col-form-label text-right">0%</label>
</div>
<button class="btn btn-primary btn-lg btn-block" type="button" id="electionButton" disabled onclick="return DisplayProgressMessage(this, 'elections');">Next</button>
<button class="btn btn-outline-primary btn-lg btn-block" type="button" id="cancelElectionButton" onclick="return Cancel(this, 'elections');">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
}
<script>
$(function () {
$('.election').change(function () {
var sum = 0;
$('.election').each(function () {
sum += parseInt($(this).val());
});
var totalElection = sum + "%";
$("#ElectionTotal").text(totalElection)
if (sum == 100) {
$("#electionButton").prop("disabled", false);
} else {
$("#electionButton").prop("disabled", true);
}
})
})
function DisplayProgressMessage(ctl, msg) {
$(ctl).prop("disabled", true).text(msg);
$("#submitButton").prop("disabled", true);
$(".submit-progress").removeClass("hidden");
const myTimeout = setTimeout(function () {
$("form").submit();
}, 5);
}
</script>
When this view loads, and I look at dev tools, this is what I see:
and
So, for some reason, this error is preventing jquery.min.js from loading which in turn causes the '$' not defined error.
It looks like this error is also preventing the style sheet from loading properly as well.
Some research into this says it's a CORS issue but I dont see how I could have a CORS issue on one page (Elections.schtml) and not on the others (Index.schtml, Agreement.schtml, Profile.schtml, and Rate.schtml).
Any idea how to resolve this?
Thanks.
I'm working on a system where the user can select a product and go to the next page. This product then gets saved in the session using laravel sessions. When the user decides to go to the next page and come back. The chosen product is indeed saved in the session, but the is no way for them to see what product they have chosen because the class isn't applied to the product that was chosen.
The code may clarify it better:
#foreach($themes as $theme)
<div class="col-md-4 mb-4">
<div class="card theme-card card-hover depth-2 border-0" id="theme-id-{{$theme->id}}">
<a href="" class="theme-link" data-toggle="modal" data-target="#theme{{ $theme->id }}">
<div class="card-image" style="height: 200px; background-image: url('/uploads/{{ $theme->thumbnail }}'); background-size: cover; background-position: center center;"></div>
<div class="card-body">
<div class="row">
<div class="col-md-2 vertical-center">
<i class="fab fa-magento fa-lg"></i>
</div>
<div class="col-md-10">
<p class="m-0">{!! str_limit($theme->name, $limit = 32, $end = '...') !!}</p>
<small class="text-muted">{{ $theme->productable_type }}</small>
</div>
</div>
</div>
</a>
<div class="card-footer bg-white border-0 text-right pt-0">
<div class="row">
<div class="col-md-6 text-left">
<input type="hidden" class="theme-name" name="theme[{{$theme->id}}]">
{{--<input type="hidden" value="{{ $theme->composer_package }}">--}}
<button data-card-id="{{$theme->id}}" class="btn btn-orange btn-sm btn-theme-choice">Kiezen</button>
</div>
<div class="col-md-6 text-right">
<span style="font-size: 20px;" >€ {{ $theme->price }} EUR</span>
</div>
</div>
</div>
</div>
</div>
#endforeach
In the above code, I for each trough every so-called "Theme" and I'm giving the ID of the theme as a data target and as ID. Then in my Javascript code, I do the following:
$('.btn-theme-choice').on('click', function (event) {
event.preventDefault();
newSelectedCardId = $(event.target).data('card-id');
if(cardId === null) {
cardId = newSelectedCardId;
} else if (cardId !== newSelectedCardId) {
$('#theme-id-' + cardId).removeClass('theme-chosen').find("input[name='theme["+cardId+"]']").val('');
cardId = null;
}
var card = $('#theme-id-' + cardId );
card.toggleClass('theme-chosen');
selectedCardInput = card.find("input[name='theme["+cardId+"]']");
if( !$('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('');
} else if ( $('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('selected');
}
console.log(selectedCardInput);
});
Here I add the class to the card so the user can See which card they've selected. This choice then gets saved in the session using some PHP code in the controller
if( $theme == null ) {
return redirect('plugins');
} elseif( $theme != null ) {
foreach($this->predefinedArray as $value) {
$request->session()->put('chosen_theme.' . $value, $theme->$value);
}
$request->session()->put('chosen_theme.composer_package', $theme->productable->composer_package);
return redirect('plugins');
}
problem
How can I read the session and add the class to the card with the IDs that were stored in the session so if the person leaves the page and comes back, they can see what cards they've selected?
Thanks in advance
Try this in your view..
<div class="card theme-card card-hover depth-2 border-0 {{ \Session::get('chosen_theme.composer_package') == $theme->id ? 'theme-chosen' : '' }}" id="theme-id-{{$theme->id}}">
Whenever the theme id is in the session and the page loaded the class will be added, and if it is not in the session then the class won't be added.
Let me know the result..
I'm writing the javascript code to save file in onedrive helping with https://dev.onedrive.com/sdk/js-v6/js-picker-save.htm
For the same,I am using my personal account *#live.com and had added the application in microsoft and pass the app_id as client id in javascript tag.
But faced that issue to get the token and open the one drive exprorer.
We are unable to issue tokens from this api version for a Microsoft account. Please contact the application vendor as they need to use version 2.0 of the protocol to support this.
<script type="text/javascript" src="https://js.live.net/v6.0/OneDrive.js" id="onedrive-js" client-id="78ef3a97-45fe-416c-a8ed-b618caee43c9"></script>
<div>
<div class="pickerDemo hidden-xs visible-sm visible-md visible-lg">
<div class="row">
<span class="cell heading">File</span><span class="cell">
<input id="inputFile" name="inputFile" type="file">
</span>
</div><div class="row">
<span class="cell heading">File name</span><span class="cell">
<input id="fileName" type="text" name="fileName">
<label>Default: the file's local name</label>
</span>
</div><div class="row">
<span class="cell heading">Button</span><span class="cell">
<button onclick="javascript:launchOneDriveSaver();" title="Save to OneDrive">
<img src="https://js.live.net/v5.0/images/SkyDrivePicker/SkyDriveIcon_white.png" style="vertical-align: middle; height: 16px;">
<span class="oneDriveButton">Save to OneDrive</span>
</button>
</span>
</div><div> </div><div class="row">
<span class="cell heading" style="vertical-align: top;">Result</span><span class="cell">
<div id="saverConsole" class="console">Save a file to OneDrive to see the return result</div>
</span>
</div>
</div>
</div>
<script type="text/javascript">
function launchOneDriveSaver(){
var saveOptions = {
file: "inputFile", /* The id of a file type input element or a valid data URI string */
fileName: "file.txt", /* Required if `saverOptions.file` is a data URI string */
openInNewWindow: true,
success: function () { /* upload is complete */ },
progress: function (p) { /* upload is progressing */ },
cancel: function () { /* upload was cancelled */ },
error: function (e) { /* an error occurred */ }
}
OneDrive.save(saveOptions);
}
</script>
I am trying to create a HTML5 based progress bar with three steps. The objective I want to achieve is field level progression as well as step level progression.
So the whole progress bar can be divided into two sections, where the first section is 50% and second section is 50% and if there are total 20 fields then after entering each field the progress bar moves 5% .Since want to implement this as well as include images in those steps. It's somehow not working out.
<div id="tmm-form-wizard" class="container substrate">
<div class="row stage-container">
<div class="stage tmm-current col-lg-4 col-md-4 col-sm-4 col-xs-4">
<div class="stage-header personaldetails"></div>
<div class="stage-content">
<h3 class="stage-title angel_font" style="color:#ffffff !important; font-size:14px !important;">Personal Details</h3>
</div>
</div><!--/ .stage-->
<div class="stage col-lg-4 col-md-4 col-sm-4 col-xs-4">
<div class="stage-header planselection"></div>
<div class="stage-content">
<h3 class="stage-title angel_font" style="color:#ffffff !important; font-size:14px !important;">Plan Selection</h3>
</div>
</div><!--/ .stage-->
<div class="stage col-lg-4 col-md-4 col-sm-4 col-xs-4 norightbar">
<div class="stage-header reviewpay"></div>
<div class="stage-content">
<h3 class="stage-title angel_font" style="color:#ffffff !important; font-size:14px !important;">Review and Pay</h3>
</div>
</div><!--/ .stage-->
</div><!--/ .row-->
</div><!--/ .container-->
</div> <!--LOGO, NEED HELP AND PROGRESS CONTAINER ENDS-->
</nav><!--FIXED NAVIGATION ENDS-->
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12 lalign">
<form action="" method="post" id="step1_form" name="step1_form">
<span class="angel_font pan_no">PAN Number</span>
<input type="text" class="form-control input_outline" placeholder="Type your PAN Number (eg: BODPM4264E)" id="pan_text" name="pan_text" onblur="checkField(this)" maxlength="10" style="text-transform:uppercase;"/>
<p class="angel_font" id="err_msgpan">PAN card must contain a mix of Alphabets (A-Z) and Numbers (0-9)</p>
</div>
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
</div> <!--PAN NUM ENDS-->
<div class="row"> <!--DOB TEXT STARTS-->
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12 lalign">
<span class="angel_font dob">Date of Birth (as per PAN card)</span>
</div>
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
</div> <!--DOB TEXT ENDS-->
<div class="row"> <!--DOB FIELD STARTS-->
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<input type="tel" class="input_outline" placeholder="DD" id="dd_text" name="dd_text" maxlength="2" onkeypress="return isNumber(event)" size="4" onblur="isEmpty(this)"/>
<input type="tel" class="input_outline" placeholder="MM" id="mm_text" name="mm_text" maxlength="2" onkeypress="return isNumber(event)" size="4" onblur="isEmpty(this)"/>
<input type="tel" class="input_outline" placeholder="YYYY" id="yyyy_text" name="yyyy_text" maxlength="4" onkeypress="return isNumber(event)" size="6" onblur="isEmpty(this)"/>
</div>
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs"></div>
</div> <!--DOB FIELD ENDS-->
/* Stage */
#tmm-form-wizard .stage:before,
#tmm-form-wizard .stage:after { background-color: #ffffff; } /* Set color for STAGE LINE */
#tmm-form-wizard .stage-header { background-color: #d8e3ee;; } /* Set background color for STAGE HEADER */
#tmm-form-wizard .stage-title { color: #464646; } /* Set color for STAGE TITLE */
#tmm-form-wizard .stage-info { color: #a8a8a8;} /* Set color for STAGE INFO */
/* end Stage */
/* Current stage */
#tmm-form-wizard .stage.tmm-current .stage-header { background-color: #fabf00; } /* Set background color for CURRENT STAGE */
#tmm-form-wizard .stage.tmm-current .stage-header.head-number { color: #fff; } /* Set color for CURRENT STAGE TEXT */
#tmm-form-wizard .stage.tmm-current:after,
#tmm-form-wizard .stage.tmm-current:before { background-color: #fabf00; } /* Set background color for CURRENT STAGE LINE */
/* end Current stage */
/* Success stage */
#tmm-form-wizard .stage.tmm-success .stage-header { background-color: #00a33e; } /* Set background color for SUCCESS STAGE */
#tmm-form-wizard .stage.tmm-success .stage-header.head-number { color: #3c611b; } /* Set color for SUCCESS STAGE TEXT */
#tmm-form-wizard .stage.tmm-success:after,
#tmm-form-wizard .stage.tmm-success:before { background-color: #fabf00; } /* Set background color for SUCCESS STAGE LINE */
#tmm-form-wizard .stage.tmm-success .stage-header:after { color: #00a33e; } /* Set color for SUCCESS STAGE ICON */
/* end Success stage */
#tmm-form-wizard.substrate { background-color: transparent !important; } /* Set background color for FORM SUBSTRATE */
If I understand your question you want to change the progress value according to some other form field, respecting whether these fields are filled or not. Try look at this sample:
$(function() {
$("input[type='text']").change(function() {
var progress = parseInt($("progress").val());
var val = $(this).val();
// Change the progress value according to the value in the input field
$("progress").val(val.length > 0 ? (progress + 10) : (progress - 10));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<progress value="0" max="100"></progress>
<br/>
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
I used the HTML5 progress, but you can use your own, and I handle change event of all input fields in the page. If some field is changed and its value is not empty, I update the progress value towards 100%.
You'll need to perform this algorithm:
elements with content / # of elements
Whenever an element is altered.
To do this, you need to "wire up" each input so that when its value is changed, the progress bar is updated:
// When the document is ready
$(function() {
// Find all the input elements are group them
// If you are using other input types besides textboxes, update the selector
var $inputs = $("input[type='text']");
// Find out how many there are:
var amount = $inputs.length;
// Wire up each input to a callback function for
// when their value gets changed
$inputs.change(function() {
var progress = parseInt($("progress").val());
// Loop through all the inputs to see how many have values
var count = 0;
$.each(inputs,function(index, value){
if(value !== ""){
count++;
}
});
// Calculate the progress %
var prog = parseInt((count / amount) * 100);
$("progress").val(prog);
});
});