I would like to call specific action method with a parameter and retrieve data from the controller in Umbraco v9.
public class SearchResultController : RenderController
{
private readonly UmbracoHelper UmbracoHelper;
private readonly IPublishedValueFallback PublishedValueFallback;
private ISearchRepository SearchRepository;
public SearchResultController(ILogger<ContentPageController> logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor,
IPublishedValueFallback publishedValueFallback,
UmbracoHelper umbracoHelper, ISearchRepository searchRepo
)
: base(logger, compositeViewEngine, umbracoContextAccessor)
{
UmbracoHelper = umbracoHelper;
PublishedValueFallback = publishedValueFallback;
SearchRepository = searchRepo;
}
[HttpGet]
public override IActionResult Index()
{
var model = new SearchResult(CurrentPage, PublishedValueFallback);
return View("~/Views/SearchResult.cshtml", model);
}
[HttpPost]
public IActionResult SearchResult(string searchString)
{
var results = SearchRepository.SearchString(searchString);
var model = new SearchResult(CurrentPage, PublishedValueFallback);
return View("~/Views/SearchResult.cshtml", model);
}
}
View:
function Search() {
debugger;
var searchString = document.getElementById("searchLabel").value;
var url = "www.test.com/search?searchString=" + searchString;
location.href = url;
}
I have also tried with #Html.Actionlink method, but I really can't make it work to properly redirect and pass the parameter. If i type the link manually and correctly in browser, the value also gets passed into the controller (debugger shows everything is ok).
Thanks for all the Help!
With Umbraco 9 being on dotnet 5+, we now use ViewComponents for this - https://our.umbraco.com/Documentation/Reference/Templating/Mvc/ViewComponents
so for example, you might have a ViewComponent to render your SearchResult list something like this:
using Microsoft.AspNetCore.Mvc;
using MyProject.Core.Models.View;
using MyProject.Core.Services;
namespace MyProject.Core.ViewComponents
{
public class SearchResults: ViewComponent
{
private readonly ISearchRepository searchRepo;
private readonly UmbracoHelper umbraco;
private readonly IPublishedValueFallback publishedValueFallback;
public SearchResults(
UmbracoHelper umbraco,
IPublishedValueFallback publishedValueFallback,
ISearchRepository searchRepo)
{
this.searchRepo = searchRepo;
this.umbraco = umbraco;
this.publishedValueFallback = publishedValueFallback;
}
public IViewComponentResult Invoke(string searchString)
{
var results = SearchRepository.SearchString(searchString);
var model = new SearchResult(umbraco.AssignedContentITem, publishedValueFallback);
return View(model);
}
}
}
Then, create the Default ViewComponent cshtml class in the /Views/Shared/Components/SearchResults directory (named Default.cshtml) something like this:
#inherits UmbracoViewPage<SearchResult>
<!-- Your View code goes here -->
You can render ViewComponents in your templates a couple of different ways, but my favourite is to use the tag-helper format - to do so, register your assembly containing them in your _ViewImports.cshtml file:
#addTagHelper *, MyProject.Core
And then you can you can place them in your templates like so:
<vc:search-results search-string="hello"></vc:search-results>
Microsoft Docs Reference: View components in ASP.NET Core
Related
I have a _LoginPartial View and want to send data to it by ViewBag, but the Controller that I'am sending data from, doesn't have a View.
public PartialViewResult Index()
{
ViewBag.sth = // some data
return PartialView("~/Views/Shared/_LoginPartial.cshtml");
}
This code didn't work for me.
It seems you're expecting this Index action to be called when you do: #Html.Partial('_LoginPartial'). That will never happen. Partial just runs the partial view through Razor with the current view's context and spits out the generated HTML.
If you need additional information for your partial, you can specify a custom ViewDataDictionary:
#Html.Partial("_LoginPartial", new ViewDataDictionary { Foo = "Bar" });
Which you can then access inside the partial via:
ViewData["Foo"]
You can also use child actions, which is generally preferable if working with a partial view that doesn't need the context of the main view. _LoginPartial seems like a good candidate, although I'm not sure how exactly you're using it. Ironically, though, the _LoginPartial view that comes with a default MVC project with individual auth uses child actions.
Basically, the code you have would already work, you would just need to change how you reference it by using Html.Action instead of Html.Partial:
#Html.Action("Index")
Notice that you're calling the action here and now the view.
You can always pass data directly to the partial view.
public PartialViewResult Index()
{
var data = // some data
return PartialView("~/Views/Shared/_LoginPartial.cshtml", data);
}
Pass multiple pieces of data
public class MyModel
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
public PartialViewResult Index()
{
var data = new MyModel(){ Prop1 = 5, Prop2 = 10 };
return PartialView("~/Views/Shared/_LoginPartial.cshtml", data);
}
I passed viewBag data to my partial view like below, and I converted that viewBag data object to JSON in my partial view by using #Html.Raw(Json.Encode(ViewBag.Part));
my code sample is given below.
public async Task<ActionResult> GetJobCreationPartialView(int id)
{
try
{
var client = new ApiClient<ServiceRepairInspectionViewModel>("ServiceRepairInspection/GetById");
var resultdata = await client.Find(id);
var client2 = new ApiClient<PartViewModel>("Part/GetActive");
var partData = await client2.FindAll();
var list = partData as List<PartViewModel> ?? partData.ToList();
ViewBag.Part = list.Select(x => new SelectListItem() {Text = x.PartName, Value = x.Id.ToString()});
return PartialView("_CreateJobCardView" ,resultdata);
}
catch (Exception)
{
throw;
}
}
Here i have passed both model and viewBag .
First off, the code in your question does not run. When you do #Html.Partial("_SomeView") the Index() method you have there does not run. All #Html.Partial("_SomeView") does is render _SomeView.cshtml in your current view using the current view's ViewContext.
In order to get this to work you need a bit of functionality that's common to all the controllers in your project. You have two options: extension method for ControllerBase or a BaseController that all the controllers in your project inherit from.
Extension method:
Helper:
public static class ControllerExtensions
{
public static string GetCommonStuff(this ControllerBase ctrl)
{
// do stuff you need here
}
}
View:
#ViewContext.Controller.GetCommonStuff()
BaseController
Controller:
public class BaseController : Controller
{
public string GetCommonStuff()
{
// do stuff you need here
}
}
Other controllers:
public class SomeController : BaseController
...
...
View:
#((ViewContext.Controller as BaseController).GetCommonStuff())
I'm using a third party reporting engine (stimulsoft) that calls an action on a controller via POST. Inside of the form, many fields are sent for the mechanics of the third party. Inside of the action I need some parameters all my parameters are inside of the URL.
I want to be able to use the model binder inside of my action.
At the moment I'm getting each fields one by one using this methods
var queryString = HttpUtility.ParseQueryString(Request.UrlReferrer.Query);
var preparedBy = queryString["preparedBy"];
var preparedAt = (queryString["preparedAt"] != null) ? Convert.ToDateTime(queryString["preparedAt"]) : DateTime.Today;
I would prefer to use a model and binding using the UrlReferrer. I've created a UrlReferrerValueProvider to bind from the action. I've tried that, but I'm getting a NullReferenceException on binder.BindModel line
public class UrlReferrerValueProvider : NameValueCollectionValueProvider
{
public UrlReferrerValueProvider(ControllerContext controllerContext)
: base(HttpUtility.ParseQueryString(controllerContext.HttpContext.Request.UrlReferrer.Query), CultureInfo.InvariantCulture)
{
}
}
public ActionResultat GetReportSnapshot()
{
var bindingContext = new ModelBindingContext()
{
ValueProvider = new UrlReferrerValueProvider(ControllerContext),
ModelName = "MyReportModel",
FallbackToEmptyPrefix = true
};
var binder = new DefaultModelBinder();
var myReportModel = binder.BindModel(ControllerContext, bindingContext);
[...]
return new EmptyResult();
}
public class MyReportModel
{
public string PreparedBy {get;set;}
public DateTime PreparedAt {get;set;}
}
Edited based on comments.
public class MyReportModel
{
public string PreparedBy {get;set;}
public DateTime PreparedAt {get;set;}
}
public class UrlReferrerValueProvider : NameValueCollectionValueProvider
{
public UrlReferrerValueProvider(ControllerContext controllerContext)
: base(HttpUtility.ParseQueryString(controllerContext.HttpContext.Request.UrlReferrer.Query), CultureInfo.InvariantCulture)
{
}
}
public ActionResult GetReportSnapshot(MyReportModel model)
{
this.UpdateModel(model, new UrlReferrerValueProvider(ControllerContext));
return new EmptyResult();
}
I have the follwing method on my controller:
[HttpPost]
public ActionResult UnplannedCourses(int studentId)
{
var model = CreateUnplannedCourseModel(studentId);
return View("UnplannedCourses", model);
}
and in my view I try:
<div class="unplannedcourses">
#Html.Action("UnplannedCourses", "Student", new { studentId = Model.StudentId })
</div>
But that gives an error: A public action method 'UnplannedCourses' was not found on controller 'Digidos.MVCUI.Controllers.StudentController'.
If I leave the [HttpPost] out, then it works, but I use the action later again from javascript so I would like to have only POST available.
Any ides?
I think my best bet is a new attribute based on the MVC sources:
public class ChildishAttribute : ActionMethodSelectorAttribute
{
private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Post);
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
var isPost = _innerAttribute.IsValidForRequest(controllerContext, methodInfo);
var isChildAction = controllerContext.IsChildAction;
var isAjax = controllerContext.RequestContext.HttpContext.Request.IsAjaxRequest();
return isChildAction || (isAjax && isPost);
}
}
[Childish]
public ActionResult UnplannedCourses(int studentId)
{
var model = CreateUnplannedCourseModel(studentId);
return View("UnplannedCourses", model);
}
Html.Action is a html helper method and invokes your controller action with Http GET not POST.
Html.Action is a html helper method and invokes your controller action which accepts GET requests.
Edit:
If your intention is to protect that page from viewing through your browser, implement ChildActionOnly attribute as follows:
[ChildActionOnly]
public ActionResult UnplannedCourses(int studentId)
{
var model = CreateUnplannedCourseModel(studentId);
return View("UnplannedCourses", model);
}
Edit:
If you would like to invoke your action through Http POST via JavaScript, have a look the at following post:
Working With JQuery Ajax API on ASP.NET MVC 3.0
I am working on a project in asp.net mvc3(c#).
I need a solution for convert a view(not a partial view) to string from different controllers.
Code Explantion:
1) Calling "details" action of proposalmoduleController from proposalsController.
2) proposalmoduleController action "details" returns a view and convert this view(return result) as a string in proposalsController.
Code
public class proposalmoduleController : ControllerBase
{
[HttpGet]
public ActionResult details(int id, int widgetuniqueid)
{
//id - widgetid of div container
List<ModuleViewModel> listmoduleviewmodel = new List<ModuleViewModel>();
List<ModuleFieldViewModel> listmodulefieldviewmodel = new List<ModuleFieldViewModel>();
var objProposalModuleService = new ProposalModuleService();
var objModuleViewModel = new ModuleViewModel();
string WidgetTitle = "";
Int64 ModuleTemplateID = 0;
//objModuleViewModel.ProposalID = proposalid;
objModuleViewModel.ProposalModuleWidgetID = id;
listmoduleviewmodel=objProposalModuleService.Select(1, objModuleViewModel,out listmodulefieldviewmodel, out WidgetTitle, out ModuleTemplateID);
return View(listmoduleviewmodel);
}
}
public class proposalsController : ControllerBase
{
public string SaveHtml(int ProposalID)
{
var objProposalSortOrderViewModelList = new List<ProposalSortOrderViewModel>();
proposalmoduleController objModuleController = new proposalmoduleController(); // Initilize the object of proposalmoduleController for accessing details method
objProposalSortOrderViewModelList = GetProposalSortorders(ProposalID);
string result;
foreach (var item in objProposalSortOrderViewModelList)
{
ViewResult viewResult = (ViewResult)objModuleController.details(Convert.ToInt32(item.KeyID), Convert.ToInt32(item.SortOrder)); // Fetch the result returned from proposalmodulecontroller,details action
result=viewResult.ToString(); // Need to get result fetch from the proposalmodulecontroller,details action as a string
}
}
}
enter code here
Please suggest any solution.
A ViewResult is not a View. ViewResult is used by the MVC engine to determine the view that must be rendered.
I think it's better if you change your perspective:
if you want to include a partial view in a view just work on the presentation code using #Html.Partial
if you want to get the details data in your proposalsController don't call the action of the proposalmoduleController but call a service method that gives you the data
I am trying to get my feet wet with asp.net mvc3 and unit testing.
I have created a model which uses the repository pattern. Here's the interface:
public interface IExtensionRepository
{
IList<Extensions> ListAll();
}
Here's the repository:
public class ExtensionRepository : IExtensionRepository
{
private ExtensionsLSDataContext _dataContext;
public ExtensionRepository()
{
_dataContext = new ExtensionsLSDataContext();
}
public IList<Extensions> ListAll()
{
var extensions = from ext in _dataContext.Extensions
select ext;
return extensions.ToList();
}
}
Here's the controller:
public class ExtensionController : Controller
{
private IExtensionRepository _repository;
public ExtensionController()
: this(new ExtensionRepository())
{
}
public ExtensionController(IExtensionRepository repository)
{
_repository = repository;
}
}
The pages seem to function as designed. Things go astray with my unit test, however. It resides in another project in the same solution. I am using Moq and NUnit. Here's my test:
[Test]
public void Test_Extension_Index_Views()
{
Mock<Extensions> extension = new Mock<Extensions>();
List<Extensions> extList = new List<Extensions>();
extension.Object.Extension = "5307";
extension.Object.Extension_ID = 1;
extension.Object.ExtensionGroup_ID = 1;
extList.Add(extension.Object);
Mock<IExtensionRepository> repos = new Mock<IExtensionRepository>();
repos.Setup(er => er.ListAll()).Returns(extList);
var controller = new ExtensionController(repos);
var result = controller.Index() as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
I am getting the following errors for the line that begins "var controller...":
The best overloaded method match for
'MvcApplication1.Controllers.ExtensionController.ExtensionController(MvcApplication1.Models.IExtensionRepository)'
has some invalid arguments
And:
Argument 1: cannot convert from
'Moq.Mock'
to
'MvcApplication1.Models.IExtensionRepository'
I know I've missed the boat somewhere, but I haven't a clue as to where... any ideas?
Change this:
var controller = new ExtensionController(repos);
to this:
var controller = new ExtensionController(repos.Object);
PS.: I know it sucks, but that's the way Moq was designed.