I face with CRUD operations in ASP Net Core web app when I use Telerik UI Editor, create sample text with some formatting it is stored well in DB.
But if I want to edit this text again telerik editor display it with html tags and without formatting.
Did someone have same issue?
Here is my code:
Controller
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var editorData = await _context.Prescriptions.FindAsync(id);
if (editorData == null)
{
return NotFound();
}
return View(editorData);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("PrescriptionID,PrescriptionText")] Prescription editorData)
{
if (id != editorData.PrescriptionID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(editorData);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!EditorDataExists(editorData.PrescriptionID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(editorData);
}
and View
#model WebApplication3.Models.Prescription
<h4>EditorData</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit" id="EditorDataForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="PrescriptionID" />
<div class="form-group">
<label asp-for="PrescriptionText"></label>
#Html.Kendo().EditorFor(m => m.PrescriptionText).Encoded(false)
<span asp-validation-for="PrescriptionText" class="text-danger k-invalid-msg" data-for="EditorContent"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="k-button k-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="EditorContent" class="k-button">Back to List</a>
</div>
when I run and create new it work
but If I want to edit it display like this
I have tried your code, it works for me. You can try my sample code, if it not works, it means there are something missed in your project.
First, I suggest you need set breakpoint at
π var editorData = await _context.Prescriptions.FindAsync(id);
this line in public async Task<IActionResult> Edit(int? id) method. And you need check the value of editorData.PrescriptionText. It should be like below.
<p>Hello world</p><p>My Sample</p><p><strong><em>Italic <span style="text-decoration:underline;">Underline</span></em></strong></p>
Second, you can try my sample code to check your configuration|static files in your project.
My HomeController code:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TelerikAspNetCoreApp1.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
Prescription a = new Prescription();
string aa = "<p>Hello world</p><p>My Sample</p><p><strong><em>Italic <span style=\"text-decoration:underline;\">Underline</span></em></strong></p>";
int id = 1;
a.PrescriptionText = aa;
return View(a);
}
public string edit = string.Empty;
public IActionResult Edit()
{
Prescription a = new Prescription();
a.PrescriptionText = HttpContext.Session.GetString("key");
return View(a);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("PrescriptionID,PrescriptionText")] Prescription editorData)
{
if (id != editorData.PrescriptionID)
{
return NotFound();
}
if (ModelState.IsValid)
{
string aa = editorData.PrescriptionText;
HttpContext.Session.SetString("key", aa);
return RedirectToAction(nameof(Edit));
}
return View(editorData);
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public IActionResult Error()
{
return View();
}
}
}
Edit.cshtml and Index.cshtml is same
#{
ViewData["Title"] = "Home Page";
}
#model TelerikAspNetCoreApp1.Prescription;
<h4>EditorData</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit" id="EditorDataForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="PrescriptionID" />
<div class="form-group">
<label asp-for="PrescriptionText"></label>
#Html.Kendo().EditorFor(m => m.PrescriptionText).Encoded(false)
<span asp-validation-for="PrescriptionText" class="text-danger k-invalid-msg" data-for="EditorContent"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="k-button k-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="EditorContent" class="k-button">Back to List</a>
</div>
Prescription.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TelerikAspNetCoreApp1
{
public class Prescription
{
public string PrescriptionText { get; set; }
public int PrescriptionID { get; set; }
}
}
Test Result:
Related
I have two Entities that have many To many relationship, Book and Tags asp Created automatically a table for the two entities, Using the code first approach
I am trying to add a collection of tags in the book creation, but the tag items are null also there is (select asp-for="Tags" ) but it shows me null in [httppost]create.
I tried to add it in through context as it catches the values of tags I add, but there is an error
cannot convert ......dbset<> to Models.tags
Code:
public class Book
{
public int BookID { get; set; }
[Required]
public string Name { get; set; } = null!;
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; }
}
public class Tags
{
public int TagsID { get; set; }
public string TagName { get; set; } = null!;
//Navigation property
public virtual ICollection<Book>? Book { get; set; }
}
//DB Context
public class BLabContext: DbContext
{
public DbSet<Book> Book { get; set; }
public DbSet<Tags> Tags { get; set; }
}
// Book Controller
public class BooksController : Controller
{
private readonly BLabContext _context;
public BooksController(BLabContext context)
{
_context = context;
}
// Tags objects
// public ICollection<Tags> Tags { get; set; }
// GET: Books
public async Task<IActionResult> Index()
{
return View(await _context.Book.ToListAsync());
}
// GET: Books/Create
(on get )
public IActionResult Create()
{
///πππππππ
var tags = _context.Tags.ToList();
ViewData["tags"] = tags;
//ViewBag.tags = tags;
return View();
}
// POST: Books/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("BookID,Name,Description,PublishedOn,Publisher,Price,ImageURL,1)πTagsπ")] Book book)
{
if (ModelState.IsValid)
{
2)π
var tags = _context.Tags;
_context.Add(book);
await _context.SaveChangesAsync();
2)π(cannot convert ......dbset<> to Models.tags
_context.Book.FirstOrDefault(b => b.BookID == book.BookID).Tags.Add(tags);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
The create view:
#using Book_Library.Models;
#model Book_Library.Models.Book
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
#* ///πππππππ*#
<div class="form-group">
<label asp-for="Tags" class="control-label"></label>
#*name="Tags"*#
<select asp-for="Tags" multiple>
#foreach (var tag in #ViewData["tags"] as IList<Tags>)
{
<option value="#tag.TagName">#tag.TagName </option>
}
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I expect to add a collection of tags to the book on creation, and also view those tags on the details view
You can create a ViewModel to achieve it, Please refer to this demo:
ViewModel
public class CreateBookViewModel
{
[Required]
public string BookName { get; set; }
public List<int> TagId { get; set; }
}
Controller
public IActionResult Create()
{
var tags = _context.Tags.ToList();
List<SelectListItem> dropdown = new List<SelectListItem>();
foreach (var item in tags)
{
var listItem = new SelectListItem();
listItem.Text = item.TagName;
listItem.Value = item.TagsID.ToString();
dropdown.Add(listItem);
}
ViewBag.tags = dropdown;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateBookViewModel book)
{
if (ModelState.IsValid)
{
Book b = new Book();
b.Name = book.BookName;
if (book.TagId.Count>0)
{
foreach (var item in book.TagId)
{
var tag = _context.Tags.Where(x => x.TagsID == item).FirstOrDefault();
b.Tags.Add(tag);
}
}
_context.Book.Add(b);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
View
#model CreateBookViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="BookName" class="control-label"></label>
<input asp-for="BookName" class="form-control" />
<span asp-validation-for="BookName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.TagId" class="control-label"></label>
<select asp-for="#Model.TagId" asp-items="#ViewBag.tags" multiple></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Now, You can add collection of Tags to Book successfully.
Note: You need to instantiate Tags in your Book class, Otherwise the above code will report a nullreferenceexception.
public class Book
{
//.........
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; } = new List<Tags>();
}
I have a method that I only use in one property where I upload photos so I only use the Photo property
I don't want to put the object as a parameter but a string that will be a replacement for the object and overwrite the Photo or in another way when using a Dto that fails to do gets null.
It's a function that works for me but I use the whole object as a parameter when I only use the PHOTO URL:
public async Task<string> UploadPhotos(IFormFile file, Product product)
{
string wwwPath = _environment.WebRootPath;
var path = Path.Combine(wwwPath, "images", file.FileName);
if (file.Length > 0)
{
using var stream = new FileStream(path, FileMode.Create);
await file.CopyToAsync(stream);
}
return product.PhotoUrl = file.FileName;
}
What I tried to do:
public async Task<string> UploadPhotos(IFormFile file, string photoUrl)
{
Product product = new()
{
PhotoUrl = photoUrl
};
string wwwPath = _environment.WebRootPath;
var path = Path.Combine(wwwPath, "images", file.FileName);
if (file.Length > 0)
{
using var stream = new FileStream(path, FileMode.Create);
await file.CopyToAsync(stream);
}
return product.PhotoUrl = file.FileName;
}
It doesn't work for me when I try to upload it doesn't show me the picture
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddProduct([FromForm] ProductViewModel model)
{
ModelState.Clear();
await _photoService.UploadPhotos(model.Photo,model.PhotoUrl);
if (ModelState.IsValid)
{
await _productRepository.AddProduct(model.Product!);
return RedirectToAction(nameof(Index));
}
return View();
}
ViewModel:
public class ProductViewModel
{
public Product Product { get; set; } = null!;
public string PhotoUrl { get; set; } = string.Empty;
public IFormFile Photo { get; set; } = null!;
}
View:
#model Solid_Project.Models.ProductViewModel
#{
ViewBag.Title = "AddProduct";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<article>
<section>
<form asp-action="AddProduct" enctype="multipart/form-data" method="post">
<div asp-validation-summary="ModelOnly"></div>
<div class="form-group">
<label asp-for="#Model.Product!.Name"></label>
<input asp-for="#Model.Product!.Name" class="form-control" placeholder="Enter Name" style="width:30%">
<span asp-validation-for="#Model.Product!.Name" class="validation"></span>
</div>
<br />
<div class="form-group">
<label asp-for="#Model.Product!.Description"></label>
<input asp-for="#Model.Product!.Description" class="form-control" placeholder="Enter Description" style="width:30%">
<span asp-validation-for="#Model.Product!.Description" class="validation"></span>
</div>
<br />
<div class="form-group">
<label asp-for="#Model.Product!.CategoryId"></label>
<div>
<select asp-for="#Model.Product!.CategoryId" asp-items="ViewBag.Category" style="width:30%" class="dropdown-toggle dropdown-toggle-design"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></select>
</div>
<span asp-validation-for="#Model.Product!.CategoryId" class="validation"></span>
</div>
<br />
<div class="form-group">
<label asp-for="#Model.Photo"></label>
<input type="file" asp-for="#Model.Photo" accept="image/*" class="form-control" style="width:30%" />
<span asp-validation-for="#Model.Photo" class="validation"></span>
</div>
<br />
<input type="submit" value="Create" class="btn btn-primary" />
</form>
</section>
</article>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
I am very new to this and I am trying to show multiple entries to show and have a single button for each entry to edit them. I thought it would be really easy but For some reason I am not getting the clicked entity. Is there way of getting modified entity without running javascript?
Any help will be appreciated. I couldn't find any example code that does this. There are many examples that returns all the entries but not the single element.
Here is my entity class Resource:
public class Resource
{
[Required]
public string title { get; set; }
[Required]
public int value { get; set; }
[Key]
[Required]
public int id { get; set; }
}
On the controller side I have:
[HttpGet]
public IActionResult Index()
{
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return View(resource);
}
Here is the View and EditorTemplates
#model learningMVC.Models.Resource[]
#{
ViewData["Title"] = "index";
}
<h1>index</h1>
<fieldset>
#Html.EditorFor(x => x);
</fieldset>
#addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
#model learningMVC.Models.Resource
<div>
First Name:<br />
<form asp-action="Index" asp-controller="Home">
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="Save" class="btn btn-primary" id="btnSubmit_#Model.id" />
</form>
</div>
In your case, you should use particalview instead of editfor, you can see my demo below.
_ResourcesPartical.cshtml:
#model Resource
<div>
<form method="post" asp-action="Index">
First Name:<br />
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="save" />
</form>
</div>
Your Index view:
#model List<Resource>
#foreach(var m in Model)
{
#await Html.PartialAsync("_ResourcesPartical", m)
}
Controller:
[HttpGet]
public IActionResult Index()
{
//here you get your data.below is my fake data.
var resources= new List<Resource>
{
new Resource
{
id = 1,
title = "aa",
value = 3
},
new Resource
{
id = 2,
title = "bb",
value = 4
}
};
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return RedirectToAction("Index");
}
Test result:
I want to make conditional validation in ASP.NET MVC Core 3.1. I have written custom validation and it works fine in server-side validation but I am unable to perform client-side validation. In my sample application, there is a Salary textbox, which is only required if the Role=Teacher is selected in the Roles dropdown. Could you please help me in the client-side validation part and here is the complete sample code.
Employee model class
public class Employee
{
public int Id { get; set; }
[Required(ErrorMessage = "Please enter name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter email")]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "Please enter role")]
[EnumDataType(typeof(Roles))]
public Roles? Role { get; set; }
[Required(ErrorMessage = "Please Enter Hire Date")]
[Display(Name = "Hire Date")]
public DateTime? HireDate { get; set; }
[RequiredIf("Role", Roles.Teacher, ErrorMessage = "Please enter salary")]
public int? Salary { get; set; }
}
Roles enum
public enum Roles
{
Student = 1,
Teacher = 2,
Assistant = 3
}
RequiredIfAttribute custom validation class
public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator
{
public string PropertyName { get; set; }
public object Value { get; set; }
public RequiredIfAttribute(string propertyName, object value, string errorMessage = "")
{
PropertyName = propertyName;
ErrorMessage = errorMessage;
Value = value;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var instance = validationContext.ObjectInstance;
var type = instance.GetType();
var proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue != null)
{
if (proprtyvalue.ToString() == Value.ToString() && value == null)
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-country", ErrorMessage);
}
}
Index view for the UI
#model ASPNETCoreValidations.Models.Employee
#using ASPNETCoreValidations.Models.enums
#{
ViewBag.Title = "Index";
}
<h1>Create</h1>
<h4>Business unit</h4>
<hr />
<div class="container">
<form asp-action="Index">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="col-md-6">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label asp-for="Role" class="control-label"></label>
<select asp-for="Role" class="form-control" asp-items="Html.GetEnumSelectList<Roles>()">
<option value="">Select Department</option>
</select>
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<div class="col-md-6">
<label asp-for="HireDate" class="control-label"></label>
<input asp-for="HireDate" class="form-control" />
<span asp-validation-for="HireDate" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label asp-for="Salary" class="control-label"></label>
<input asp-for="Salary" class="form-control" />
<span asp-validation-for="Salary" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/js/RequiredIfValidate.js"></script>
}
Jquery for client-side validation
jQuery.validator.addMethod("requiredif",
function (value, element, param) {
// I need help here. This method never gets executed and I don't know how to implement validation here ...
// return true or false depending on the condition
});
jQuery.validator.unobtrusive.adapters.addBool("requiredif");
Index action
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(Employee employee)
{
if (ModelState.IsValid)
{
RedirectToAction("Index");
}
return View();
}
According to your description, I suggest you could add a property in the RequiredIfAttribute AddValidation attribute to add the role to the input salary.
Then I suggest you could try to use below validation unobtrusive scripts:
RequiredIfAttribute :
public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator
{
public string PropertyName { get; set; }
public object Value { get; set; }
public RequiredIfAttribute(string propertyName, object value, string errorMessage = "")
{
PropertyName = propertyName;
ErrorMessage = errorMessage;
Value = value;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var instance = validationContext.ObjectInstance;
var type = instance.GetType();
var proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue != null)
{
if (proprtyvalue.ToString() == Value.ToString() && value == null)
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-country", ErrorMessage);
context.Attributes.Add("data-val-country-role", Value.ToString());
}
}
Scripts:
#section Scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
<script>
$.validator.addMethod('country', function (value, element, params) {
var genre = $(params[0]).val(), role = params[1], salar = value;
var selecttest = $("#Role option:selected").text();
if (selecttest == role) {
if (value.length == 0) {
console.log("selecttest == role value = null");
return false;
} else {
console.log("selecttest == role value != null");
return true;
}
} else {
console.log("selecttest != role");
return true;
}
});
$.validator.unobtrusive.adapters.add('country', ['role'], function (options) {
var element = $(options.form).find('select#Salary')[0];
options.rules['country'] = [element, options.params['role']];
options.messages['country'] = options.message;
});
</script>
}
Result:
I have the following ViewModel
public class EditPatientViewModel
{
public Domain.Entities.Patient patient;
public IEnumerable<Espece> Especes;
public IEnumerable<Client> Clients;
}
the following controller
public ViewResult Edit(int Id_pat)
{
var ViewModel = new EditPatientViewModel();
ViewModel.patient = patientRepo.GetPatientById(Id_pat);
ViewModel.Especes = especeRepo.Especes;
return View(ViewModel);
}
[HttpPost]
public ActionResult Edit(EditPatientViewModel editPatientViewModel)
{
if (ModelState.IsValid)
{
patientRepo.Save(editPatientViewModel.patient);
TempData["message"] = "SauvΓ©";
return RedirectToAction("Index");
}
else
{
return View(editPatientViewModel);
}
}
and the following view
#model Veto.Models.ViewModels.EditPatientViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit Patient</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-10">
#Html.HiddenFor(m => m.patient.Id_pat)
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(m => m.patient.Nom_pat)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Every time I submit the form the ViewModel posted is not null but attributes are.. I would like to retrieve the attributes to make an update.
Why?
Tx,
Two same problem in one hour :)
Change your ViewModel to this:
public class EditPatientViewModel
{
public Domain.Entities.Patient patient { get; set; }
public IEnumerable<Espece> Especes { get; set; }
public IEnumerable<Client> Clients { get; set; }
}
In complex types, mvc model binder search for properties not for member variables.