Google-esque Pagination with Razor - asp.net-mvc

I currently have pagination functionality that displays the total page count, but I was wondering how I can have page links to display similar to Google search results? Essentially only showing 10 total links at a time, and if the current page is greater than 7, the first link displayed will be currentPageIndex - 5.
This is the current Razor/html that I have that displays the number of links equal to the total page count:
#for (int i = 0; i < Model.PageCount; i++)
{
if (Model.CurrentPageIndex == i)
{
<li id="page#(i)" class="disabled">#(i + 1)</li>
}
else
{
<li id="page#(i)">#(i + 1)</li>
}
}

This seems like more of a logic problem than a razor problem.
#{
int pagesDisplayed = 10;
int firstPage = Model.CurrentPageIndex - pagesDisplayed / 2;
if(firstPage < 0){
firstPage = 0;
}
}
#for (int i=firstPage; i <= (firstPage + pagesDisplayed); i++){
if (Model.CurrentPageIndex == i)
{
<li id="page#(i)" class="disabled">#(i + 1)</li>
}
else
{
<li id="page#(i)">#(i + 1)</li>
}
}

Related

Rails - How to pass data between Rails controller and JavaScript Stimulus controller to dynamically change view

I have a Rails app where I have a search results page, which pulls results based on a user's input from a form.
To simplify the Rails controller method:
def search_results
# [...] ActiveRecord queries to get results
#movies.shuffle!
end
Then in the view, I only want to display the first three results. At the moment, that's something like:
<%#movies[0..2].each do |movie|%>
<div class="row d-flex justify-content-center" data-controller="search-result-card">
<%# Visual of the card %>
</div>
<%end%>
What I want to do is have a refresh button on this page replaces the visible cards with the next three #movies, but without actually reloading the page.
I've drawn up a refresh button and I've got a data-controller on it... but I'm just not sure how I can use this to change the code.
<div class="refresh-button" data-controller="refresh">
<button data-action="click->refresh#next">Refresh</button>
</div>
I'm not sure whether I need to create a partial of the card... or if there's a way to simply change the imbedded Ruby so that <%#movies[0..2].each do |movie|%> becomes <%#movies[3..5].each do |movie|%> and have the view update accordingly.
I don't think that rendering and then hiding all of the results would be feasible, because there could be tens of thousands of them potentially (although I could be wrong and this might be fine to do).
Any suggestions most appreciated!
This sounds like plain old pagination.
If there could be thousands of movies you'd be generation markup/html for thousands of movies. With pagination you'd get get 3 records at a time. Could even use a turbo-frame to replace the next 3.
But if you really want to do what your thinking, you'd have hide all cards except for the first 3, then use stimulus to get the next or last three cards.
A test example
Controller
class TestController < ApplicationController
def index
#movies = Array.new(100) { |e| e = e + 1 }
end
Template using slim
.bg-white
div[data-controller="movies"]
- cnt = 0
button.btn[data-action="click->movies#last_set"] Last
button.btn[data-action="click->movies#next_set"] Next
- #movies.each do |m|
- if cnt < 3
div[data-movies-target="movie" class='block'] = "This is movie #{m}"
- cnt += 1
- else
div[data-movies-target="movie" class='hidden'] = "This is movie #{m}"
Stimulus Controller
import { Controller } from "#hotwired/stimulus"
// Connects to data-controller="movies"
export default class extends Controller {
static targets = ['movie']
connect() {
this.size = this.movieTargets.length
this.idx = 0
// this.idx points to the first current cards (3 for example)
}
next_set() {
let curr = this.idx
this.idx = curr + 3 // set new idx
if (this.idx > this.size) {this.idx = this.size - 3}
for (var i = curr; i <= curr + 2; i++) {
this.movieTargets[i].classList.add("hidden")
}
for (var i = this.idx; i <= this.idx + 2; i++) {
this.movieTargets[i].classList.remove("hidden")
}
// console.log(`idx = ${this.idx}`)
}
last_set() {
let curr = this.idx
this.idx = curr - 3 // set new idx
if (this.idx < 0 ) {this.idx = 0}
for (var i = curr; i <= curr + 2; i++) {
this.movieTargets[i].classList.add("hidden")
}
for (var i = this.idx; i <= this.idx + 2; i++) {
this.movieTargets[i].classList.remove("hidden")
}
// console.log(`idx = ${this.idx}`)
}
}
Just a rough example.
EDIT Not very good at javascript and cleaned up the stimulus controller, added variable number of elements to display and work with pages instead of elements.
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = ['perPage','movie','status','nextBtn','lastBtn']
connect() {
this.size = this.movieTargets.length
this.page = 0
this.perPage = Number(this.perPageTarget.innerHTML)
this.pages = Math.floor(this.size/this.perPage)
if ((this.size % this.perPage) > 0) {this.pages += 1}
this.setStatus()
// this.page points to the current set of cards (3 for example)
}
next_set() {
if (this.page == (this.pages - 1)) {return}
this.hide()
this.page += 1
this.show()
this.setStatus()
}
last_set() {
if (this.page == 0) {return}
this.hide()
this.page -= 1
this.show()
this.setStatus()
}
hide(){
let curr = this.page * this.perPage
let last = curr + this.perPage - 1
while(curr <= last){
if (curr >= 0 && curr < this.size) {
this.movieTargets[curr].classList.add("hidden")
}
curr++
}
}
show(){
let curr = this.page * this.perPage
let last = curr + this.perPage - 1
while(curr <= last){
if (curr >= 0 && curr < this.size) {
this.movieTargets[curr].classList.remove("hidden")
}
curr++
}
}
setStatus(){
this.statusTarget.innerHTML = `Page ${this.page + 1} of ${this.pages}`
if (this.page == 0) {
this.lastBtnTarget.classList.add('hidden')
}else{
this.lastBtnTarget.classList.remove('hidden')
}
if (this.page == this.pages) {
this.nextBtnTarget.classList.add('hidden')
}else{
this.nextBtnTarget.classList.remove('hidden')
}
}
}

Using a Twitter Bootstrap media query to set a Razor view variable

In my Razor view I have the following code to layout the page. However based on the media query I want to change the value of numberOfColumns. So that when MD I use 2 and when SM I use 1 - ie. Change how many columns I output based on the media query. Is this possible?
If not is there another way to do this?
bool inRow = false;
int numberOfColumns = 3; //<---- Change based on media query
int columnNumber = 0;
foreach (OzCpCruiseListItem cruiseItem in Model.CruisesBrief)
{
columnNumber++;
if (columnNumber == 1)
{
inRow = true;
}
if (inRow && columnNumber == 1)
{
#Html.Raw("<!-- START Row --><br />")
#Html.Raw("<div class=\"row\">")
}
<div class="col-lg-4 col-md-6 col-sm-12"> //<--- 3 col for large, 2 for medium, 1 small
</div>
if (columnNumber == numberOfColumns)
{
inRow = false;
#Html.Raw("</div>")
#Html.Raw("<!-- END Row --><br />")
columnNumber = 0;
}
}
//Close row if needed
if (inRow)
{
#Html.Raw("<!-- END AT END Row --><br />")
#Html.Raw("</div")
}
Thanks to #DavidG who tried to understand what I was on about. Below is my solution to what I was trying to ask for. Stated another way: "I never wanted the columns in the row to wrap to the next line when in a particular media query."
The only way I can come up to do this is to output some HTML per media query and vary the classes (for the columns) based on the number of columns I am expecting via:
result += $"<div class=\"col-{aMediaValue.ToLower()}-{columnSize}\">";
If there is a better way I would like to hear it as I don't like the fact I am outputting 4 lots of HTML for the same data - 1 for each media query.
#functions {
HtmlString CardViewOutput(string aMediaValue)
{
string result = "";
int columnCount;
switch (aMediaValue.Trim().ToUpper())
{
case "LG":
columnCount = 3;
break;
case "MD":
columnCount = 2;
break;
case "SM":
case "XS":
columnCount = 1;
break;
default:
columnCount = 1;
break;
}
int columnSize = 12 / columnCount;
bool inRow = false;
int columnNumber = 0;
foreach (OzCpCruiseListItem cruiseItem in Model.CruisesBrief)
{
columnNumber++;
if (columnNumber == 1)
{
inRow = true;
}
if (inRow && columnNumber == 1)
{
result += "<!-- START Row --><br />";
result += "<div class=\"row\">";
}
result += $"<div class=\"col-{aMediaValue.ToLower()}-{columnSize}\">";
result += "</div>";
if (columnNumber == columnCount)
{
inRow = false;
result += "</div>";
result += "<!-- END Row --><br />";
columnNumber = 0;
}
}
//Close row if needed
if (inRow)
{
result += "<!-- END AT END Row --><br />";
result += "</div>";
}
return new HtmlString(result);
}
}
#*-- Card View -----------------------------------------------------------------------------------------------------------*#
#if (Model.CruisesBrief.Count == 0)
{
#Html.Raw("<div class=\"row\"><div class=\"col-lg-12 col-md-12 col-sm-12 col-xs-12\">No cruises to display.</div></div>")
}
else
{
#Html.Raw("<div class=\"visible-lg\">")
#Html.Raw("LARGE")
#Html.Raw(CardViewOutput("LG"))
#Html.Raw("</div>")
#Html.Raw("<div class=\"visible-md\">")
#Html.Raw("MEDIUM")
#Html.Raw(CardViewOutput("MD"))
#Html.Raw("</div>")
#Html.Raw("<div class=\"visible-sm\">")
#Html.Raw("SMALL")
#Html.Raw(CardViewOutput("SM"))
#Html.Raw("</div>")
#Html.Raw("<div class=\"visible-xs\">")
#Html.Raw("EXTRA SMALL")
#Html.Raw(CardViewOutput("XS"))
#Html.Raw("</div>")
}
#*-- /Card View ----------------------------------------------------------------------------------------------------------*#

disable checkbox based on value on jqx widgets grid cellsrenderer

disable checkbox based on value on jqx widgets grid cellsrenderer
here is my code:
var cellsrenderer = function (row, columnfield, value, defaulthtml, columnproperties) {
var rowscount = $("#jqxgrid").jqxGrid("getdatainformation").rowscount;
for (j = 0; j < rowscount; j++) {
for (j = 0; j < rowscount; j++) {
var data = $('#jqxgrid').jqxGrid('getrowdata', j);
if (data.flag1 == '2') {
if (columnfield == 'generatepc' && value == true)
//here need to disable the checkbox
}
}
}
}
i'm unable to fix it, can you anyone provide solution or idea will be good to me

mixing c# and html in quick succession

I'm trying to build a grid server side, I wrote this in a cshtml file:
#{
var items = (List<FancyItem>)ViewBag.items;
var col = 0;
var colMax = 3;
foreach (var it in items) {
if (col == 0) {<tr>}
if (col == 0) {</tr>}
col++;
if (col == colMax) {col = 0;}
}
}
So in theory, the column creation code would go in between the two if's, however, I never got that far. The two if's are supposed to create rows when the columns reset but it seems everything after < tr> gets interpreted as plain text. I don't know what to do, what kind of syntax candy would fix this?
I see your issue - Razor expects you to close any opening tags or it thinks you are still writing HTML
Try this:
Edit: Added #s to get the content to write to the HTML output stream
foreach (var it in items)
{
if (col == 0)
{
#Html.Raw("<tr>");
}
if (col == 0) {
#Html.Raw("</tr>");
}
col++;
if (col == colMax)
{
col = 0;
}
}

How to get the top 7 items in listbox

how can I remove the top 7 items in a listbox?
int end = Count + 7;
for (int i = Count; i < end; i++)
{
page.nuus.Items.Add(page.local_story_list[i]);
}
this happens when the bottom of a scroller is reached, then the latest 7 items gets added to the list, however I want to remove the previous 7 items before this occurs?
You can remove the first item 7 times.
for (int i = 0; i < 7; i++)
{
if (page.nuus.Items.Count > 0)
{
page.nuus.Items.RemoveAt(0);
}
}

Resources