Posting Html with images (WYSIWYG) to ASP.net core controller - forms

I'm having trouble posting my wysiwyg content to my controller in asp.net core. I can't seem to get any value from the form editor. The value for the Content property comes to the controller as null. I'm using the summernote form editor to handle my richtext box editor.
Here is my code
public class Editor
{
public int EditorId { get; set; }
public string Content { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Editor editor)
{
if (ModelState.IsValid)
{
_context.Add(editor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(editor);
}
View:
<h2>Create</h2>
<h4>Editor</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Content" class="control-label"></label>
<textarea asp-for="Content" id="summernote" name="editordata"></textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script>
$(document).ready(function () {
$('#summernote').summernote();
});
</script>
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.9/summernote.js"></script>
}
#section Styles{
<!-- include libraries(jQuery, bootstrap) -->
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
<!-- include summernote css/js -->
<link href="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.9/summernote.css" rel="stylesheet">
}
So the issue is when I post the form it's gets to the controller but the content comes over as null. I'm not sure how to post the content
Here are my thoughts, I'm thinking i'm missing a some attribute that allows html to come over the wire to my controller, but all the reserach i've found is that asp.net core doesn't require that. Or I need to handle this type of request in the middleware pipeline, but that doesn't make much sense since it's just html strings i'm sending over the wire to the controller.

It looks like the top of your view was not left out, I assume you have Editor as model.
The problem is on your text area you are using both asp-for and then setting the id and name to something that doesn't match your model property.
You should just use asp-for and let it decide the id and name instead of adding those yourself.
What is really getting posted is a string named editordata because you used that name on the textarea. remove that and it will be named Content to match the property of the model
You also don't need the [Bind] attribute shown in the controller action in your screenshot.

I have been sitting with the same issue and was able to resolve it due to Joe's answer!
Could I suggest working on the summernote class for the text area instead of using your id?
I noticed when I use the id that my textarea's display property doesn't get set to none, but it works when i use the class="summernote".
<textarea asp-for="Instructions" class="summernote"></textarea>
<script>
$(document).ready(function () {
$('.summernote').summernote();
});
</script>

Put this script in your page head:
<script src="https://cdn.ckeditor.com/4.13.0/standard/ckeditor.js"></script>
Lets say you have model called ForumModel where you save contents of editor. Property where you your content is saved is called answer:
public string Answer { get; set; }
So in your view you have following tag:
#model ForumModel
Therefore if you want to add editor:
<textarea id="editor1" asp-for="#Model.Answer" class="form-control" required=""></textarea>
<script>
CKEDITOR.replace("editor1");
</script>
And now all that is left is to call your controller on submit button. When your form is submitted you go to constructor that saves your contents.
public IActionResult Reply(ForumModel forumModel)
{
forumModel.SaveReply();
return RedirectToAction("SomeRandomPage");
}

Related

Dynamic queries and JpaSpecificationExecutor in Spring

I am trying to create a simple Spring project where restaurants can add menu items to shared database and users can use a html form to search the dishes based on a range of criteria- especially dietary requirements
Form example:
Restaurant Name: Chez Hans
Gluten Free: (X)
Egg Free: (X)
Vegan: ()
Example SQL command
Select all FROM "dishes" Dish WHERE restaurant_name = "Chez Hans" AND egg_free = TRUE AND
gluten_Free = TRUE;
A list of dishes that fit their criteria would then be returned to the user.
Any field in the form can be left blank, and not checking a box for example, "vegan" does not mean that criteria should be set as 'false', but rather not included within the query.
Because of this it seemed the best way to handle the issue was using JpaSpecificationExecutor to create dynamic SQL queries (similar to the implementation in the answer to the problem below)
Filtering database rows with spring-data-jpa and spring-mvc
I have created a solution based on my research and prior knowledge. However, when I implement my solution, no dishes are returned- even though there are dishes in the database that fit the search criteria. No errors are being produced, but simply a blank table, so I am not sure where I am going wrong.
I have researched countless articles/videos regarding JpaSpecificationExecutor/dynamic queries but there are parts of the that theory I am still unsure about. This is what I gather about JpaSpecificationExecutor/dynamic queries (but I may be wrong)
The meta model is not need to create dynamic queries but to verify the correctness of database query statements
To create queries using meta-model classes a wrapper class is required (In my example- DishSearch)
The following lines are to cast metamodel SingularAttribute class back to the original class value.
Path dname = root.get(Dish_.dname);
Path vegan= root.get(Dish_.vegan);
I am quite new to Spring and still finding it pretty difficult. Any help or advice would be very much appreciated!
Please see below my DishSpecification class:
package com.bron.demoJPA.specification;
public class DishSpecification implements Specification<Dish> {
private final DishSearch criteria;
public DishSpecification(DishSearch ds) {
criteria =ds;
}
#Override
public Predicate toPredicate(Root<Dish> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
Path<String> dname = root.get(Dish_.dname);
Path<Boolean> vegan= root.get(Dish_.vegan);
Path<Boolean> eggFree= root.get(Dish_.eggFree);
Path<Boolean> glutenFree= root.get(Dish_.glutenFree);
final List<Predicate> predicates = new ArrayList<Predicate>();
if(criteria.getDname()!=null) {
predicates.add(cb.equal(dname, criteria.getDname()));
}
if(criteria.isVegan()!=false) {
predicates.add(cb.equal(vegan, criteria.isVegan()));
}
if(criteria.isEggFree()!=false) {
predicates.add(cb.equal(eggFree, criteria.isEggFree()));
}
if(criteria.isGlutenFree()!=false) {
predicates.add(cb.equal(glutenFree, criteria.isGlutenFree()));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
Please see my DishSearch Class:
package com.bron.demoJPA.specification;
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class DishSearch {
private Long dishId;
private String dname;
private String description;
private double price;
private boolean vegan;
private boolean glutenFree;
private boolean eggFree;
private AppUser app;
}
Please see my SearchController Class:
#Controller
public class SearchController {
#Autowired
DishRepository drep;
#GetMapping("/showSearchForm")
public String showNewDishForm(Model model) {
// Create model attribute to bind form data
DishSearch dishSearch = new DishSearch();
model.addAttribute("dishSearch", dishSearch);
return "search_Dish";
}
#PostMapping("/showDishList")
public String saveUser(#ModelAttribute("dishSearch")DishSearch dishSearch) {
Specification<Dish> spec = new DishSpecification(dishSearch);
drep.findAll(spec);
return "show_dish_List";
}
}
Please see my DishRepository Class:
#Repository
public interface DishRepository extends JpaRepository<Dish, Long>, JpaSpecificationExecutor<Dish>{
#Transactional
#Modifying
List<Dish> findAll(Specification<Dish> spec);
}
Please see my search_Dish.html Thymeleaf Template:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Dish Management System</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<br>
<div class="col-sm-10 offset-sm-1 text-center">
<div class="container">
<h2> Manage Dishes </h2>
<hr>
</div>
<form action="#" th:action="#{/showDishList}" th:object="${dishSearch}" method="POST">
<div class="col-sm-10 offset-sm-1 text-center">
<input type="text" th:field="*{dname}"
placeholder="Dish Name" class="form-control mb-4 col-10">
</div>
<div class="form-check form-check-inline">
<label class=" form-check-label" for="inlineCheckbox1 ">Vegan?</label>
<input type="checkbox" th:field="*{vegan}" />
<label class="form-check-label" for="inlineCheckbox1">Gluten Free?</label>
<input type="checkbox" th:field="*{glutenFree}" />
<label class="form-check-label" for="inlineCheckbox1">Egg Free?</label>
<input type="checkbox" th:field="*{EggFree}" />
</div>
<br>
<br>
<br>
<br>
<button type="submit" class="btn btn-info col-4"> Search Database</button>
</form>
</div>
<hr>
</body>
</html>
Please see my show_dish_List.html Thymeleaf Template:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Search Results</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
<br>
<div class="col-sm-10 offset-sm-1 text-center">
<h1>Dish List</h1>
</div>
<table border="1" class="table table-striped table-responsive-md">
<thead>
<tr>
<th>Dish Name</th>
<th>Dish description</th>
<th>Dish Price</th>
<th>Restaurant</th>
</tr>
</thead>
<tbody>
<tr th:each="dishSearch : ${listDishSearch}">
<td th:text="${dishSearch.dname}"></td>
<td th:text="${dishSearch.description}"></td>
<td th:text="${dishSearch.price}"></td>
</tr>
</tbody>
</table>
<div class="col-sm-10 offset-sm-1 text-center">
<a th:href="#{/showNewDishForm}"
class="btn btn-primary btn-sm mb-3"> Search Again</a>
</div>
----------------------------Update------------------------------
In addition to the answer provided below, in the Dish Specification Class I changed
if(criteria.getDname()!=null) {
predicates.add(cb.equal(dname, criteria.getDname()));
}
to
if(criteria.getDname()!="") {
predicates.add(cb.equal(dname, criteria.getDname()));
}
and the search is working fine now!
I believe the issue is that you are not adding the result in the Model which is being used to render the page show_dish_List.html, therefore nothing is being populated in the UI. Your UI is expecting the data to be in listDishSearch and there is nothing in that variable.
Update your code to:
#PostMapping("/showDishList")
public String saveUser(#ModelAttribute("dishSearch") DishSearch dishSearch, Model model) {
Specification<Dish> spec = new DishSpecification(dishSearch);
model.addAttribute("listDishSearch", drep.findAll(spec));
return "show_dish_List";
}
and everything should be working fine.
Remove the method findAll from your DishRepository repository. It is already being provided by the interface JpaSpecificationExecutor.

How to generate empty div for modal window when page is rendered first time?

Are you able to suggest how to generate an empty 'div' tag for a modal window when the page is rendered the first time?
'div' content will be fetched with ajax request.
I would like to avoid fetching modal window content twice - first when the page is rendered and then when I click on the link to show modal window.
I do not use wicket modal window implementation.
I have a base ModalBorder class.
ModalBorder.java
public class ModalBorder extends Border {
public ModalBorder(String id) {
super(id);
}
}
with ModalBorder.html markup
<wicket:border>
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<wicket:body/>
</div>
</div>
</wicket:border>
AboutAppModalPanel.java
public class AboutModalPanel extends Panel {
public AboutModalPanel(String id) {
super(id);
setOutputMarkupId(true);
var mb = new ModalBorder("modalBorder");
mb.setRenderBodyOnly(true);
add(mb);
}
}
AboutAppModalPanel.html
<wicket:panel>
<div wicket:id="modalBorder">
<article class="media">
<div class="media-content">
<div class="content">
<p>
<strong>
About application content
</strong>
</p>
</div>
</div>
</article>
</div>
</wicket:panel>
I would like to achieve the below output:
MainPage.html
Page is rendered first time (with empty div)
<html>
<body>
...
<div class="modal" id="aboutAppModalPanel2">
<!-- modal is empty and hidden (not-active) -->
</div>
</body>
</html>
and then requesting modal content via ajax request
<html>
<body>
...
<div class="modal is-active" id="aboutAppModalPanel2">
<!-- modal is filled in and SHOWN (added 'is-active' class) -->
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<p><strong>About application content</strong>
...
</div>
</body>
</html>
but now I am facing this issue:
Page is rendered first time (with filled in div)
<html>
<body>
...
<div class="modal" id="aboutAppModalPanel2">
<!-- modal is filled in and HIDDEN (not-active) -->
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<p><strong>About application content</strong>
...
</div>
</body>
</html>
and then requesting modal content via ajax request
<html>
<body>
...
<div class="modal is-active" id="aboutAppModalPanel2">
<!-- modal is filled in and SHOWN (added 'is-active' class) -->
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<p><strong>About application content</strong>
...
</div>
</body>
</html>
Are you able to suggest a solution?
I use Wicket 9.
Add an empty WebMarkupContainer instead of your panel to the page, making sure you call setOutputMarkupId(true) so it can be refreshed in an ajax call. Then replace it with your panel in the Link's onClick(AjaxRequestTarget target) method.
thanks for a tip. I needed to change my implementation but now it works.
// In page constructor:
// Instead of empty WebMarkupContainer you suggested an empty panel with setOutputMarkupId(true)
add(new AjaxEmptyPanel("aboutApp"));
add(showAboutAppLink());
// and AjaxLink:
private AjaxLink<String> showAboutAppLink() {
return new AjaxLink<>("showAboutApp", new ResourceModel("SHOW")) {
private static final long serialVersionUID = -3035458028030059416L;
#Override
public void onClick(AjaxRequestTarget ajaxRequestTarget) {
var aboutApp = new AboutAppModalPanel("aboutApp");
aboutApp.setOutputMarkupId(true);
getPage().replace(aboutApp);
ajaxRequestTarget.add(aboutApp);
}
};
}

create custom durandal modal

I am trying to develop custom modal plugin for durandal 2.1 to have my own logic and abstract it from the rest of my app here is what I have so far but something does not work and modal gets inserted in DOM twice
define(['jquery', 'knockout', 'plugins/dialog'], function ($, ko, dialog) {
var modal = {
install: function (config) {
dialog.addContext("Modal", {
addHost: function (theDialog) {
var body = $("body");
$('<div id="Dialog" class="AlignC"><div class="ModalHost"></div></div>').appendTo(body);
theDialog.host = $('#Dialog').get(0);
},
removeHost: function (theDialog) {
alert("demoving host");
$("#Dialog").remove();
},
compositionComplete: function (child, parent, context) {
var theDialog = dialog.getDialog(context.model);
}
});
}
};
return modal;
});
and here is how i call it from my viewmodel
dialog.show(this, null, 'Modal');
can anyone tell me what is wrang with this code why my model ELEMENT is inserted twice and ovelay each other. how can i fix that.
second element does not have content inside.
by the way here is view I am trying to show inside modal
<span class="Loader"></span>
<div class="Modal">
<h2 class="Caps">SomeName</h2>
<div class="Row">
<input type="text" />
</div>
<div class="Desc">
description
<br />
XXY YYX XXY
</div>
<div class="Buttons">
<span class="Green">Check</span>
<span>Add</span>
</div>
</div>
Ok I managed to fix this behavior. the problem with click binding was firing twice and this was the problem associated with inserting tow HTML elements in DOM after fixing click handler, everything works just fine.

How to use Parsley.js with dynamic content

I am using parsley.js 2.0.2. for client side form validation.
Now I noticed on the Parsley Website that parsley 2.x has dynamic form validation.
I have a form on my page with parsley. It works correctly and does validate. Now on the same page I have a link that dynamically adds a form from an external file. Issue is now parsley.js won't validate the newly added form.
On the parsley website they have an example where one can use JavaScript to validate but I tried it and it does not work. Here is the snippet code of the example:
<script src="jquery.js"></script>
<script src="parsley.min.js"></script>
<form id="form">
...
</form>
<script type="text/javascript">
$('#form').parsley();
</script>
I am aware that the content in the DOM changed but is there a way that I can tell parsley to validate this newly added form or something that will trigger the validation process?
I will appreciate the help!
Thanks
Here is my form on the index.php page (This form does successfully validate):
<form action="server.php" method="post" name="main-form" id="myForm" data-parsley-validate>
<div>
<label for="njsform-name">Name</label>
<input name="name" type="text" id="njsform-name" placeholder="Mike" data-parsley-required="true" data-parsley-minlength="2">
</div>
<div>
<label for="njsform-email">Surname</label>
<input name="email" type="text" id="njsform-email" placeholder="Gates" data-parsley-required="true" parsley-minlength="2">
</div>
<div class="submitWrap">
<input class="submit" type="submit" value="Apply Now" />
</div>
Here is the link that gets the external content
<ul class="services-list">
<li><a class="s-option" href="views/form-short_term_loans.php">My Link</a></li>
</ul>
Here is the code I am using to dynamically change the content (does successfully retrieve external form and populates):
$(document).ready(function() {
var hash = window.location.hash.substr(1);
var href = $('.services-list li a').each(function(){
var href = $(this).attr('href');
if(hash==href.substr(0,href.length-5)){
var toLoad = hash+'.html #form-section';
$('#form-section').load(toLoad)
}
});
$('.services-list li a').click(function(){
var toLoad = $(this).attr('href')+' #form-section';
$('#form-section').hide('fast',loadContent);
$('#load').remove();
$('#intro-section').append('<span id="load">Getting required form...</span>');
$('#load').fadeIn('normal');
window.location.hash = $(this).attr('href').substr(0,$(this).attr('href').length-5);
function loadContent() {
$('#form-section').load(toLoad,'',showNewContent())
}
function showNewContent() {
$('#form-section').show('normal',hideLoader());
}
function hideLoader() {
$('#load').fadeOut('normal');
}
return false;
});
});
The second form is just a duplicate but the form id is myForm2 and the name second-form
Add a call to
$('#xxxxxx').parsley();
After the load of the new form. With xxxxx the id of the new form inserted in the DOM

ASP.NET MVC2 +file uploading (HttpPostedFileBase class)

I have problem with uploading my file. I want to upload it from my edit view:
<%
using (Html.BeginForm("edit","profile",FormMethod.Post, new { enctype="multipart/form-data" }))
{%>
<%: Html.ValidationSummary(true) %>
<%: ViewData["ErrorMessage"] %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Image) %>
</div>
<div class="editor-field">
<input type="file" id="Image" name="Image" />
<label id="LabelErrorImage" class="errorMessage" />
</div>
<p>
<input type="submit" value="Save" onclick="return Validate(); return false;"/>
</p>
</fieldset>
<% } %>
I want to use HttpPostedFileBase class. My edit action:
[Authorize]
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string id, HttpPostedFileBase file, FormCollection formValues)
{
if (ModelState.IsValid)
{
if (file != null && file.ContentLength > 0)
{
CustomHelpers.createFolder();
var tmpPath = MyConfig.UPLOAD_FILE_PATH + "/" + Membership.GetUser().ProviderUserKey.ToString();
var path = Path.Combine(Server.MapPath(MyConfig.UPLOAD_FILE_PATH), "Avatar");
var fileExtension = Path.GetExtension(file.FileName);
file.SaveAs(path);
user.Image = "Avatar";
}
adventureDB.SaveChanges();
return RedirectToAction("Index");
}
}
But I always have empty the file object, why????? Do you have any ideas, suggestions why it can work like that? Maybe there is problem how I pass on the file value to my Edit action?
EDIT:
IT IS REALLY STRANGE AS EVEN WHEN I REMOVE
using (Html.BeginForm("Index","Profile",FormMethod.Get, new { enctype="multipart/form-data" }))
The page source still has:
<body>
<form method="post" action="6111e591-b92d-4bcb-b214-ab8f664b35f9" id="form1">
I mean I can not change the tag but have no idea why :/
Try changing:-
public ActionResult Edit(string id, HttpPostedFileBase file,
FormCollection formValues)
to:-
public ActionResult Edit(string id, HttpPostedFileBase image,
FormCollection formValues)
as the name of your input is image
<input type="file" id="Image" name="Image" />
edit
To be honest something else is stopping the binding of image. Is this the whole form that you have posted?
A few things to test
You have HTTPOST decorating your method twice, although I don't believe this should make a difference.
View the source and make sure there is nothing else named name=image in the source.
Make sure you empty your cache and make sure source is correct before testing again
Try using <form action="/profile/index" method="post" enctype="multipart/form-data">
Judging by your last edit you have a problem with master pages/layout? Is this a mvc/webforms hybrid?
The solution of this problem when:
We use Master.Site,
We want to upload file in a view,
We are sure that it should work but we all the time has null,
Then:
Guys were right - I had wrong name in my view - check it!
Check source code of your view and if you have 2 < form > tags you should remove the < form > tag from Master site as then the second one is ignored!
Now it should work.
Well, in your view you named the file input 'image' but your action method accepts a parameter called 'file'. Rename one of those and it should work.