Using Core 3.1 and Razor Pages
I trying to undertake the simple task of passing a search string into a ViewComponent and invoke the results.
I have encountered two issue I cannot find help with:
How to pass the input search string to the view component?
How to invoke the view component when the search button is clicked?
_Layout Page
<input id="txt" type="text" />
<button type="submit">Search</button>
#await Component.InvokeAsync("Search", new { search = "" })
//Should equal input string
I am new to core so any nudges in the right direction would be appreciated.
View component is populated on server side and then return to your client for rendering, so you can't directly pass client side input value into view component . In your scenario , when clicking search button , you can use Ajax to call server side method to load the view component and pass the input value :
Index.cshtml
<input id="txt" type="text" />
<button onclick="loadcomponents()">Search</button>
<div id="viewcomponent"></div>
#section Scripts{
<script>
function loadcomponents() {
$.ajax({
url: '/?handler=Filter',
data: {
id: $("#txt").val()
}
})
.done(function (result) {
$("#viewcomponent").html(result);
});
}
</script>
}
Index.cshtml.cs
public IActionResult OnGetFilter(string id)
{
return ViewComponent("Users", new { id = id });
}
UsersViewComponent.cs
public class UsersViewComponent : ViewComponent
{
private IUserService _userService;
public UsersViewComponent(IUserService userService)
{
_userService = userService;
}
public async Task<IViewComponentResult> InvokeAsync(string id)
{
var users = await _userService.GetUsersAsync();
return View(users);
}
}
Edit: Oh, you edited the razor tag in after I posted my answer. Well, my answer is only valid for ASP.NET Core MVC.
I assume that your controller looks something like this:
[HttpGet]
public IActionResult Index()
{
var model = new IndexVM();
return View(model);
}
[HttpPost]
public IActionResult Index(IndexVM model)
{
// you can do something with the parameters from the models here, or some other stuff
return View(model);
}
Your ViewModel can look like this:
public class IndexVM
{
public string SearchTerm {get;set;}
}
Your View where you use your ViewComponent:
#model IndexVM
// <form tag ...
<input asp-for="SearchTerm" />
<button type="submit">Search</button>
#await Component.InvokeAsync(nameof(Search), Model)
ViewComponent:
public class Search : ViewComponent
{
public IViewComponentResult Invoke(IndexVM indexVM)
{
// Do something with indexVM.SearchTerm
}
}
View of ViewComponent:
#model IndexVM
// ...
Related
So basically I want to make search functionality in ASP.NET MVC. I am currently trying to retrieve data from a form as a string and print it to ensure that it gets the correct information. My view and controller are:
View:
<div style="text-align: center;">
<form method="post" asp-controller="MainPage" asp-action="Search" id="form1">
<label for="licensePlate">Enter license plate:</label>
<input type="text" placeholder="License Plate" name="licensePlate" id="licensePlate" />
<button type="submit">Search</button>
</form>
</div>
Controller:
public class MainPageController : Controller
{
private readonly ILogger<MainPageController> _logger;
private readonly UnblockMeContext _dbContext;
public MainPageController(ILogger<MainPageController> logger,UnblockMeContext appData)
{
_logger = logger;
_dbContext = appData;
}
public IActionResult Index()
{
var users = _dbContext.Users.ToList();
return View(users);
}
public IActionResult Search(string result)
{
return Content(result);
}
}
When I press on search button to submit the form a blank page appears but I want to print out the string that was submitted.
How should I do this ?
You need to change the controller search action method parameter name to match with the FORM NAME.
public IActionResult Search(string licensePlate = "")
{
return Content(licensePlate);
}
I want create a Master/Detail page that shows properties of the model as well items of properties of the same model that are collections. The page itself should only have one save button, that stores the values in a database. I also want to allow the the user to make changes to the collection properties, that are shown on the page without saving them into the database. The following code shows the setup for the picture collection, but I also want to do this for a "Child-table/grid" i.e. collection of "pocos". Is there a way to do this in MVC?
To my understanding, I would have to keep the instance of the object and pass it between the HTMLActions, as this instance holds all the changes.
Just some pointers in the right direction would be nice or, if the case, pointing out, that MVC should not be used for this...
The model:
public class MasterModel : ModelBase
{
public MasterModel()
{
}
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ListBase<PicModel> pics;
public ListBase<PicModel> Pics
{
get { return pics; }
set { pics = value; }
}
}
Controller:
public ActionResult Edit(int id)
{
if (id <= 0 )
{
return RedirectToAction("Index");
}
m = new MasterModel (id);
return View(m);
}
[HttpPost]
public ActionResult NewPic(int id, HttpPostedFileBase uploadFile)
{
PicModel p = new PicModel();
MemoryStream ms = new MemoryStream();
uploadFile.InputStream.CopyTo(ms);
b.Picture= ms.ToArray();
m.Pics.Add(b); //Here it fails, as the MasterModel m is a different one then when the ActionResult Edit is called
}
View:
#model app1.Models.MasterModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/index.js")" type="text/javascript"></script>
<script>
$("#PicForm").on("submit", function (e) {
e.preventDefault();
var form = $(this);
var formData = new FormData(form.get(0));
$.ajax({
url: form.attr("action"),
method: form.attr("method"),
data: formData,
processData: false,
contentType: false
})
});
</script>
<div class="col-md-4 col-lg-4">
#using (Html.BeginForm("NewPic", "MasterModel ", FormMethod.Post, new { id = "PicForm", enctype = "multipart/form-data" }))
{
#Html.HiddenFor(model => model.Id)
<div class="container-fluid">
#foreach (app1.Models.PicModel b in Model.Pics)
{
var base64 = Convert.ToBase64String(b.Picture);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
<img src="#imgSrc" width="200" height="200" />
}
</div>
<div>
<input type="file" id="uploadFile" name="uploadFile" />
<input type="submit" value="uploadFile" class="submit" />
</div>
}
</div>
Update 06.01.2018:
What works in MVC5 is to use the sessionString. However, I've learned that this won't work in asp.net Core.
Set:
m = (MasterModel )System.Web.HttpContext.Current.Session["sessionString"];
Get:
System.Web.HttpContext.Current.Session["sessionString"] = m;
or, ..., that MVC should not be used for this...
Pure MVC won't cut it, and you're already on your way with the Ajax calls.
But you'll find that that gets more and more complicated.
The best route would be to study up on SPA, with for instance Angular.
What works in MVC5 is to use the Session[].
Yes, but that is server-side state manangment, problems with scale-out etc.
But usable, for ASP.NET Core you could use the MemoryCache, or step up to ReDis. You still have (can configure) a Session Id.
With a SPA you won't need the cache/session so much, just use it for optimization.
Try TempData to store your Data and access in next Request.
I have a page that has 2 text boxes First Name and last Name after user click on sign up button API will run and returns user info and shows another page(view) that had user Phone, email,.. That fill with the info that API returns. I have 1 controller and 2 views.
I get the info from API and return the second view but not sure how fill the text boxes with the info I have. The problem is using the models in view, I have 2 models one for each view. I am getting this error when I call the second view:
The model item passed into the dictionary is of type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]', but this dictionary requires a model item of type Models.CreateLogInRequest'.
This is my controller:
[HttpGet]
public ActionResult SearchUser()
{
return View();
}
[HttpPost]
public async Task<ActionResult> SearchUser(UserSearchRequest userSearchRequest)
{
HttpClient client = new HttpClient();
object userObject = null;
string baseUrl = "http://test/api/users";
if (userSearchRequest.FirstName != null && userSearchRequest.LastName)
{
var response = await client.GetAsync(string.Format("{0}{1}/{2}/{3}", baseUrl, "/users", userSearchRequest.FirstName, userSearchRequest.LastName));
if (response.IsSuccessStatusCode)
{
userObject = new JavaScriptSerializer().DeserializeObject(response.Content.ReadAsStringAsync().Result) as object;
}
}
if (userObject != null)
{
return View("Create", userObject);
}
return View("Create", null);
}
[HttpPost]
public ActionResult Create(CreateLogInRequest createLogInRequest)
{
return View();
}
This is my First View that shows 2 text boxes:
#using (Html.BeginForm("SearchUser", "SignUp", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input id="FirstName" name="FirstName" type="text" placeholder="First NAME" />
<input id="LastName" name="LastName" type="text" placeholder="LastName " />
<input id="btnSubmit" name="btnSubmit" type="submit" value="SIGN UP TODAY" />
}
and this is my model for 1st view:
public class UserSearchRequest
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
This is the second View:
#model Models.CreateLogInRequest
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm("create", "SignUp", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input id="Email" name="Email" type="text" placeholder="Email" value="#Model.Email" />
<input id="Phone" name="Phone" type="text" placeholder="Phone" value="#Model.Phone" />
<input id="btnSubmit" name="btnSubmit" type="submit" value="CREATE ACCOUNT" />
}
and this is Model for this view:
public class CreateLogInRequest
{
public string Email { get; set; }
public string Phone { get; set; }
....
}
See my comments and try this:
[HttpGet]
public ActionResult SearchUser()
{
return View();
}
[HttpPost]
public async Task<ActionResult> SearchUser(UserSearchRequest userSearchRequest)
{
HttpClient client = new HttpClient();
CreateLogInRequest userObject = null;
string baseUrl = "http://test/api/users";
if (userSearchRequest.FirstName != null && userSearchRequest.LastName)
{
var response = await client.GetAsync(string.Format("{0}{1}/{2}/{3}", baseUrl, "/users", userSearchRequest.FirstName, userSearchRequest.LastName));
if (response.IsSuccessStatusCode)
{
userObject = new JavaScriptSerializer().DeserializeObject<CreateLogInRequest>(response.Content.ReadAsStringAsync().Result);
}
}
if (userObject != null)
{
return RedirectToAction("Create", userObject);
}
return View("Create", null);
}
[HttpPost]
public ActionResult Create(CreateLogInRequest createLogInRequest)
{
return View();
}
In the Controller you can create a new instance of Models.CreateLogInRequest model and fill the related properties received from 1st View. If Models.CreateLogInRequest does not contain such properties then it is better to load these values by using TempData or ViewBag in the Controller retrieved from the 1st View and pass them to the 2nd View. For the differences between ViewBag, ViewData, or TempData you might have a look at When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications. Hope this helps...
I have got the two buttons in the same view one is working with the data to show in a label in another view and I have written the function for the button2 (adding another value), when I click on the button2 its not showing the data in view ..... rather it's giving error like this ... http:404 Resource not found error
and this is the view
#model MvcSampleApplication.Models.products
#{
ViewBag.Title = "Valuesadd";
}
<h2>Valuesadd</h2>
#using (Html.BeginForm("SubmitValue","EnterValue",FormMethod.Post))
{
<div>
<fieldset>
<legend>Enter Textbox Value</legend>
<div class ="editor-label">
#Html.LabelFor(m => m.EnteredValue)
</div>
<div class="editor-field">
#Html.TextBoxFor(m=>m.EnteredValue)
</div>
<p>
<input type="submit" value="Submit1" />
</p>
</fieldset>
</div>
}
#using (Html.BeginForm("SubmitValue2","EnterValue",FormMethod.Post))
{
<p>
<input type="submit" value="Submit2" />
</p>
}
and this is the controller for
namespace MvcSampleApplication.Controllers
{
public class EnterValueController : Controller
{
[HttpPost]
public ActionResult SubmitValue(MvcSampleApplication.Models.products model)
{
TempData["logindata"] = model.EnteredValue;
return RedirectToAction("submittedvalues" , "SubmitValue2");
// how can we redirect to another view when the button is clicked in one view
}
public ActionResult submittedvalues()
{
var model = new MvcSampleApplication.Models.category();
string data = TempData["logindata"] != null ? TempData["logindata"].ToString() : "";
model.lablvalue = data;
return View(model);
}
// action for second button click
public ActionResult submittedvalues2()
{
var model = new MvcSampleApplication.Models.category();
string data = TempData["logindata"] != null ? TempData["logindata"].ToString() : "";
model.lablvalue = "HIIII"+data;
return View(model);
}
}
}
would you pls suggest any idea ..
Many thanks...
Your form action and action in the controller are not named the same. Also you don't have a HttpPostfor it
#using (Html.BeginForm("SubmitValue2","EnterValue",FormMethod.Post))
{
}
//add this
[HttpPost]
public ActionResult submittedvalues2()
{
var model = SOMETHING;
return View("submittedvalues", model);
}
or
[HttpPost]
public ActionResult submittedvalues2()
{
//Do your post actions and redirect to action
return RedirectToAction("submittedvalues");
}
SubmitValue2 in the form should be submittedvalues2, and add a HttpPost attribute on it
I have some questions regarding my MVC learning curve
Goal:
I want to get the txtMHM entered text
after the btnStat is pushed and show the entered text via a label or span or ...
The View Model you should use (simplified):
public class YourViewModel
{
public string TextEntered { get ; set ; }
}
The View:
#model YourViewModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.TextEntered)
<br />
<input id="btnStat" type="submit" value="MHM" />
}
#Html.LabelFor(m => m.TextEntered)
The Controller Action method:
[HttpPost]
public ActionResult ChangeLabelText(YourViewModel yourViewModel)
{
return View(yourViewModel);
}
Your Altered Code
index.cshtml
#model MVCTest1.Models.EnteredTextModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.TextEntered)
<br />
<input id="btnStat" type="submit" value="MHM" />
}
#Html.LabelFor(m => m.TextEntered)
The Model
namespace MVCTest1.Models
{
public class EnteredTextModel
{
public string TextEntered { get; set; }
}
}
The HomeController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCTest1.Models ;
namespace MVCTest1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
[HttpPost]
public ActionResult Index(EnteredTextModel theModel)
{
return View(theModel);
}
public ActionResult About()
{
return View();
}
}
}
Sypress,
Best practice would be to use a viewmodel for your action. However, at a very simple level, based on exactly what you have above, you could use the viewbag object to pass back the input value. without further ado, the frig, i mean code :):
Controller actions:
[HttpGet]
public ActionResult ChangeLabelText()
{
return View();
}
[HttpPost]
public ActionResult ChangeLabelText(FormCollection formCollection)
{
ViewBag.LastNameEntered = formCollection["txtName"];
return View();
}
View stuff (assumes that the view is named ChangeLabelText.cshtml of course):
#{
ViewBag.Title = "ChangeLabelText";
}
<h2>ChangeLabelText</h2>
<form action="ChangeLabelText" method="post">
<input id="txtMHM" type="text" name="txtName" value="" />
<input id="btnStat" type="submit" value="Post" />
<br />
#Html.Label("Entered Text");
<span id="spnEnteredText">#ViewBag.LastNameEntered </span>
</form>
and the above is called as such http://localhost:xxxx/Home/ChangeLabelText (where xxxx is the port number of your dev server)
I would add that this would NOT be the way that I would approach this to be honest, but is my direct response to your example. go for sharks' example using the viewmodel.
good luck
[EDIT] - I've updated my answer now that I'm at a machine, so the above should work as intended.