i am a beginner in spring mvc and was struck with a error i was not able to pass data to controller using ajax.please find the code and suggest the possible solutions.
<form class="form-horizontal bucket-form" id="myform" method="post" >
<div class="control-label text-center">
<p class="required"><em>required fields</em></p></div>
<div class="form-group">
<label class="col-sm-3 control-label required">First Name</label>
<div class="col-sm-6">
<input type="text" name="firstname" id="firstname" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Last Name</label>
<div class="col-sm-6">
<input type="text" name="lastname" id="lastname" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Gender</label>
<span style="padding-left:15px">
<label class="radio-inline">
<input type="radio" name="gender" id="gender" >
Male
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender" >
Female
</label>
</span>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Email id</label>
<div class="col-sm-6">
<input type="email" class="form-control" placeholder="" name="mail" id="mail" >
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Profession</label>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="" name="prof" id="prof" >
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label ">Organisation</label>
<div class="col-sm-6">
<input type="text" name="org" id="org" class="form-control" placeholder="eg:Tech Mahindra">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label ">Experience</label>
<div class="col-sm-6">
<input type="number" class="form-control" min="0" name="exp" id="exp" placeholder="">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Achievements</label>
<div class="col-sm-6">
<textarea class="form-control" rows="5" name="ach" id="ach" placeholder=""></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label required">Event ID</label>
<div class="col-sm-3">
<input type="number" class="form-control" name="eid" id="eid" >
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Upload image</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="forImage" accept="image/*">
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-lg-6">
<button class="btn btn-primary" id="speakerSubmit" type="submit">Save</button>
<button type="reset" class="btn btn" onClick="myFunction()">Reset</button>
<script type="text/javascript">
function myFunction() {
document.getElementById("myform").reset();
}
</script>
</div>
</div>
</form>
</div>
</section>
</section>
<div class="footer">
<div class="row">
<div style="float:left" class="col-md-6"><p
style="padding:15px">© 2017.All rights reserved | Powered by TechMahindra </div> </p>
<div class="social col-md-6 " style="float:right">
</div>
</div>
</div>
</section>
<script src="js/jquery2.0.3.min.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script src="js/bootstrap.js"></script><!--bootstrap script-->
<script src="js/jquery.dcjqaccordion.2.7.js"></script>
<script src="js/scripts.js"></script>
<script src="js/jquery.nicescroll.js"></script>
<script>
$( document ).ready(function() {
$("#speakerSubmit").click(function(){
alert("create ready");
alert("data="+window.opener);
var firstname=document.getElementById("firstname").value;
alert("Firstname " +firstname );
var lastname=document.getElementById("lastname").value;
alert("lastname " +lastname);
var mail=document.getElementById("mail").value;
alert("mail " +mail);
var prof=document.getElementById("prof").value;
alert("prof " +prof);
var org=document.getElementById("org").value;
alert("org "+org);
var exp=document.getElementById("exp").value;
alert("exp " +exp);
var ach=document.getElementById("ach").value;
alert("ach "+ach);
var eid=document.getElementById("eid").value;
alert("eid "+eid);
var gender="M";
//var ge=document.getElementsByName("optionsRadios").value; //1 n as _
var details={firstname:firstname,lastname:lastname,gender:gender,mail:mail,prof:prof,org:org,exp:exp,ach:ach,eid:eid};
alert(details.FNAME+" "+details.LNAME+" "+details.GENDER+" "+details.SPKR_MAILID+" "+details.PROFESSION+" "+details.ORGN+" "+details.EXP+" "+details.ACHIEVMNTS+" "+details.SPKR_EVNT_ID);
var res=JSON.stringify(details);
alert("mytxt "+res);
//alert(res.FNAME+" "+res.LNAME+" "+res.GENDER+" "+res.SPKR_MAILID+" "+res.PROFESSION+" "+res.ORGN+" "+res.EXP+" "+res.ACHIEVMNTS+" "+res.SPKR_EVNT_ID);
$.ajax({
url: "http://localhost:8080/EMS_APP/spkr",
type: "POST",
contentType:"application/json",
data : res,
success: function(data, status) {
if(data){
//window.open("CreateEvent.jsp","_self"); //using "_self" or "_parent" will open in same window and same tab
// window.open("CreateEvent.jsp","_self");
alert("inserted");
}else{
//window.open("index.jsp","_self");
alert("not inserted");
}
},
error: function(e) {
console.log("error");
}
});
});
});
</script>
Speaker Class
package com.ems.DO;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name="ems_spkr_tbl")
public class Speaker {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="SPKR_ID",nullable=false)
private int speakerId;
#Column(name="FNAME")
private String firstname;
#Column(name="LNAME")
private String lastname;
#Column(name="GENDER")
private String gender;
/*private String DOB;*/
#Column(name="SPKR_MAILID")
private String mail;
#Column(name="PROFESSION")
private String prof;
#Column(name="ORGN")
private String org;
#Column(name="EXP")
private String exp;
#Column(name="ACHIEVMNTS")
private String ach;
#Column(name="SPKR_EVNT_ID")
private int eid;
public Speaker(){
}
public Speaker(int speakerId, String firstname, String lastname, String
gender, String mail, String prof,String org, String exp, String ach, int
eid) {
super();
this.speakerId = speakerId;
this.firstname= firstname;
this.lastname = lastname;
this.gender = gender;
this.mail = mail;
this.prof = prof;
this.org = org;
this.exp = exp;
this.ach = ach;
this.eid = eid;
}
/*#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="SPKR_ID",nullable=false)*/
public int getSpeakerId() {
return speakerId;
}
public void setSpeakerId(int speakerId) {
this.speakerId = speakerId;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getProf() {
return prof;
}
public void setProf(String prof) {
this.prof = prof;
}
public String getOrg() {
return org;
}
public void setOrg(String org) {
this.org = org;
}
public String getExp() {
return exp;
}
public void setExp(String exp) {
this.exp = exp;
}
public String getAch() {
return ach;
}
public void setAch(String ach) {
this.ach = ach;
}
public int getEid() {
return eid;
}
public void setEid(int eid) {
this.eid = eid;
}
}
SpeakerController
Controller class where i am trying to read the values passed from jsp page using #RequestParam("att-name") annotation and used the same name in ("attribute-name") but was struck with error like 400 Status.
package com.ems.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ems.DO.Speaker;
import com.ems.service.SpeakerService;
#Controller
public class SpeakerController {
#Autowired
SpeakerService speakerService;
#RequestMapping(value="/spkr",method=RequestMethod.POST)
public #ResponseBody boolean speakerAdd(#RequestParam("firstname") String
FNAME,#RequestParam("lastname") String LNAME,#RequestParam("gender")
String GENDER,#RequestParam("mail") String
SPKR_MAILID,#RequestParam("prof") String PROFESSION,#RequestParam("org")
String ORGN,#RequestParam("exp") String EXP,#RequestParam("ach") String
ACHIEVEMENTS,#RequestParam("eid") String eventid) {
System.out.println("in speakercontrol");
Speaker s=new Speaker();
int i=Integer.parseInt(eventid);
/*s.setfirstname(FNAME);
s.setlastname(LNAME);
s.setgender(GENDER);
s.setspkrmailid(SPKR_MAILID);
s.setprofession(PROFESSION);
s.setorgn(ORGN);
s.setexp(EXP);
s.setachievements(ACHIEVEMENTS);
s.setspkrevntid(eventid);*/
s.setFirstname(FNAME);
s.setLastname(LNAME);
s.setGender(GENDER);
s.setMail(SPKR_MAILID);
s.setProf(PROFESSION);
s.setOrg(ORGN);
s.setExp(EXP);
s.setAch(ACHIEVEMENTS);
s.setSpeakerId(i);
System.out.println(eventid);
System.out.println("Ajax");
System.out.println("----In controller----");
boolean status=speakerService.add(s);
return false;
}
}
SpeakerserviceImpl
package com.ems.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.ems.DO.Speaker;
import com.ems.dao.SpeakerDao;
#Repository
public class SpeakerServiceImpl implements SpeakerService{
#Autowired
SpeakerDao speakerDao;
public SpeakerServiceImpl() {
super();
// TODO Auto-generated constructor stub
}
public boolean add(Speaker s) {
//System.out.println(fname +" "+skill);
System.out.println("----In service----");
boolean status=speakerDao.add(s);
return status;
}}
SpeakerdaoImpl
package com.ems.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.ems.DO.Speaker;
#Repository
public class SpeakerDaoImpl implements SpeakerDao{
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public boolean add(Speaker s) {
boolean status=false;
Session session = sessionFactory.getSessionFactory().openSession();
session.save(s);
System.out.println(s.getAch()+" "+s.getMail()+" "+s.getSpeakerId());
System.out.println("one record inserted");
return true;
}
}
Error Result
The Request sent by client is syntactically incorrect
status-400
Controller
#PostMapping("EMS_APP/spkr")
public ResponseEntity addSpeaker(#RequestBody SpeakerAddRequest request) {
Speaker speaker = new Speaker();
speaker.setFirstName(request.getFirstName());
...
}
What is happening?
#PostMapping is short for #RequestMapping(method = RequestMethod.POST).
#PostMapping("EMS_APP/spkr") is short for #RequestMapping(value = "EMS_APP/spkr", method = RequestMethod.POST).
This specifies the URL and HTTP method on which the method will be called.
#RequestBody is used to map the payload of the POST request to a POJO. In this case, the POJO is SpeakerAddRequest.
You misused #RequestParam. #RequestParam is used to map parameters in the URL. For example:
// GET request with URL ../EMS_APP/spkr?name=foo
#GetMapping("EMS_APP/spkr")
public ResponseEntity<Speaker> getSpeakerByName(#RequestParam("name") String speakerName) {
// speakerName== "foo"
}
Data transfer object (DTO)
public class SpeakerAddRequest {
#JsonProperty("firstname")
private String firstName;
...
public String getFirstName() {
return firstName;
}
}
What is happening?
The mapping of the POST payload to the POJO happens here. For example, property"firstname": "John" will get mapped to private String firstName because the property key is the same as the key specified in #JsonProperty
Form
Replace your ajax call
$.ajax({
...
});
with
$.post("EMS_APP/spkr", $("#myform").serialize());
What is happening?
Instead of manually mapping all the form elements, use this to automatically transform the form into a proper payload and send it as a POST request to the specified URL.
Related
To my ASP.NET MVC web application, I'm trying to apply a full calendar with create and view events.
For that, I found this amazing tutorial on here and I followed same.
All parts are working as same as the tutorial.
But when I try to create an event, for the controller it won't pass the selected date Start and EndDate to the controller. Other data comes to the controller but the dates are not passing.
I tried in the script checking by console log and I found that the date values are written on the console when I hit submit. But only the dates are not passing.
Can you help me to find the error here?
This is the View
<div id="myModalSave" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Save Event</h4>
</div>
<div class="modal-body">
<form class="col-md-12 form-horizontal">
<input type="hidden" id="hdEventID" value="0" />
<div class="form-group">
<label>Subject</label>
<input type="text" id="txtSubject" class="form-control" />
</div>
<div class="form-group">
<label>Start</label>
<div class="input-group date" id="dtp1">
<input type="text" id="txtStart" class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" id="chkIsFullDay" checked="checked" /> Is Full Day event</label>
</div>
</div>
<div class="form-group" id="divEndDate" style="display:none">
<label>End</label>
<div class="input-group date" id="dtp2">
<input type="text" id="txtEnd" class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="txtDescription" rows="3" class="form-control"></textarea>
</div>
<div class="form-group">
<label>Theme Color</label>
<select id="ddThemeColor" class="form-control">
<option value="">Default</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="black">Black</option>
<option value="green">Green</option>
</select>
</div>
<button type="button" id="btnSave" class="btn btn-success">Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</form>
</div>
</div>
</div>
</div>
This is the Script
$('#btnSave').click(function () {
//Validation/
if ($('#txtSubject').val().trim() == "") {
alert('Subject required');
return;
}
if ($('#txtStart').val().trim() == "") {
alert('Start date required');
return;
}
if ($('#chkIsFullDay').is(':checked') == false && $('#txtEnd').val().trim() == "") {
alert('End date required');
return;
}
else {
var startDate = moment($('#txtStart').val(), "DD/MM/YYYY HH:mm A").toDate();
var endDate = moment($('#txtEnd').val(), "DD/MM/YYYY HH:mm A").toDate();
if (startDate > endDate) {
alert('Invalid end date');
return;
}
}
var data = {
EventID: $('#hdEventID').val(),
Subject: $('#txtSubject').val().trim(),
Start: $('#txtStart').val().trim(),
End: $('#chkIsFullDay').is(':checked') ? null : $('#txtEnd').val().trim(),
Description: $('#txtDescription').val(),
ThemeColor: $('#ddThemeColor').val(),
IsFullDay: $('#chkIsFullDay').is(':checked')
}
SaveEvent(data);
// call function for submit data to the server
})
function SaveEvent(data) {
$.ajax({
type: "POST",
url: '/home/SaveEvent',
data: data,
success: function (data) {
if (data.status) {
//Refresh the calender
FetchEventAndRenderCalendar();
$('#myModalSave').modal('hide');
}
},
error: function () {
alert('Failed');
}
})
}
This is the model
public partial class EventsMain
{
[Key]
public int Id { get; set; }
public string Subject { get; set; }
public string Description { get; set; }
public DateTime Start_Date { get; set; }
public DateTime? End_Date { get; set; }
public bool IsFullDay { get; set; }
public string Theme_Color { get; set; }
public int Create_By { get; set; }
public DateTime Created_Date { get; set; } = DateTime.Now;
}
Controller
public JsonResult SaveEvent(EventsMain e)
{
var status = false;
if (e.Id > 0)
{
//Update the event
var v = db.EventsMain.Where(a => a.Id == e.Id).FirstOrDefault();
if (v != null)
{
v.Subject = e.Subject;
v.Start = e.Start;
v.End = e.End;
v.Description = e.Description;
v.IsFullDay = e.IsFullDay;
v.ThemeColor = e.ThemeColor;
}
}
else
{
e.Create_By = int.Parse(((System.Security.Claims.ClaimsIdentity)User.Identity).FindFirst("UserId").Value);
db.EventsMain.Add(e);
}
db.SaveChanges();
status = true;
return new JsonResult { Data = new { status = status } };
}
I'm very new to programming and I have a problem with getting roles from a form through FetchAPI.
This is my rest controller
#PostMapping("admin/users/save")
public ResponseEntity<User> save(#RequestBody User user){
return ResponseEntity.ok(userRepo.save(user));
}
This is JS
<script> const url1='http://localhost:8080/api/admin/users/save'
const form=document.getElementById("newUserForm")
form.addEventListener('submit', async (e)=>{
e.preventDefault()
const formData=new FormData(form);
const formDataSerialized = Object.fromEntries(formData)
const jsonobject = {...formDataSerialized}
console.log(formDataSerialized,"formDataSerialized")
try{
const responce= await fetch(url1, {
method: 'POST',
body: JSON.stringify(jsonobject),
headers: {
'Content-Type': 'application/json',
}
});
const json = await responce.json()
console.log(json)
}catch(e){
console.error(e)
alert('there was an error')
}
})
This is how an object should look in DB
[{"id":1,"name":"john","age":23,"country":"USA","email":"john#mail.com","password":"$2a$12$HbAWvSQU/QQ7HuTxdy862.ifBPSev3NdM5Sa1iGUL1YZdopnwbVZm","roles":[{"id":1,"name":"ADMIN","authority":"ADMIN"},
And this is what i get
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<sof.fetchapi314.entity.Role>` from String value (token `JsonToken.VALUE_STRING`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<sof.fetchapi314.entity.Role>` from String value (token `JsonToken.VALUE_STRING`)<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 77] (through reference chain: sof.fetchapi314.entity.User["roles"])]
After sending an object that looks like this
age: "22" country: "22" email: "22" name: "22" password: "22" roles: "2"
Form:
<h1>MyForm</h1>
<form id="newUserForm">
<div class="form-group">
<label class="col-form-label" for="name" style="display: block;
text-align: center;"><b>Name</b></label>
<input class="form-control" type="text" name="name" id="name"
placeholder="Enter name"/>
</div>
<div class="form-group">
<label class="col-form-label" for="age" style="display: block;
text-align: center;"><b>Age</b> </label>
<input class="form-control" type="number" name="age" step="1" min="1" max="150" id="age"
placeholder="Enter age"/>
</div>
<div class="form-group">
<label class="col-form-label" for="country" style="display: block;
text-align: center;"><b>Country</b> </label>
<input class="form-control" type="text" name="country" id="country"
placeholder="Enter country"/>
</div>
<div class="form-group">
<label class="col-form-label" for="email" style="display: block;
text-align: center;"><b>Email</b> </label>
<input class="form-control" type="text" name="email" id="email"
placeholder="Enter email"/>
</div>
<div class="form-group">
<label class="col-form-label" for="password" style="display: block;
text-align: center;"><b>Password</b> </label>
<input class="form-control" type="password" name="password" id="password"
placeholder="Enter password"/>
</div>
<div class="form-group">
<label class="col-form-label" for="roles" style="display: block;
text-align: center;"><b>Roles</b> </label>
<select class="form-select form-control" aria-label="multiple select example" id="roles"
name="roles" size="2" multiple="multiple" >
<option value="1">ADMIN</option>
<option value="2">USER</option>
</select>
</div>
<button type="submit" class="btn btn-success btn-lg">Submit</button>
</form>
I believe the problem is that I send roles as a number, not as an array and I have absolutelu no idea how to fix it. I an BAD in java and pre beginner level in JS.
Edit:
Role Class:
#Entity
#Data
#Table(name="roles")
public class Role implements GrantedAuthority, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Role() {
}
private String name;
#Override
public String toString() {
return this.name;
}
#Override
public String getAuthority() {
return name;
}
}
and User class
#Data
#Entity
#Table(name = "users")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
#Column(name = "country")
private String country;
#Column(name = "email")
String email;
#Column(name = "password")
String password;
#ManyToMany(cascade=CascadeType.MERGE)
#JoinTable(
name="user_role",
joinColumns={#JoinColumn(name="USER_ID", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="id")})
private List<Role> roles;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
}
#Override
public String getUsername() {
return name;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
I'm beginner in web designing with ASP.NET Core. I wrote a view component that has a form with some inputs related to a view model. One of these inputs is a file input (of the IFormFile datatype).
I want to submit this view model to an action of a controller (POST action), check the validity of model, return another view component if the model state is valid, and remain on this view component with this view model if model state is not valid.
This is my View Model: PricingViewModel.cs
public class PricingViewModel
{
[Display(Name = "Select a file")]
public IFormFile formFile { get; set; }
[Display(Name = "ColumnCode")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colCode { get; set; }
[Display(Name = "ColumnName")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colName { get; set; }
}
My View Component (controller): PricingComponent.cs
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
My View Component (view): PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
My Home Controller: HomeController.cs
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); // 1
}
else if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); //2
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return ViewComponent("PricingComponent", new { pricing = pricing }); //3
}
}
Plan A
The above approach is my primary plan.
Problem
If I use options of submit input tag (asp-action, asp-controller) like above, the view model sends correctly, but I don't know how to handle the validity of the model and remain on this view component. In the above code, when the ShowPricing action runs, if the model state is valid, the code works correctly, but when model is invalid (1,2,3), the PricingView doesn't show the validation summery, and just loads with current view model.
Plan B
I used AJAX to send the viewModel to the action and instead of showing the validation summary, I send an alert to the user with AJAX. I changed PricingView as following:
My View Component (view): PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="fromFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '#Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
$.ajax({
type: "POST",
url: _url,
data: {
formFile: input,
colCode: $("#colCode").val(),
colName: $("#colName").val(),
},
success: function (result)
{
var IsValid = $('body').find('[name="IsValidPricing"]').val();
if (IsValid)
{
$("#ShowExcelTable").html(result);
}
else {
alert("Invalid Data");
}
},
});
});
});
</script>
<div class="form-group mt-4">
<input type="submit" value="Show" id="ShowPricingBtn" />
</div>
</form>
Problem
In this code:
If the model state is not valid, the alert sends correctly, but
If the model state is valid, the formFile input doesn't send correctly to action and it's null in view model.
I don't know whether I should go with the original or the alternate approach these problems. Do you know where I'm going wrong?
Not sure how do you call view components,here are the working demos:
For PlanA
1.Create ViewComponents/PricingComponent.cs and ViewComponents/ShowPricingExcelComponent.cs.
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
public class ShowPricingExcelComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("ShowPricingExcel", pricing));
}
}
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml.
#model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.
<h1>Excel....</h1>
Project Structure:
4.Views/Home/Index.cshtml:
#await Component.InvokeAsync("PricingComponent")
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return View("Index", pricing);
}
if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return View("Index", pricing);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return View("Index", pricing); //3
}
}
}
Result:
For PlanB
1.Create ViewComponents/PricingComponent.cs and ViewComponents/ShowPricingExcelComponent.cs.
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml.
Firstly,it should be type="button" otherwise it will call twice to the backend.Secondly,what you did in ajax is not correct,more detailed explation you could refer to this answer.At last,you could not judge the modelstate by get the value of IsValidPricing value in your sucess function.Because the value you get is always be the data you first render the page,you cannot get the changed ViewBag value when ajax post back.
#model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
#*it should be type="button"*#
<input type="button" value="Show" id="ShowPricingBtn" />
</div>
</form>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '#Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
var fdata = new FormData();
fdata.append("formFile", input);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
type: "POST",
url: _url,
data: fdata,
contentType: false,
processData: false,
success: function (result)
{
console.log(result);
if (result==false)
{
alert("Invalid Data");
}
else {
$("#ShowExcelTable").html(result);
}
},
});
});
});
</script>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.
<h1>Excel....</h1>
4.Views/Home/Index.cshtml:
#await Component.InvokeAsync("PricingComponent")
<div id="ShowExcelTable"></div>
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp)|| !int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
return Json(false);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return Json(false);
}
}
}
Result:
I'm not able to reproduce your error. Your code, as presented, works as expected. A validation message is displayed.
To make it a working example, I've added a GET method first.
[HttpGet]
public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { pricing = new PricingViewModel() });
Open the URL Home/ShowPricing
Fill out the form.
Send the form. And the validation message is displayed.
I am junior (or less than junior) in IT. I have a problem with passing data from view to controller... What I want to achieve:
When user click:
<button type="submit" class="btn btn-primary">Dodaj kategorię</button>
I want pass whole object "Category" to controller (also with List Subcategory). Second button with id="addSubCategory" will add a field to enter the next subcategory, but I want to send everything after clicking the type = submit button. How can I pass all subcategories name to list and send one post method?
That is my View:
#model AplikacjaFryzjer_v2.Models.Category
#{
ViewData["Title"] = "Dodaj nową kategorię";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Dodaj nową kategorię</h1>
<form method="post">
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Dodaj kategorię</button>
</div>
<div class="col-md-6 offset-6">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Subcategories"></label>
<input asp-for="Subcategories" />
<span asp-validation-for="Subcategories" class="text-danger"></span>
</div>
<button class="btn btn-primary" id="addSubCategory" value="table">Dodaj podkategorię</button>
</div>
</div>
</form>
My Action CreateCategory in controller:
[HttpPost]
public IActionResult CreateCategory(Category category)
{
_categoriesRepository.AddCategory(category);
return RedirectToAction("ManageCategories");
}
And my object (model):
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public List<Subcategory> Subcategories {get;set;}
}
For submitting complex models, you need to ensure that the name attribute of these controls bound to the Subcategory class fields are displayed in the form of a collection index.
And trigger the addSubCategory click event in js to add Subcategory control and data.
Since you added model validation, I suggest you use ViewBag.Subcategories to save the Subcategories data that has been added to the current page to prevent data loss after clicking the validation.
And you only need to add an asp-validation-summary in your form. Since these fields belong to a model and are in a form, their error information will be counted in the asp-validation-summary div.
Here is a complete example:
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<Subcategory> Subcategories { get; set; }
}
public class Subcategory
{
[Required]
[DisplayName("Subcategory.Name")]
public string Name { get; set; }
}
Controller:
public IActionResult CreateCategory()
{
ViewBag.Subcategories = new List<Subcategory>() { };
return View();
}
[HttpPost]
public IActionResult CreateCategory(Category category)
{
if (!ModelState.IsValid)
{
// store Subcategories data which has been added
ViewBag.Subcategories = category.Subcategories == null ? new List<Subcategory>() { } : category.Subcategories;
return View("CreateCategory");
}
_categoriesRepository.AddCategory(category);
return RedirectToAction("ManageCategories");
}
View:
#model AplikacjaFryzjer_v2.Models.Category
#{
ViewData["Title"] = "Dodaj nową kategorię";
Layout = "~/Views/Shared/_Layout.cshtml";
var SubcategoriesData = (IList<AplikacjaFryzjer_v2.Models.Subcategory>)ViewBag.Subcategories;
}
<h1>Dodaj nową kategorię</h1>
<form method="post">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Dodaj kategorię</button>
</div>
<div class="col-md-6 offset-6">
#for (int i = 0; i < SubcategoriesData.Count(); i++)
{
<div class="form-group">
<label>Name#(i)</label>
<input asp-for="Subcategories[i].Name" value="#SubcategoriesData[i].Name" />
<span asp-validation-for="Subcategories[i].Name" class="text-danger"></span>
</div>
}
<button class="btn btn-primary" onclick="RemoveSubcategory(this)" id="removeSubcategory">remove</button>
<button class="btn btn-primary" id="addSubCategory" value="table">Dodaj podkategorię</button>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</form>
#section Scripts
{
<script>
var i = #SubcategoriesData.Count()-1;
$(document).ready(function () {
if (#SubcategoriesData.Count() <= 0) {
$("#removeSubcategory").hide();
}
$("#addSubCategory").click(function (e) {
e.preventDefault();
i++;
var name = '<label>Name' + i + '</label><input name = "Subcategories[' + i + '].Name" type="text"/>';
$("#removeSubcategory").before('<div class="form-group">' + name + '</div>');
$("#removeSubcategory").show();
});
});
function RemoveSubcategory(btn) {
event.preventDefault();
$(btn).prev("div").remove();
i--;
if (i == #SubcategoriesData.Count() -1) {
$("#removeSubcategory").hide();
}
}
</script>
}
Here is test result:
I would like to select a product category in one drop-down list and display subcategories for it.
My models:
ProductCategory
public class ProductCategory
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage ="Pole 'Nazwa kategorii' jest wymagane")]
[Display(Name ="Nazwa kategorii")]
public string Name { get; set; }
[Display(Name = "Dodaj zdjęcie")]
public string ImagePath { get; set; }
public virtual ICollection<ProductSubCategory> ProductSubCategory { get; set; }
}
ProductSubCategory
public class ProductSubCategory
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Pole 'Nazwa podkategorii' jest wymagane")]
[Display(Name = "Nazwa kategorii")]
public string Name { get; set; }
[Display(Name = "Wybierz zdjęcie")]
public string ImagePath { get; set; }
public int ProductCategoryId { get; set; }
[ForeignKey("ProductCategoryId")]
[Display(Name = "Kategoria")]
public ProductCategory ProductCategory { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Create Product Page
public IActionResult OnGet()
{
ViewData["ProductCategoryId"] = new SelectList(_context.ProductCategory, "Id", "Name");
ViewData["ProductSubCategoryId"] = new SelectList(_context.ProductSubCategory, "Id", "Name");
return Page();
}
public JsonResult OnGetSubCategories(int category)
{
var subcategories = _context.ProductSubCategory.Where(c => c.ProductCategoryId == category).ToList();
return new JsonResult(new SelectList(subcategories, "Id", "Name"));
}
CreateProduct html
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.Name" class="control-label"></label>
<input asp-for="Product.Name" class="form-control" />
<span asp-validation-for="Product.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.Description" class="control-label"></label>
<input asp-for="Product.Description" class="form-control" />
<span asp-validation-for="Product.Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ImagePath" class="control-label"></label>
<input type="file" asp-for="Product.ImagePath" class="form-control" name="image" onchange="readURL(this);"/>
<span asp-validation-for="Product.ImagePath" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.DateOfNotification" class="control-label"></label>
<input asp-for="Product.DateOfNotification" class="form-control" />
<span asp-validation-for="Product.DateOfNotification" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ProductCategoryId" class="control-label"></label>
<select id="category" asp-for="Product.ProductCategoryId" class ="form-control" asp-items="ViewBag.ProductCategoryId"></select>
</div>
<div class="form-group">
<label asp-for="Product.ProductSubCategoryId" class="control-label"></label>
<select id="subCategory" asp-for="Product.ProductSubCategoryId" class ="form-control" asp-items="ViewBag.ProductSubCategoryId"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
script
<script>
$("#category").change(function () {
var category = $("#category").val();
$.ajax({
url: "CreteProduct?handler=SubCategories",
method: "GET",
data: { category: category },
success: function (data) {
$("#subCategory option").remove();
$.each(data, function (index, itemData) {
$("#subCategory").append("<option value='" + itemData.Id + "'>" + itemData.Name + "</option>");
});
}
})
});
</script>
Result: The subcategory drop-down list is undefined. After selecting the category the number of items is good but displays 'undefined'.
You need to use itemData.value to replace itemData.Id,itemData.text to replace itemData.Name.Here is a demo worked:
cshtml.cs:
public class CreateProductModel : PageModel
{
[BindProperty]
public Product Product { get; set; }
public static List<ProductCategory> productCategories = new List<ProductCategory> { new ProductCategory { Id = 1, Name = "pc1" }, new ProductCategory { Id = 2, Name = "pc2" } };
public static List<ProductSubCategory> productSubCategories = new List<ProductSubCategory> { new ProductSubCategory { Id = 11, Name = "psc11", ProductCategoryId = 1 }, new ProductSubCategory { Id = 12, Name = "psc12", ProductCategoryId = 1 }, new ProductSubCategory { Id = 21, Name = "psc21", ProductCategoryId = 2 }, new ProductSubCategory { Id = 22, Name = "psc22", ProductCategoryId = 2 } };
public IActionResult OnGet()
{
ViewData["ProductCategoryId"] = new SelectList(productCategories, "Id", "Name");
ViewData["ProductSubCategoryId"] = new SelectList(productSubCategories, "Id", "Name");
return Page();
}
public JsonResult OnGetSubCategories(int category)
{
var subcategories = productSubCategories.Where(p => p.ProductCategoryId == category).ToList();
return new JsonResult(new SelectList(subcategories, "Id", "Name"));
}
}
cshtml:
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.Name" class="control-label"></label>
<input asp-for="Product.Name" class="form-control" />
<span asp-validation-for="Product.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.Description" class="control-label"></label>
<input asp-for="Product.Description" class="form-control" />
<span asp-validation-for="Product.Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ImagePath" class="control-label"></label>
<input type="file" asp-for="Product.ImagePath" class="form-control" name="image" onchange="readURL(this);" />
<span asp-validation-for="Product.ImagePath" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.DateOfNotification" class="control-label"></label>
<input asp-for="Product.DateOfNotification" class="form-control" />
<span asp-validation-for="Product.DateOfNotification" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ProductCategoryId" class="control-label"></label>
<select id="category" asp-for="Product.ProductCategoryId" class="form-control" asp-items="ViewBag.ProductCategoryId"></select>
</div>
<div class="form-group">
<label asp-for="Product.ProductSubCategoryId" class="control-label"></label>
<select id="subCategory" asp-for="Product.ProductSubCategoryId" class="form-control" asp-items="ViewBag.ProductSubCategoryId"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
#section scripts{
<script type="text/javascript">
$("#category").change(function () {
var category = $("#category").val();
$.ajax({
url: "CreateProduct?handler=SubCategories",
method: "GET",
data: {category: category },
success: function (data) {
$("#subCategory option").remove();
$.each(data, function (index, itemData) {
$("#subCategory").append("<option value='" + itemData.value + "'>" + itemData.text + "</option>");
});
}
})
});
</script>
}
result: