Dynamic ViewComponent update with ajax - ajax

...text description of the problem at the bottom
I have this controller:
public class StoreController : Controller
{
ShopContext db;
public StoreController(ShopContext context)
{
db = context;
}
public IActionResult Index()
{
return View();
}
public ViewComponentResult ListOfProducts(int page)
{
return ViewComponent("ListOfProducts", page);
}
part of Index View:
<div class="row">
<div id="ListOfProducts"></div>
</div>
ListOfPoducts ViewComponent:
public class ListOfProducts : ViewComponent
{
private readonly ShopContext db;
public ListOfProducts(ShopContext context)
{
db = context;
}
public async Task<IViewComponentResult> InvokeAsync(int page)
{
int SkipCount = (page - 1) * 9;
var products = await db.Products
.OrderByDescending(a => a.Rating)
.Skip(SkipCount)
.Take(9)
.ToListAsync();
int CountOfProducts = await db.Products.CountAsync();
ViewBag.CountOfPage = CountOfProducts / 9;
if (ViewBag.CountOfPage % 9 == 0)
ViewBag.CountOfPage++;
ViewBag.Page = page;
return View("Default", products);
}
}
with this View:
#foreach (var product in Model)
{
<div class="col-md-4 col-xs-6">
#await Html.PartialAsync("_ProductCellPartial", product)
</div>
}
<partial name="_StorePagination"/>
StorePagination:
<ul class="store-pagination">
#if (ViewBag.CountOfPage <= 9)
{
for (int i = 1; i <= ViewBag.CountOfPage; i++)
{
<li>#i</li>
}
}
else
{
if (ViewBag.Page <= 4)
{
for (int i = 1; i <= 7; i++)
{
<li>#i</li>
}
<li><a>...</a></li>
<li>#ViewBag.CountOfPage</li>
}
else if (ViewBag.Page > 4 && ViewBag.Page <= ViewBag.CountOfPage - 3)
{
<li><a href="#store-anchor" >1</a></li>
<li><a>...</a></li>
for (int i = ViewBag.Page - 2; i <= ViewBag.Page + 2; i++)
{
<li>#i</li>
}
<li><a>...</a></li>
<li>#ViewBag.CountOfPage</li>
}
else if (ViewBag.Page > ViewBag.CountOfPage - 3 && ViewBag.Page <= ViewBag.CountOfPage)
{
<li>1</li>
<li><a>...</a></li>
for (int i = ViewBag.Page - 6; i <= ViewBag.CountOfPage; i++)
{
<li>#i</li>
}
}
}
</ul>
ProductCell - item in the product table(ViewPartial)
and this script:
$(document).ready(function () {
$(".store-pagination").children().children().click(function () {
event.preventDefault();
var id = $(this).attr("href"),
top = $(id).offset().top;
$('body,html').animate({ scrollTop: top }, 200)
$(".store-pagination").children().removeClass("active")
$(this).parent().addClass("active")
var page = $(this).text()
$("#ListOfProducts").load("/Store/ListOfProducts", "page=" + page);
})
});
About problem:
I want to make a dynamic change of a part of the page (going by pagination did not overload the all page). The problem is (as I understand it) that it is impossible to call ViewComponent from itself (I hope it's understandable). According to my code above, I call the Ajax request when I click on the page number of the goods list, but this does not work. And I tried to call this script outside ViewComponent and it worked (confirms what I said above).
How can I solve this problem?
...Sorry for my English, inexperience.

You're dealing with two separate things. Server-side, you're using a view component, but client-side, all you have is the DOM created by the browser from the HTML document it received as a response. When you want to do something like replace part of the page with some new content from the server, you have to make a new request to server to get the content you want and then manually replace the portion of the DOM with that. In other words, from the client-side perspective, there's no such thing as the view component, and it doesn't know or care how the response was constructed.
That means you'll need to utilize AJAX, which is the client-side mechanism for making a request to the server without causing the browser's view to change entirely. That AJAX request will need to be served via some endpoint on your server, so you'll need an action that can respond to the request and return the content you want. That content will be a view that you'll need to render server-side. If you want to utilize a view component in that view, you may of course, but you cannot simply request the view component itself.

Related

Incrementing session attribute on button click in thymeleaf and call url

i am using thymeleaf in spring boot and reading the session attributes like this on the index page
<p th:text="${session.counter}" th:unless="${session == null}">[...]</p>
where the counter is coming from the below function
#RequestMapping({"/"})
String index(HttpSessbelow request ion session) {
session.setAttribute("counter", "0");
return "index";
}
Whenever someone click on the button on page , we should be able to increase the counter and call a url in the application , how can we achieve this
<button onclick="/activate}">...</button>
#RequestMapping({"/"})
String activate(HttpSession session) {
if(session.getAttribute(counter) == 1){
activate();
return "thanksPage" ;
}
}
Here's how I would structure it.
#RequestMapping("/")
public String start(HttpSession session) {
session.setAttribute("counter", 0);
return "redirect:/current";
}
#RequestMapping("/current")
public String current() {
int counter = (Integer) session.getAttribute("counter);
if (counter == 1) {
activate();
return "thanksPage";
} else {
return "index";
}
}
#RequestMapping("/increment")
public string increment(HttpSession session) {
session.setAttribute("counter", ((Integer) session.getAttribute("counter)) + 1);
return "redirect:/current";
}
And the button should go to /increment
<button onclick="/increment">...</button>

Display One record at a time in MVC through List

I have 5 records coming from a simple select stored procedure.
ID Name
1 RecordOne
2 RecordTwo
3 RecordThree
4 RecordFour
5. RecordFive
Requirement is to display one record at a time example:
Record One
Previous Next
Two Action links or buttons with Previous and Next text.
If user clicks Next user will see
RecordTwo
and so on,same for previous case.
My model
namespace MVCLearning.Models
{
public class VMNews
{
public List<Student> StudentDetails { get; set; }
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
}
}
Action
public ActionResult Index()
{
VMNews objnews = new VMNews();
objnews.StudentDetails = db.Database.SqlQuery<Student>("usp_studentdetails").ToList();
return View(objnews);
}
View
<div>
#foreach (var item in Model.SD.Take(1))
{
<h3>#item.Name</h3>
<h3>#item.Age</h3>
}
#Html.ActionLink("Next", "index", new { Model.SD[0].ID})
#Html.ActionLink("Previous", "index", new { Model.SD[0].ID })
The way I have written the view is totally wrong am not getting how and what to write on the action and what to write on the View.
What will be one of the way to achieve this.
Change you method to
public ActionResult Index(int? index)
{
int max = 5; // modify based on the actual number of records
int currentIndex = index.GetValueOrDefault();
if (currentIndex == 0)
{
ViewBag.NextIndex = 1;
}
else if (currentIndex >= max)
{
currentIndex = max;
ViewBag.PreviousIndex = currentIndex - 1;
}
else
{
ViewBag.PreviousIndex = currentIndex - 1;
ViewBag.NextIndex = currentIndex + 1;
}
VMNews objnews = new VMNews();
Student model = db.Database.SqlQuery<Student>("usp_studentdetails")
.Skip(currentIndex).Take(1).FirstOrDefault();
return View(model);
}
Note that the query has been modified to return only one Student since that is all that you require in the view. Also I have asssumed if a user enters a value greater than the number of records it will return the last record (you may in fact want to throw an error?)
The view now needs to be
#model Student
<h3>#Model.Name</h3>
<h3>#Model.Age</h3>
#if (ViewBag.PreviousIndex != null)
{
#Html.ActionLink("Previous", "Index", new { index = ViewBag.PreviousIndex })
}
#if (ViewBag.NextIndex != null)
{
#Html.ActionLink("Next", "Index", new { index = ViewBag.NextIndex })
}

How to generate Ajax Paging Navigation in Wicket with ListView?

I have a HTML markup for Ajax Paging Navigation like:
<div class="btn-group">
<a href="#" class="btn btn-default">
<i class="ico ico-prev"></i>
</a>
<div class="dropdown2 inline">
<a href="#" class="btn btn-default btn-shorter">
<strong>1-8</strong>
</a>
<ul class="dropdown-menu spec_width">
<li><a data-ico="1" href="#">10-30</a></li>
<li>30-40</li>
<li>40-50</li>
<li>50-60</li>
</ul>
</div>
<a href="#" class="btn btn-default">
<i class="ico ico-next"></i>
</a>
when user choose interval of pages (10-30, 30-40 etc) the data is changing (Ajax).
How I can implement it with ListView in wicket? I think that it should be smth like:
ListView yourListView = new ListView("your-list-view", new PropertyModel(this, "pages")){
#Override
protected void populateItem(ListItem item) {
item.add(new Label("label", item.toString()));
}
};
and markup:
<ul><li wicket:id="your-list-view"><span wicket:id="label"></span></li></ul>
I exactly know, how to do it with DropDownChoice, but it isn't suitable in my case, because I have to generate <ul><li></li></ul> instead of <select><option></option></select> tags.
p/s: sample how to do it with DropDownChoice:
public abstract class AjaxPagingPanel extends Panel{
private Criteria criteria;
private List<Page> pages;
private Page currentPage;
private long listSize;
private int pagesCount;
private DropDownChoice pagingDropDownChoice;
private Label pagingSizeLabel;
private AjaxLink previousLink;
private AjaxLink nextLink;
public AjaxPagingPanel(String id, Criteria pagingCriteria) {
super(id);
criteria = pagingCriteria;
listSize = criteria.getResultSize();
pagesCount = (int) Math.ceil((double) listSize / criteria.getPageSize());
long pageSize = pagingCriteria.getPageSize();
currentPage = new Page(pagingCriteria.getPageNum(), (pagingCriteria.getPageNum() - 1) * pageSize + 1, Math.min( pagingCriteria.getPageNum() * pageSize, pagingCriteria.getResultSize()) ); // Model for DropDownChoice
pages = new ArrayList(pagesCount);
for (int i = 0; i < pagesCount; i++) {
pages.add(new Page(i + 1, i * pageSize + 1, Math.min((i + 1) * pageSize, pagingCriteria.getResultSize()) ));
}
// Label updated by ajax to render listSize
pagingSizeLabel = new Label("pageSize", new PropertyModel(this, "listSize"));
add(pagingSizeLabel.setOutputMarkupId(true));
// Ajax DropDownChoice used as Page navigator
pagingDropDownChoice = new DropDownChoice("pagesDropDown", new PropertyModel(this, "currentPage"), new PropertyModel(this, "pages"), new ChoiceRenderer("period", "pageNum"));
pagingDropDownChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
criteria.setPageNum((int)currentPage.getPageNum());
updatePagingList(target);
setLinkVisibility();
target.add(pagingSizeLabel);
target.add(pagingDropDownChoice);
target.add(nextLink);
target.add(previousLink);
}
});
add(pagingDropDownChoice.setOutputMarkupId(true));
add(previousLink = new IndicatingAjaxLink("previousLink"){
#Override
public void onClick(AjaxRequestTarget target) {
if (criteria.getPageNum() > 1) {
criteria.setPageNum(criteria.getPageNum() - 1);
int index = pages.indexOf(currentPage);
currentPage = pages.get(index - 1);
updatePagingList(target);
setLinkVisibility();
target.add(pagingSizeLabel);
target.add(pagingDropDownChoice);
target.add(nextLink);
target.add(previousLink);
}
}
});
previousLink.setOutputMarkupPlaceholderTag(true);
// Next link of Page navigator
add(nextLink = new IndicatingAjaxLink("nextLink"){
#Override
public void onClick(AjaxRequestTarget target) {
if (criteria.getPageNum() < pagesCount) {
criteria.setPageNum(criteria.getPageNum() + 1);
int index = pages.indexOf(currentPage);
currentPage = pages.get(index + 1);
updatePagingList(target);
setLinkVisibility();
target.add(pagingSizeLabel);
target.add(pagingDropDownChoice);
target.add(nextLink);
target.add(previousLink);
}
}
});
nextLink.setOutputMarkupPlaceholderTag(true);
setLinkVisibility();
}
public Page getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Page currentPage) {
this.currentPage = currentPage;
}
public final void setLinkVisibility() {
if (criteria.getPageNum() == 1) {
previousLink.setVisible(false);
} else {
previousLink.setVisible(true);
}
if (criteria.getPageNum() == pagesCount || pagesCount == 0) {
nextLink.setVisible(false);
} else {
nextLink.setVisible(true);
}
}
// Method must be overrided by a class which is using AjaxPagingPanel
public abstract void updatePagingList(AjaxRequestTarget target);
// Method to refresh the AjaxPagingPanel, for example after Ajax search
public void refresh(Criteria pagingCriteria, AjaxRequestTarget target) {
criteria = pagingCriteria;
listSize = criteria.getResultSize();
pagesCount = (int) Math.ceil((double) listSize / criteria.getPageSize());
long pageSize = pagingCriteria.getPageSize();
currentPage = new Page(pagingCriteria.getPageNum(), (pagingCriteria.getPageNum() - 1) * pageSize + 1, Math.min( pagingCriteria.getPageNum() * pageSize, pagingCriteria.getResultSize()) );
pages.clear();
for (int i = 0; i < pagesCount; i++) {
pages.add(new Page(i + 1, i * pageSize + 1, Math.min((i + 1) * pageSize, pagingCriteria.getResultSize()) ));
}
pagingDropDownChoice.modelChanged();
setLinkVisibility();
target.add(pagingSizeLabel);
target.add(pagingDropDownChoice);
target.add(nextLink);
target.add(previousLink);
}
/**
* This class is used as a model class in DropDownChoice component and
* provides list of page as [1-50] [51-100] [101-150] [151-200]...
*/
public class Page implements Serializable{
private long pageNum;
private long firstPage;
private long lastPage;
public Page(long pageNum, long firstPage, long lastPage) {
this.pageNum = pageNum;
this.firstPage = firstPage;
this.lastPage = lastPage;
}
public long getPageNum() {
return pageNum;
}
public void setPageNum(long pageNum) {
this.pageNum = pageNum;
}
public String getPeriod() {
return Long.toString(firstPage) + "-" + Long.toString(lastPage);
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Page)){
return false;
}
return this.pageNum == ((Page)obj).pageNum;
}
#Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + (int) (this.pageNum ^ (this.pageNum >>> 32));
return hash;
}
}
Rather than using a simple ListView and trying to build your own paging support, you likely want a PageableListView and an attached AjaxPagingNavigator.
The PageableListView extends ListView to add support for paging and the AjaxPagingNavigator supplies the navigation UI for the paging.
There are other repeaters that can be made to support paging, but this is the closest to what you're currently doing.
If you're using hibernate to access your database, you might want to look at DataView for good support of paging.

Controller Action get called repeatedly

I am using Razor WebGrid and my controller action is getting repeatedly called when click on sort by header or click on page links. First time it is getting called only once but next time onwards it is getting called repeatedly. Please can anyone help.
[HttpGet]
public ActionResult Index(int page = 1, string sort = "GridName", string sortDir = "ASC")
{
const int pageSize = 5;
string successMessage = string.Empty;
var message = string.Empty;
int pageIndex1 = page - 1;
if (RenderFirstTime == 1)
{
GridCRUDCollection.UpdateGridModelCollection(null, pageSize, pageIndex1, sort, sortDir);
RenderFirstTime = 0;
return View(GridCRUDCollection);
}
else
{
GridCRUDCollection.UpdateGridModelCollection(null, pageSize, pageIndex1, sort, sortDir);
return PartialView("GridDetailsView", GridCRUDCollection);
}
}
Code with an <img src="#" ... > is what did it for me. Once I removed the #'s the controller got called just once. The grid may be generating those and populating them later via Javascript. One way to diagnose it is to use LabJS to prevent immediate execution.

How to get results from database in asp.net mvc 3 using ADO.NET

I wrote model class which returns a List then i pass it to view, but when the page is requested the view call the model many times and then crash the page, here's my code please help me I am new to asp.net MVC
Model
public List<string> DoWork()
{
List<string> results = new List<string>();
using (SqlConnection con = new SqlConnection(#"Connection String Here"))
{
con.Open();
using (SqlCommand cmd = new SqlCommand(#"SELECT Column1 FROM Table1", con))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
results.Add(rdr.GetString(0));
}
}
}
}
return results;
}
Controller
public ActionResult Index()
{
MyData data = new MyData();
return View(data);
}
View
<ul>
#for (int i = 0; i <#Model.DoWork().Count; i++)
{
<li>
#Model.DoWork()[i]
</li>
}
</ul>
The idea is that the DB layer and View layer should be separated.
This being said, you should get your DB entities in your controller action method, not in the view, and use a ViewModel pattern to spit it to the view.In your case, you have a simple view model, IEnumerable, but it can be any entity.
public ActionResult Index()
{
MyData data = new MyData();
IEnumerable<string> thelist = data.DoWork();
return View(thelist);
}
and in yout View, use the IEnumerable as your model, and loop though it , not calling the DB to fetch your data:
#model IEnumerable<string>
......
#foreach(string s in Model)
{
<li>#s<li>
}
You are calling the DoWork method each time you want to use a result...
Store the result in a variable, and use that, instead.
<ul>
#{ List<string> works = Model.DoWork(); }
#for (int i = 0; i < works.Count; i++)
{
<li>
#works[i]
</li>
}
</ul>

Resources