I have a Razor app where Lightbox2 was previously used for image galleries in the app. Now, I want to make a change to use PhotoSwipe instead. I started off by following the documentation on the official site: https://photoswipe.com/getting-started/. My issue is that when I click on any thumbnail, the full image is opened in a new tab instead of opening the image in the gallery component of PhotoSwipe. The documentation seemed like a pretty straightforward setup, but in this case I am not sure what else I am missing. Thanks in advance for all your help.
PhotoSwipe initialization (init-photoswipe.js):
import PhotoSwipeLightbox from '../photoswipe-dist/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery: '#gallery--custom-html-markup',
children: 'a',
pswpModule: () => import('../photoswipe-dist/photoswipe.esm.js')
});
lightbox.init();
Code snippet of Razor component:
<ContentTemplate>
#if (IsLoading)
{
<PlaceholderLoading />
}
else
{
<div class="card-body pb-1">
<h5 class="card-title pr-2">
#(Observation.Question ?? Observation.Name)
</h5>
</div>
#if (ThumbnailUrl is PhotoNotTaken or ImageCannotBeDisplayed)
{
<img class="card-img-bottom" src="#ThumbnailUrl" alt="No photo available" />
}
else
{
<div class="pswp-gallery" id="gallery--custom-html-markup">
<a href="#PhotoUrl" data-pswp-width="1669" data-pswp-height="2500" target="_blank">
<img class="card-img-bottom" src="#ThumbnailUrl" alt="#(Observation.Question ?? Observation.Name)" />
</a>
</div>
}
}
</ContentTemplate>
Snippet of _Host.cshtml file:
<!-- PhotoSwipe CSS -->
<link rel="stylesheet" href="~/_content/ProjectComponents/js/photoswipe-dist/photoswipe.css">
<!-- PhotoSwipe initialization -->
<script src="~/_content/ProjectComponents/js/custom/init-photoswipe.js" type="module"></script>
Note: The _Host.cshtml file is in a separate project for the actual website. The PhotoSwipe initialization and Razor component code are in another project for Shared Components. Both projects are part of the same solution, so not sure if this will cause some issues with PhotoSwipe.
I run into the same issue; it appears to be the dynamic content isn't picked up when lightbox.init() first initialized; to get that worked around we need to
assign the lightbox initialization function to a global object something like following (note the window.MyJS object):
import PhotoSwipeLightbox from '/libs/photoswipe/photoswipe-lightbox.esm.min.js';
const lightbox = new PhotoSwipeLightbox({
gallery: '#gallery--custom-html-markup',
children: 'a',
pswpModule: () => import('/libs/photoswipe/photoswipe.esm.min.js')
});
window.MyJS = {
initializeGallery: function () {
lightbox.init();
}
}
Later inside of the gallery component we should do the following:
#inject IJSRuntime JS
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await IJS.InvokeVoidAsync("MyJS.initializeGallery");
}
}
Hope that helps
Related
I'm looking for a way to create a link that will create a screenshot of a Razor component and download it as a JPG, PNG or PDF through a Blazor Server application. Ideally, it will only contain the Razor component and all child components, but no parents, and the image will have the precise appearance of the current state displayed on the browser.
The only similar thing is capturing HTML canvases, but since I'm so new to Blazor, I'm not exactly sure how to apply that, and was wondering if there's a way of achieving something similar via C#. Any suggestions would be appreciated. Thanks :)
I don't think it will be easy to achieve that with C# alone, since the best approach to the problem is to convert the HTML data to canvas and export it as an image which is a hard process, but there are libraries available for that, in JS.
How to do it:
I'm gonna use this library since it seems to be the simplest: html2canvas
Here is the link to it's JS file: html2canvas.js
download it and place it in your wwwroot folder, then, include that in the _host.cshtml by adding this at the end of the body tag:
<script src="html2canvas.js"></script>
then add a JS function to call from C# so we can have the even handler written in C# by adding this after the previous code in _host.cshtml:
<script>
window.takeScreenshot = async function(id) {
var img = "";
await html2canvas(document.querySelector("#" + id)).then(canvas => img = canvas.toDataURL("image/png"));
var d = document.createElement("a");
d.href = img;
d.download = "image.png";
d.click();
return img;
}
</script>
This will automatically take a screenshot from the element and download it, also return its URL. Note that the component must be inside a div tag with an id, otherwise, you can't select it alone, example good child in parent:
Parent.razor
<div id="child"></div>
<Child />
</div>
To use this function, use the JsInterop class. Simply, inject (basically include) it in your razor component where you need this functionality by adding this at the top of the file:
#inject IJSRuntime JS
next, a function to do everything:
#inject IJSRuntime JS
#code {
public async System.Threading.Tasks.Task<string> TakeImage(string id)
{
return await JS.InvokeAsync<string>("takeScreenshot", id);
}
}
This function will return a data URL of an image taken from an element specified by the id parameter.
Sample usage with a button to take image:
#page "/screenshot"
#inject IJSRuntime JS
#code {
string image_url = "";
string child_id = "child";
public async System.Threading.Tasks.Task<string> TakeImage(string id)
{
return await JS.InvokeAsync<string>("takeScreenshot", id);
}
public async System.Threading.Tasks.Task ButtonHandler()
{
image_url = await TakeImage(child_id);
}
}
<button #onclick="ButtonHandler">Take image</button>
<h3>Actual component:</h3>
<div id=#child_id>
<ChildComponent />
</div>
<h3>Image:</h3>
<img src=#image_url />
<br />
<br />
<br />
<p>URL: #image_url</p>
Pressing the button will download the image, show it, show the raw URL, and save the URL to variable image_url.
You can shorten System.Threading.Tasks.Task to Task by adding #using System.Threading.Tasks to _Imports.razor file.
You can remove auto-download functionality by removing these 4 lines in the JS function:
var d = document.createElement("a");
d.href = img;
d.download = "image.png";
d.click();
If you want to take an image of the entire page automatically and download it without any user interaction:
modify the JS function and set the query selector to body and remove the id parameter:
<script>
window.takeScreenshot = async function() {
var img = "";
await html2canvas(document.querySelector("body")).then(canvas => img = canvas.toDataURL("image/png"));
var d = document.createElement("a");
d.href = img;
d.download = "image.png";
d.click();
return img;
}
</script
set the function to run when the document loads:
#inject IJSRuntime JS
#page "/component"
#code {
string image_url = "";
public async System.Threading.Tasks.Task<string> TakeImage()
{
return await JS.InvokeAsync<string>("takeScreenshot");
}
protected override async System.Threading.Tasks.Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender) //Ensure this is the page load, not any page rerender
{
image_url = await TakeImage();
}
}
}
Special URL to automatically download image:
To generate a URL, which when loaded will download the image as the previous part, but only when that special URL is loaded, not the normal page URL.
To do this, I'm gonna use query strings, so the special URL will be like this: http://localhost:5001/page?img=true
For that, we need to get the URI, using NavigationManager, which can be injected like the IJSRuntime. For parsing the URI, we can use QueryHelpers class.
The final code will look like this:
#inject IJSRuntime JS
#inject NavigationManager Nav
#page "/component"
#code {
string image_url = "";
public async System.Threading.Tasks.Task<string> TakeImage()
{
return await JS.InvokeAsync<string>("takeScreenshot");
}
protected override async System.Threading.Tasks.Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender) //Ensure this is the page load, not any page rerender
{
var uri = Nav.ToAbsoluteUri(Nav.Uri);
if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("img", out var isImg))
{
if (System.Convert.ToBoolean(isImg.ToString()))
{
image_url = await TakeImage();
}
}
}
}
}
Now you can add ?img=true to the component's URL and you will get a screenshot of it.
Note that if the body/parent of the div has a background, and you want it to be in the image, you need to add background: inherit; to the CSS rules of the div containing the child component.
First of all I should say I have followed almost all the Questions and forum Post below
Stackoverflow Question 1
Stackoverflow Question 2
Stackoverflow Question 3
Stackoverflow Question 4
aspsnippets.com
Server Error in Application ... A potentially dangerous Request.Form value was detected
Avoiding the ‘A potentially dangerous Request.Form value was detected’
c-sharpcorner.com
A potentially dangerous Request.Form value was detected from the client in asp.net
all the thread mentioned to add <httpRuntime requestValidationMode = "2.0" /> or <pages validateRequest ="false" /> inside the web.config file , but this isn't working for me .
Once I did that and start debugging , getting this kind of error
Actually I'm trying to is Loading a HTML file into Rich Text Editor content then Once I click Save as PDF button saving that Rich Text Editor content to PDF file
these are the relevant controller class methods
[ValidateInput(false)]
public ActionResult output_xhtml()
{
PrepairEditor(delegate(Editor editor)
{
editor.LoadHtml("~/example.html");
});
return View();
}
[HttpPost]
[ValidateInput(false)]
public ActionResult output_xhtml(string m)
{
Editor theeditor = PrepairEditor(delegate(Editor editor)
{
});
theeditor.SavePDF("~/aaa.pdf");
return View();
}
PrepairEditor() method
protected Editor PrepairEditor(Action<Editor> oninit)
{
Editor editor = new Editor(System.Web.HttpContext.Current, "editor");
editor.ClientFolder = "/richtexteditor/";
editor.ContentCss = "/Content/example.css";
//editor.ClientFolder = "/Content/richtexteditor/";
//editor.ClientFolder = "/Scripts/richtexteditor/";
editor.Text = "Type here";
editor.AjaxPostbackUrl = Url.Action("EditorAjaxHandler");
if (oninit != null) oninit(editor);
//try to handle the upload/ajax requests
bool isajax = editor.MvcInit();
if (isajax)
return editor;
//load the form data if any
if (this.Request.HttpMethod == "POST")
{
string formdata = this.Request.Form[editor.Name];
if (formdata != null)
editor.LoadFormData(formdata);
}
//render the editor to ViewBag.Editor
ViewBag.Editor = editor.MvcGetString();
return editor;
}
//this action is specified by editor.AjaxPostbackUrl = Url.Action("EditorAjaxHandler");
//it will handle the editor dialogs Upload/Ajax requests
[ValidateInput(false)]
public ActionResult EditorAjaxHandler()
{
PrepairEditor(delegate(Editor editor)
{
});
return new EmptyResult();
}
this is screenshot of error occurring place in PrepairEditor() method
output_xhtml.cshtml view file
<!DOCTYPE html>
<html>
<head>
<title>RichTextEditor - Output XHTML</title>
</head>
<body>
<script type="text/javascript">
var editor;
function RichTextEditor_OnLoad(editor) {
editor = editor;
var content = true;
if (!content) {
setTimeout(function () {
editor.SetText("<table>.....</table>");
}, 1000);
return;
}
}
</script>
<script type='text/javascript'>
function RichTextEditor_OnLoad(editor) {
editor.SetWidth(1150); //Sets the width.
editor.SetHeight(612); //Sets the height.
}
</script>
#using (Html.BeginForm())
{
<div>
#Html.Raw(ViewBag.Editor)
<br />
<button id="btn_sumbit" type="submit" class="btn btn-danger submit">Save as PDF</button>
</div>
<br />
<div>
<h3>
Result html:
</h3>
<div>
#ViewBag._content
</div>
</div>
}
</body>
</html>
Once I did that and start debugging , getting this kind of error
Look at the error you are getting. You already have a <httpRuntime /> section in your web.config. You can't have two of them. Instead of adding a new one, change the existing one.
It is because you are passing HTML
add: [AllowHtml] above your method
[AllowHtml] goes on the property in your model not the controller method. Its namespace is System.Web.MVC
How can I get the value of a textbox using razor?
<div>
<input type="text" id="somevalue" name="somevalue" class="form-control"/>
<input type="button" value="Search" class="btn btn-success"/>
</div>
<ul id="ReportsList" class="nav">
#foreach (var item in Model){
var roomName= document.getElementByID('somevalue').value
if (item.roomName == roomName) {
<li class="errorItem">
<a href="#" class="list-group-item">
<i class="fa fa-warning fa-fw"></i> #Html.DisplayFor(modelItem => item.roomName)
<span class="pull-right text-muted small">#Html.DisplayFor(modelItem => item.roomCapacity) pers.
</span>
..........
}
Is it possible to get the value of the textbox using MVC Razor? Cause using the getElementByID doesn't seem to work in razor...
Don't be brought down by the down-ticks.
You are obviously new to Razor and Mvc & Javascript. You problem is that you are mixing a server-side language with a client-side language. Razor is a server-side language so you will not be able to access client-side code (ie html or javascript) using Razor. Razor is used to render html to the client browser. Think of the code that you see in a cshtml file as a template for the code that will become an html file. The javascript on the other hand will only run when it gets to the users browser.
Now, lets try to make some sense of your code.
<div>
<input type="text" id="somevalue" name="somevalue" />
<input type="button" value="Search" />
</div>
<ul id="ReportsList" class="nav">
#foreach (var item in Model)
{
var roomName= document.getElementByID('somevalue').value; // This is javascript code.
if (item.roomName == roomName) {
<li>
#Html.DisplayFor(modelItem => item.roomName)
#Html.DisplayFor(modelItem => item.roomCapacity)
</li>
}
}
</ul>
I removed the classes to make it more legible. The problem above is that you are trying to find a value to use with your razor code. That code is running before it gets to the browser so that won't work.
You cannot solve this problem using Razor. That means your DisplayFor's are going to be useless for your scenario.
You need javascript to solve the problem, so you will need to do away with the Razor Code. Assuming your Model has as list of object with the properties you created in your example, you could do something like this.
<script type="text/javascript">
var data = #(Html.Raw(Json.Encode(Model));
for(var o in data) {
var item = data[o];
// You need to create an element here and add it to the ul here
// You could use jquery.
}
</script>
Unfortunately, you have the wrong tools here.
To actually accomplish what you are trying to do you are going to be better off investing in some javascript frameworks. I suggest that you learn AngularJs to do this.
Concerning Organization of Javascript
As stated in the comments you can use a script tag in your cshtml file. Unfortunately, this is not your problem. I added a little bit of a way to organize your javascript as well.
your.cshtml file.
<script type="text/javascript">
.. getElementById in here and do something.
</script>
Better Organization Might Look Like This
Put the code in a javascript file. In this example the name is person.js. I am using a person example because it is an easy way to look at creating an usable object in javascript. In this case person is the object.
person.js
function Person() {
}
Person.prototype = {
// Sets the element with id = "nameId" to "Jim Bob"
setName: function() {
var element = document.getElementById("nameId");
// Now do something with it.
element.innerHTML = "Jim Bob"; // get some user input.
}
};
// You could initialize this as a global reference.
// I don't recommend this but it will be the easiest way for now.
var person = new Person();
Next, you would have to use it somehow. The simplest way to use it is not the best way.
<button id="setNameButton" onclick="person.setName()">Set Name</button>
Improved example using JQuery
This example will bind the event in an unobtrusive way (ie. you won't be mixing javascript and html).
function Person() {
this.initialize();
this.name = "Jim Bob";
}
Person.prototype = {
initialize: function() {
// get reference to this object.
var self = this;
// Set up the click for button.
$(document).on('click', "#setNameButton", function() {
// Set the name
self.setName();
});
}
// Sets the element to this.name field.
setName: function() {
var element = document.getElementById("nameId");
// Now do something with it.
element.innerHTML = this.name;
}
};
For several days I just can not find the problem itself, which is really driving me crazy.
I have asp.net (mvc4) web application, where there are several index pages (showing list), when clicking on any item in the list, it returns edit view page.
In the edit page (in the view itself) - there is a submit button, which should update the db and redirect to the index page.
(At first, that "submit" was loaded with all html edit code via partial view, but I changed it so i can redirect to index page - because "child actions are not allowed to perform redirect actions").
So the problem is that controller does not redirect to the index page.
When I put a breakpoint in the post function in controller, I see that few threads runs the code although not asked for threads, and if i stand with the cursor on one of the processes debug arrows, I can see message "the process or thread has changed since last step".
Somehow, I don't know how, I solved the problem in one page (dont know how, because I dont know what caused this), but sometimes (randomly) it's appears again, and in the other page I did not manage to solve it.
Here is some code from controller and from view:
Controller:
[HttpPost]
public ActionResult Edit([ModelBinder(typeof(OpportunityBinder))] OpportunityModel draft)
{
try
{
if (Request.Params["cancel"] != null)
{
return RedirectToAction("index", Utils.CrmDB.GetOpportunities());
}
if (draft.IsValid())
{
if (Utils.CrmDB.UpdateOpportunity(draft))
return RedirectToAction("Index", Utils.CrmDB.GetOpportunities());
}
return View(draft);
}
catch
{
return View();
}
}
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
/* Some divs */
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
<fieldset>
/* Some partial views*/
/* loaded using #Html.Action() */
</fieldset>
}
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/modernizr")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryui")
}
Partial view code:
#model Genius_crm.Models.ActivityListModel
<p>
<button id="dlgAddActivity">
New
</button>
</p>
<div>
<table>
/* some tr and td */
</table>
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#dlgAddActivity').each(function () {
var $link = $(this);
var pathname = window.location.pathname;
var parts = pathname.split('/')
var sub = parts[3]
var $dialog = $('<div id="addDialog"></div>')
.load('#Url.Action("AddActivityToOppPartial", "Opportunity")?CustId=#ViewBag.CustId&OppId=#ViewBag.OppId')
.dialog({
autoOpen: false,
title: $link.attr('title'),
width: 758,
height: 265,
resizable: false,
//close: function (event, ui) { window.location.reload();}
});
$link.click(function () {
$dialog.dialog('open');
return false;
});
});
});
So - I Hope I have made my problem clear.
I've been through some posts on the subject, and none of them helped me.
The problem appears also in chrome, and in IE too.
EDIT #1 -
When commenting out the partial views the problem disappears in all pages!
EDIT #2 -
Seems that there is a problem with buttons loaded in partials which using other controller actions.
EDIT #3 -
I have added code of partial view loaded with #Html.Action(), which include one script for jquery-ui dialog.
Oh lord ! It was a tough week, But at least I learned a lesson.
The problem was composed of two parts:
(1) EF and DataContext management (I re-tag the question).
I used a single static instance (singleton) of DataContext, and it turned out to be ineffective and problematic.
So I was looking for some information and found that there are better implementations for DataContext management (you can read here) and now - everything seems right!
(2) jQuery validating all partials on form submit. I had to find a way to handle it.
A solution of one part without the other - did not yield a good result.
$link.click(function () {
$dialog.dialog('open');
return false;
});
Should be like
$link.click(***return*** function () {
$dialog.dialog('open');
return false;
});
My content will be edited number of times.So i need to store result html to database and load it again when it is neccessary.Here is my current start implementation:
#using (#Html.BeginForm("EditArticle", "Admin", new { id = ViewData["id"] }))
{
<div id="editor"> </div>
<input type="submit" value="save changes" onclick = "setValue()" />
<input type ="hidden" id="value" name="html" />
}
<script>
var editor, html = 'Model.Text';
function createEditor() {
if (editor)
return;
var config = { width:"900px"};
editor = CKEDITOR.appendTo('editor', config,html);
}
function setValue() {
$("#value").val(editor.getData());
}
createEditor();
</script>
But I get eror with initialization html variable.So, could anyone show how correct encode / decode html?
EDIT
Here is data controller receives:
html = <p>ARTICLE 3</p>\r\n
Values like this I store in database and try insert again.
First things first, to fix your code syntactically, it should probably read something like:
var editor, html = '#Html.Raw(Model.Text)';
However, why not instead of dealing with the markup in JavaScript and having to escape and unescape it, dump it directly where it should go i.e.
<textarea name="editor1">#Html.Raw(Model.Text)</textarea>
<script>
CKEDITOR.replace( 'editor1' );
</script>
And then transform that textarea into your ckEditor? As per their basic example here: http://docs.ckeditor.com/#!/guide/dev_framed
Secondly, aside from that I am not sure what errors you are receiving with your controller, so you will have to post further details for me to help you beyond the above.
I hope this helps.