I have created a modular application based on Zero Framework following this tutorial
Because that tut didn't guide to create an Area in module Web. So I decided to create another area (named Payment) in root web project.
Areas/F1 <--my default area
Areas/Payment <--new area
I created a very simple controller (inherited from project controller base class) like this:
public class BankCodeController : FastOneControllerBase
{
// GET: Payment/BankCode
public ActionResult Index()
{
return View();
}
}
...and my Index.cshtml
#using FastOne.Web.Navigation
#{
ViewBag.CurrentPageName = PageNames.App.Payment.BankCode;
}
<div class="row margin-bottom-5">
<div class="col-xs-12">
<div class="page-head">
<div class="page-title">
<h1>
<span>BankCode</span>
</h1>
</div>
</div>
</div>
</div>
<div class="portlet light">
<div class="portlet-body">
<p>BANK CODE CONTENT COMES HERE!</p>
</div>
</div>
But I faced the error as below when I tried to access that view
The controller for path '/Payment/BankCode' was not found or does not
implement IController.
> Line 40: #RenderSection("Styles", false)
> Line 41:
> Line 42: #Html.Action("TenantCustomCss", "Layout")
> Line 43:
> Line 44: <script type="text/javascript">
Hope anyone could help me on this error some guide to create this Area in module Web project (in the tutorial). Thanksss
Update
I found this solution in ASPNET Zero forum
Since I use _Layout.cshtml of F1 area, this error happens.
I fixed it by adding F1 area name to #Html.Action usages in _Layout.cshtml.
For example
#Html.Action("Header", "Layout") becomes #Html.Action("Header", "Layout", new { area = "F1" })
#Html.Action("Sidebar", "Layout", new { currentPageName = ViewBag.CurrentPageName }) becomes #Html.Action("Sidebar", "Layout", new { currentPageName = ViewBag.CurrentPageName, area = "F1" })
Related
i am lost with NET6.
I created this partial view _MenuNav.cshtml :
#model IEnumerable<CateringMilano.Models.Menu>
#foreach (var item in Model)
{
<div>
<a alt="#item.MenuTitle)">#item.MenuName</a>
<b>#item.MenuTitle</b>
</div>
}
and the cod in my controller is :
// GET: /Menus/_MenuNav/
public ActionResult _MenuNav(string menuPosition)
{
// Get my db
var model = _context.Menu.Include(m => m.ChildMenuSub) as IQueryable<Menu>;
model = model.Where(p => p.MenuPosition == menuPosition);
// Send your collection of Mreations to the View
return PartialView(model);
}
and in the last project with net 4 i use to write the following code in the principal view in order to call my partial view :
#Html.Action("_MenuNav", "Menus", new { menuPosition = "Menu" })
but it looks like it does not work anymore this with NET6
Do you know how to rewrite my code in order to get the same result?
you must be mixing view components with partial views. Partial view were always used like this
<div id="partialName">
<partial name="_PartialName" />
</div>
with model
<partial name="_PartialName" model="Model.MyInfo" />
or for async
<div id="partialName">
#await Html.PartialAsync("_PartialName")
</div>
and the most popular way to update parital view is using ajax
And you can check my answer about viewcomponents here
https://stackoverflow.com/a/69698058/11392290
I'm new to AJAX, and I have a very simple example, but has a problem; first call the data is duplicated at the View and in subsequent calls work correctly. What am I doing wrong?
The ~/Views/Shared/_Layout.cshtml file had all scripts necesary:
<script src="~/Scripts/jquery-3.1.0.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
First time, the initial view:
Second time, first time button click inserts a number instead of replace number:
Third time, second time button click works fine but in the second line number... an so on:
This is my code:
Model
namespace MQWebSt.Models
{
public class AjaxTest
{
public int Number { get; set; }
}
}
Controller:
using System.Web;
using System.Web.Mvc;
using MQWebSt.Models;
namespace MQWebSt.Controllers
{
public class AjaxTestController : Controller
{
// GET: AjaxTest
public ActionResult Vista()
{
AjaxTest at = new AjaxTest { Number = 1 };
return View(at);
}
[HttpPost]
public ActionResult Vista( AjaxTest model)
{
Random rnd = new Random();
model.Number = rnd.Next(1, 100);
return PartialView("AjaxTestPartial", model);
}
}
}
View Vista.chtml:
#model MQWebSt.Models.AjaxTest
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Ajax.BeginForm("Vista", new AjaxOptions { UpdateTargetId = "divEmp", InsertionMode = InsertionMode.Replace }))
{
<button class="btn btn-primary btn-md glyphicon glyphicon-menu-right" name="Contestar" type="submit" value="+1"></button>
<div class="panel panel-footer">
<table id="divEmp">
#Model.Number.ToString()
</table>
</div>
}
AjaxTestPartial.chtml:
#model MQWebSt.Models.AjaxTest
#Model.Number.ToString()
The issue is you're using InsertionMode = InsertionMode.Replace, it's going to replace the data in the UpdateTargetId. You could use InsertionMode.InsertBefore instead and it would build the values out in the parent of #divEmp. However, if you need the values to be placed in #divEmp, you'll need to write a JavaScript handler for the OnSuccess option of your Ajax form, that way you can dictate if you're doing insert vs. pre-pend vs. replace.
The issue is, your HTML markup code in your razor view is not valid. With the code you have, razor will generate the below markup (Check the view source)
<div class="panel panel-footer">
1
<table id="divEmp"></table>
</div>
You can see that 1 is not inside the table. It is outside. So when your ajax form submit happens, it will update the tables content with the new value. that is the reason you are still seeing the initial number.
The solution is to change the table to a div/span
<div id="divEmp"> #Model.Number.ToString() </div>
Or if you absolutely need to have a table for any reason, have the value inside a td and use "divEmp" as the id attribute value of that.
<table >
<tr>
<td id="divEmp">1</td>
</tr>
</table>
I'm building application with Spring MVC 3.2 and Thymeleaf templating engine. I'm a beginner in Thymeleaf.
I have everything working, including Thymeleaf but I was wondering if anyone knows of a simple and clear toturial on how to do simple Ajax request to controller and in result rendering only a part of a template (fragment).
My app has everything configured (Spring 3.2, spring-security, thymeleaf, ...) and works as expected. Now I would like to do Ajax request (pretty simple with jQuery but I don't wan't to use is since Thymeleaf in its tutorial, chapter 11: Rendering Template Fragments (link) mentiones it can be done with fragments.
Currently I have in my Controller
#RequestMapping("/dimensionMenuList")
public String showDimensionMenuList(Model model) {
Collection<ArticleDimensionVO> articleDimensions;
try {
articleDimensions = articleService.getArticleDimension(ArticleTypeVO.ARTICLE_TYPE);
} catch (DataAccessException e) {
// TODO: return ERROR
throw new RuntimeException();
}
model.addAttribute("dimensions", articleDimensions);
return "/admin/index :: dimensionMenuList";
}
the part of the view where I would like to replace <ul></ul> menu items:
<ul th:fragment="dimensionMenuList" class="dropdown-menu">
<li th:unless="${#lists.isEmpty(dimensions)}" th:each="dimension : ${dimensions}">
</li>
</ul>
Any clue is greatly appreciated. Especially if I don't have to include any more frameworks. It's already too much for java web app as it is.
Here is an approach I came across in a blog post:
I didn't want to use those frameworks so in this section I'm using jQuery to send an AJAX request to the server, wait for the response and partially update the view (fragment rendering).
The Form
<form>
<span class="subtitle">Guest list form</span>
<div class="listBlock">
<div class="search-block">
<input type="text" id="searchSurname" name="searchSurname"/>
<br />
<label for="searchSurname" th:text="#{search.label}">Search label:</label>
<button id="searchButton" name="searchButton" onclick="retrieveGuests()" type="button"
th:text="#{search.button}">Search button</button>
</div>
<!-- Results block -->
<div id="resultsBlock">
</div>
</div>
</form>
This form contains an input text with a search string (searchSurname) that will be sent to the server. There's also a region (resultsBlock div) which will be updated with the response received from the server.
When the user clicks the button, the retrieveGuests() function will be invoked.
function retrieveGuests() {
var url = '/th-spring-integration/spring/guests';
if ($('#searchSurname').val() != '') {
url = url + '/' + $('#searchSurname').val();
}
$("#resultsBlock").load(url);
}
The jQuery load function makes a request to the server at the specified url and places the returned HTML into the specified element (resultsBlock div).
If the user enters a search string, it will search for all guests with the specified surname. Otherwise, it will return the complete guest list. These two requests will reach the following controller request mappings:
#RequestMapping(value = "/guests/{surname}", method = RequestMethod.GET)
public String showGuestList(Model model, #PathVariable("surname") String surname) {
model.addAttribute("guests", hotelService.getGuestsList(surname));
return "results :: resultsList";
}
#RequestMapping(value = "/guests", method = RequestMethod.GET)
public String showGuestList(Model model) {
model.addAttribute("guests", hotelService.getGuestsList());
return "results :: resultsList";
}
Since Spring is integrated with Thymeleaf, it will now be able to return fragments of HTML. In the above example, the return string "results :: resultsList" is referring to a fragment named resultsList which is located in the results page. Let's take a look at this results page:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
</head>
<body>
<div th:fragment="resultsList" th:unless="${#lists.isEmpty(guests)}" id="results-block">
<table>
<thead>
<tr>
<th th:text="#{results.guest.id}">Id</th>
<th th:text="#{results.guest.surname}">Surname</th>
<th th:text="#{results.guest.name}">Name</th>
<th th:text="#{results.guest.country}">Country</th>
</tr>
</thead>
<tbody>
<tr th:each="guest : ${guests}">
<td th:text="${guest.id}">id</td>
<td th:text="${guest.surname}">surname</td>
<td th:text="${guest.name}">name</td>
<td th:text="${guest.country}">country</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
The fragment, which is a table with registered guests, will be inserted in the results block.
Rendering only Thymeleaf fragments also works well with ModelAndView.
Your controller
#RequestMapping(value = "/feeds", method = RequestMethod.GET)
public ModelAndView getFeeds() {
LOGGER.debug("Feeds method called..");
return new ModelAndView("feeds :: resultsList");
}
Your view
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head></head>
<body>
<div th:fragment="resultsList" id="results-block">
<div>A test fragment</div>
<div>A test fragment</div>
</div>
</body>
</html>
What's actually rendered
<div id="results-block">
<div>A test fragment</div>
<div>A test fragment
</div>
</div>
As an alternate version to Sohail's great answer, I want to give a version that using javascript can send the whole th:object to the controller, integrating Thymeleaf in your forms, not having to use #PathVariable which becomes messy or not usable at all when you've forms with many fields.
For the form (using an example which returns an object which has an id and a name Strings, and feeds a combobox with a Map that has some of those objects as values) we have:
<form method="post" th:action="#{/yourMapping}" th:object="${object}" id="yourFormId">
<select th:field="*{mapOfObjects}">
<option
th:each="entry: ${mapOfObjects}"
th:value="${entry.value.id}"
th:text="${entry.value.name}" >
</option>
</select>
<p>Name:
<input type="text" th:field="*{name}" />
</p>
</form>
When this form is submited (using a button with type submit for example) the whole document will be replaced. However we can intercept this submit with javascript and do it the ajax-way. To achieve this, we will add an interceptor to our form using a function. First call the function that adds the interceptor right after the form:
<script>formInterceptor("yourFormId");</script>
And the function looks like this (place it in the head of the document or wherever suits your needs):
<script>
function formInterceptor(formName) {
var $form = $("#" + formName);
$form.on('submit', function(e) {
e.preventDefault();
$.ajax({
url : $form.attr('action'),
type : 'post',
data : $form.serialize(),
success : function(response) {
if ($(response).find('.has-error').length) {
$form.replaceWith(response);
}
else{
$("#ajaxLoadedContent").replaceWith(response);
}
}
});
});
};
</script>
Now whenever the form is submited, this function will trigger, and it will:
Prevent the original form submit
Make an ajax call using the url defined in the form's th:action
Serialize the form data. Your controller will be able to recieve this in an object
Replace the part of your html code with the returned fragment
The replaced part should look like this
<div id="ajaxLoadedContent"></div>
And the controller can recieve the th:object in the form, with it's values filled, like this (Replace Object with your object's type and "object" with a proper name):
#PostMapping(value = /yourMapping)
public String ajaxCtrlExample(#ModelAttribute("object") Object object, Model model) {
return yourFragmentPath;
}
And that's everything. Call the function that adds the interceptor after every form you need in ajax-version.
I'm a newbie in web language.
I started a .NET project on my visual studio 2010, it's in MVC 4 and razor structure.
The problem is that i had an folder in the views folder, with a view in it, but when i try to access the view i got this error:
The view 'searchWeb' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/searchWeb.aspx
~/Views/Home/searchWeb.ascx
~/Views/Shared/searchWeb.aspx
~/Views/Shared/searchWeb.ascx
~/Views/Home/searchWeb.cshtml
~/Views/Home/searchWeb.vbhtml
~/Views/Shared/searchWeb.cshtml
~/Views/Shared/searchWeb.vbhtml
I guess he is not searching in the right folder...but how to change this ?
I looked some topics and apparently i have to make a new viewEngine and inherirted from the old one ?
Is it possible to fix it without making a new view engine ? or can you show me how to make the new view engine ? thank you !
EDIT:
this is my homeController.cs:
namespace Searcher.Controllers
{
public class HomeController : Controller
{
public ActionResult homeWeb()
{
ViewBag.Message = "Searching for sites over the web.";
return View();
}
public ActionResult homeImages()
{
ViewBag.Message = "Searching for images over the web.";
return View();
}
public ActionResult homeVideos()
{
ViewBag.Message = "Searching for videos over the web.";
return View();
}
[HttpPost]
public ActionResult searchWeb()
{
// do something with the search value
return View();
}
}
}
and this is my homeWeb, which is calling the searchWeb:
#{
ViewBag.Title = "Search Web";
}
#section featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
</div>
</section>
}
#using (Html.BeginForm("searchWeb", "Home", FormMethod.Post, new { id = "searchForm" }))
{
<div class="main-block">
<p>
<div>
<input size="200px" type="text" name="searchValue" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</p>
</div>
}
So where is your searchWeb.cshtml file?
Your controller is telling the view engine to look for the search.Web.cshtml file in the Home controller, which normally translates to
~/View/Home/searchWeb.cshtml (one of the default search locations you mentioned in your question).
Its not recomended to change view search location. Trust me:). Especially for newbie in MVC.
And you dont need to change it, this must working from the box.
So make shure that the view location is right. As i can see for this view you need create the action
in Home Controller like
public ActionResult searchWeb()
{
return View();
}
or you trying to call thi view as partial?
I have the following layout template:
<div id="columns" class="#View.LayoutClass">
<div id="mainColWrap">
<div id="mainCol">
#RenderBody()
</div>
</div>
#if (View.ShowLeftCol){
<div id="leftCol">
#RenderSection("LeftCol", required: false)
</div>
}
#if (View.ShowRightCol){
<div id="rightCol">
#RenderSection("RightCol", required: false)
</div>
}
</div>
If View.ShowLeftCol or View.ShowRightCol are set to false, I get the following error:
The following sections have been defined but have not been rendered for the layout page "~/Views/Shared/_Layout.cshtml": "RightCol".
I am trying to have a single layout template instead of trying to dynamically select a template at runtime. Is there a way to ignore this error and continue rendering? Can anyone think of another way to implement that would allow me to dynamically show/hide columns with Razor?
Thanks!
Was given a suggestion on the ASP.net forums that works.
Essentially, if I define #section LeftCol in my view template but don't run any code that calls RenderSection in my layout, I get the error because it doesn't get called when View.ShowLeftCol is false. The suggestion was to add an else block and essentially throw away whatever contents are in the LeftCol section.
#if (View.ShowLeftCol)
{
<div id="leftCol">
#RenderSection("LeftCol", false)
</div>
}
else
{
WriteTo(new StringWriter(), RenderSection("LeftCol", false));
}
Based on the concern raised about memory I decided to test the following out as well. Indeed it also works.
#if (showLeft)
{
<section id="leftcol">
<div class="pad">
#RenderSection("LeftColumn", false)
</div>
</section>
}
else
{
WriteTo(TextWriter.Null, RenderSection("LeftColumn", false));
}
Also, at the top of my page, this is my new logic for showLeft/showRight:
bool showLeft = IsSectionDefined("LeftColumn");
bool showRight = IsSectionDefined("RightColumn");
bool? hideLeft = (bool?)ViewBag.HideLeft;
bool? hideRight = (bool?)ViewBag.HideRight;
if (hideLeft.HasValue && hideLeft.Value == true) { showLeft = false; }
if (hideRight.HasValue && hideRight.Value == true) { showRight = false; }
Someone else said it didn't work for them, but it worked like a charm for me.
#using System.Reflection;
#{
HashSet<string> renderedSections = typeof(WebPageBase).GetField("_renderedSections", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(this) as HashSet<string>;
}
Then add to that hash set whatever section name you want to pretend to have rendered.
#if (View.ShowLeftCol)
{
<div id="leftCol">
#RenderSection("LeftCol", false)
</div>
}
else{ <!-- #RenderSection("LeftCol", false) --> }
easier way!!