While coding an assignment I've come to need to transfer data from the code-behind to the view so that I can parse that data with Javascript and build some HTML with it, and I've decided to use asp:HiddenField to that end.
However, it seems something goes wrong, since I get the error "The name "HiddenFieldData" does not exist in the current context".
I assume that I'm somehow not linking the view to the model correctly.
Perhaps it's because I'm using a model that is not the appropriate cshtml.cs, but one that is "given" to the view via the controller.
Truth be told, this is my first time with ASP.NET so it's very likely the problem is somewhere here.
The code in question, I've marked the trouble spots with '>>>>':
Controller -
public class saveController : Controller
{
// GET: Save
public ActionResult SaveRoute()
{
saveModel model = new saveModel();
Model given >>>> return View(model);
}
}
Model -
public class saveModel
{
private DataMiner miner;
public saveModel(string ip = "127.0.0.1", int port = 5400, int duration = 10, int interval = 1000)
{
// Initialize miner
miner = new DataMiner(ip, port, duration, interval);
}
public void SaveRoute()
{
// Mine and retrieve data
miner.Mine();
double[][] data = miner.GetData();
int lines = data.GetLength(0);
int cols = data.GetLength(1);
string[] str_data = new string[lines];
for (int i = 0; i < lines; ++i)
{
// Turn double data into strings to write
str_data[i] = data[i].ToString();
}
// Write to file
System.IO.File.WriteAllLines(#"file1.txt", str_data);
// Write values to HiddenField
string values = String.Join(" ", str_data);
Error here >>>> HiddenFieldData.Value = values;
// Call JS function to load at
ScriptManager.RegisterStartupScript(this, GetType(), "showDataMined", "showDataMined();", true);
}
}
View -
#model RESTful_Flight_Simulator.Models.saveModel
#{
ViewBag.Title = "SaveRoute";
}
<html>
<head>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
<script type="text/javascript" language="javascript">
function showDataMined()
{
var body = document.body
var tbl = document.createElement('table');
tbl.style.width = '100px';
tbl.style.border = '1px solid black';
for (var i = 0; i < 3; i++)
{
var tr = tbl.insertRow();
for (var j = 0; j < 2; j++)
{
if (i == 2 && j == 1) { break; }
else
{
var td = tr.insertCell();
td.appendChild(document.createTextNode('Cell'));
td.style.border = '1px solid black';
if (i == 1 && j == 1) {
td.setAttribute('rowSpan', '2');
}
}
}
}
// Build title for table
var title = document.createElement('h3');
title.innerHTML = "Data mined:";
// Finally, append title and table to body
body.appendChild(document.createElement('hr'));
body.appendChild(title);
body.appendChild(tbl);
}
</script>
</head>
<body>
HiddenField >>>> <asp:HiddenField id="HiddenFieldData" runat="server" value="" />
<h2>Saving route...</h2>
</body>
</html>
Thanks ahead for any help!
Related
I have a view with 1700 records. I want to paginate them using ajax to make page load lighter. I am able to do paging and bring set of new records everytime based on the page selected.
The Problem
I am showing only 10 indexes in page-bottom, the page selected as well as 4 to the left and 5 to the right. Now I need CurrentPage value which I send everytime from jQuery/ajax to controller which I get as a ajax data parameter. The problem is in getting back Current page value persistent to view when the next page index I select. I always get the old value and not the last selected page value. I have even used ViewBag instead of tempData but no success.
View Code:
#model IEnumerable<UrCompedDAL.DBModels.SlotMachineModel>
<div class="my-properties">
<table id="tbl_slots" class="table no-margin" data-search="true" data-pagination="false">
<tbody class="orgTbody">
#foreach (var item in Model)
{
<tr>
//Code for Slot list
</tr>
}
</tbody>
</table>
<ul class="paging">
#{
int i = 1;
int pg = Convert.ToInt32(TempData["Current"]);
if (i > 0 || i == ViewBag.PageSize)
{
<li>
<<
</li>
}
if (pg < 6)
{
for (i = 1; i < 11; i++)
{
<li>
#i
</li>
}
}
else
{
for (i = pg - 4; i < pg; i++)
{
<li>
#i
</li>
}
for (i = pg; i < pg + 6; i++)
{
<li>
#i
</li>
}
}
if (i > 1 || i < ViewBag.PageSize)
{
<li>
>>
</li>
}
}
</ul>
</div>
<script>
$(document).ready(function () {
$('.lipaging').click(function () {
$("#loadingDiv").show();
$(this).addClass('active');
var pageThis = $(this).text();
var current = #TempData["Current"];
if (pageThis == '>>') {
pageThis = current +1;
}
if (pageThis == '<<') {
pageThis = current -1;
}
$.ajax({
type: 'get',
url: '#Url.Action("Index", "Game_SlotMachine")',
data: {
CurrentPage: pageThis
}
}).done(function (data) {
var startIndex = data.indexOf("<tbody");
var endIndex = data.indexOf("</tbody>");
var html = data.substring(startIndex, endIndex + 8);
$('#tbl_slots').html('');
$('#tbl_slots').html(html);
setTimeout(function () {
filter();
}, 300);
$("#loadingDiv").hide();
});
});
Controller Code:
public ActionResult Index(int id = 0, int CurrentPage = 1)
{
List<SlotMachineModel> slotmodel = new List<SlotMachineModel>();
slotmodel = UrCompedDAL.DataAccessor.Instance.GameAccessor.GetAllSlotMachines().ToList();
ViewBag.PageSize = slotmodel.Count / 10;
TempData["Current"] = CurrentPage;
slotmodel = slotmodel.Skip((CurrentPage - 1) * 10).Take(10).ToList();
return View(slotmodel);
}
Please help.
Pack your model IEnumerable<UrCompedDAL.DBModels.SlotMachineModel> into other model and set your model as a property of new model. Pass this new model as a model for your view. You will be able to pass as many data from controller as you like.
The issue is that you're re-creating the whole view, but only updating the table from the result
var html = data.substring(startIndex, endIndex + 8);
$('#tbl_slots').html('');
$('#tbl_slots').html(html);
(you should also reconsider how you extract the table from the view and use a partial instead).
None of the ajax/2nd+ rendering of the paging ul is used and is "thrown away" each time.
You can either overwrite your ul in the same way as the table or update the paging element via javascript (probably the former).
Reusing the whole view (rather than a partialview), you'd get something like:
}).done(function (data) {
var html = $("<div/>").html(data);
$('#tbl_slots').html(html.find("#tbl_slots").html());
$('ul.paging').html(html.find("ul.paging").html());
I've struggled with this for quite some time. Today I finally wrote the following code.
The ViewModel contains an int property which later tells the view how many pages the data has been split into.
The controller splits the data by taking a specified amount of rows and, in the event of paging, splits by pageNumber * recordsPerPage
Take a look:
The ViewModel
public class ThreadPostsViewModel
{
public Thread Thread { get; set; }
public List<Post> Posts { get; set; }
public int Pages { get; set; }
}
The Controller
private int PostsPerPage = 10;
public ActionResult Thread(int id, int page = 1)
{
using (OrtundEntities Db = new OrtundEntities())
{
// get the thread and its parent data (parent for breadcrumbs)
var Thread = Db.Threads.Include(t => t.Title).FirstOrDefault(x => x.Id == id);
// create a list for the Posts
List<Post> Posts = new List<Post>();
// select based on paging
if (page == 1)
// no paging has happened, get the first set of records
Posts = Db.Posts.Include(x => x.User).Where(x => x.ThreadId == id).OrderByDescending(x => x.Date).Take(PostsPerPage).ToList();
else
// we're on a new page. Skip however many rows we've already seen
Posts = Db.Posts.Include(x => x.User).Where( x=> x.ThreadId == id).OrderByDescending(x => x.Date).Take(PostsPerPage).Skip(PostsPerPage * page).ToList();
// create and return the view model
ThreadPostsViewModel Model = new ThreadPostsViewModel
{
Thread = Thread,
Posts = Posts,
Pages = Posts.Count / PostsPerPage
};
return View(Model);
}
}
The View
#model Ortund.Models.ThreadPostsViewModel
<div class="paging">
#for (int i = 1; i < Model.Pages; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
</div>
<div class="posts-list">
#foreach (var Post in Model.Posts)
{
<div class="post" id="#Post.Id">
</div>
}
</div>
In this code, assuming 300 posts are selected from the database and 10 posts are specified per page, then there should be 30 pages.
Even that's a hefty amount of links to fit into your page design so how can I minimize these paging links and display, say, 10 paging links only where, when you get to say, page 8, the links will change to show you 3-13, for example?
Even having the paging links display as follows would be preferable:
1 2 3 4 5 ... 90 91 92 93 94
In controller put value of current page:
ViewBag.currentPage = page;
In view you can do something like this (not tested):
<div class="paging">
#if (Model.Pages > 11 && ViewBag.currentPage > 6)
{
for (int i = ViewBag.currentPage - 6; i < ViewBag.currentPage -1; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
for (int i = ViewBag.currentPage + 1; i < ViewBag.currentPage + 6; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
}
else
{
for (int i = 1; i < Model.Pages; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
}
</div>
I'm trying to implement this algorith in a View page using Razor, but, it does not display the expected result and I don't get any compilation errors. Any suggestion please ?
Edit : I apologize I was not very clear, I confess. My problem is that I do not understand why ViewBag.NbrePages is equal to 0. However, the database had been filled.
Action();
[HttpGet]
public ActionResult Rechercher(string rech, string type, int num = 1)
{
int nbLignesDepassees = 10 * (num - 1);
ViewBag.Recherche = Server.HtmlEncode(rech);
ViewBag.Type = Server.HtmlEncode(type);
ViewBag.NumPgeCourrante = num;
if (type == "nomAppMetier")
{
var appsMetiers = _db.AppsMetiers
.Where(x => SqlFunctions.PatIndex("%" + rech + "%", x.nomApplication) > 0)
.OrderBy(x => x.nomApplication)
.Skip(nbLignesDepassees)
.Take(10);
ViewBag.NbrePages = (int)(appsMetiers.Count() / 10) ;
return View("RechercheAppsMetiers",appsMetiers);
}
if (type == "nomPoste")
{
var postes = _db.Postes
.Where(x => SqlFunctions.PatIndex("%" + rech + "%", x.nomPoste) > 0)
.OrderBy(x => x.nomPoste)
.Skip(nbLignesDepassees)
.Take(10);
ViewBag.NbrePages = (int)(postes.Count() / 10);
return View("RecherchePostes", postes);
}
return HttpNotFound();
}
View();
<ul>
#{
for (int i = 0; i < ViewBag.NbrePages; i++)
{
if(i==1 || i==2 || i==3){
<li class="disabled">&maquo;</li>
}else{
<li>«</li>
}
if (i == ViewBag.NumPgeCourrante)
{
<li class="active">#i <span class="sr-only">(current)</span></li>
}
else
{
<li>#i </li>
}
if(i==ViewBag.NbrePages || i==ViewBag.NbrePages-1 || i==ViewBag.NbrePages-2){
<li class="disabled">»</li>
}else{
<li>»</li>
}
}
}
</ul>
Thanks a lot !
Rather than having so much logic in the view, consider the following:
A model
public class PagesModel
{
public int NumberOfPages { get; set; }
public int CurrentPage { get; set; }
}
A helper method in a class
public static class Helpers
{
public static bool GetClassNames(int page, int totalPages, int currentPage)
{
var classNames = new List<string>();
var isWithinFirstOrLastThree = page <= 2 || page >= (totalPages - 2);
if (isWithinFirstOrLastThree)
{
classNames.Add("disabled");
}
if (page == currentPage)
{
classNames.Add("active");
}
return string.Join(" ", classNames.ToArray());
}
}
And then your view could be as simple as
#model PagesModel
#for (int i = 0; i < Model.NumberOfPages; i++)
{
<li class="#Helpers.GetClassNames(i, Model.NumberOfPages, Model.CurrentPage)">
&maquo;
#i
</li>
}
This doesn't exactly match what you are trying to achieve, but I hope that it is helpful nonetheless.
NbrePages will be either 0 or 1 (if you have more that 10 records) due to Take(10) and using integer division:
ViewBag.NbrePages = (int)(appsMetiers.Count() / 10) ;
So most likely you get less that 10 items in appsMetiers.
Suggestion to improve source based on original misspelling of the variable in CSHTM:
Using good names or strongly typed model would help to avoid spelling like NbrePges in for condition in original post:
for (int i = 0; i < ViewBag.NbrePages; i++)
CSHTML files are not compiled till run-time access, so no compile errors. Since ViewBag allows any property to be used you are not getting any intellisense warning either.
Instead of ViewBag consider some strongly typed model or at least put strongly typed object for paging into ViewBag:
class PagingState
{
public int NumberOfPages { get;set;}
public int CurrentPage { get;set;}
}
and in view:
var pageingState = (PagingState)(ViewBag.Paging);
for(int I = 0; i < pageingState.NumberOfPages; i++)...
I created a 2D array Board, holding objects of GameCell.
Default.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link rel="stylesheet" href="includes/css/style.css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>ארבע בשורה</h1>
<div id="Board">
<asp:Label ID="Label1" runat="server"></asp:Label>
<asp:PlaceHolder ID="MyBoard" runat="server" />
</div>
</div>
</form>
</body>
</html>
Default.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
Game.CreateControls();
}
protected void Page_Init(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
Game.Set(MyBoard, Label1);
Game.Reset();
}
}
GameCell.cs:
public class GameCell : ImageButton
{
public Position Position; // Justs holds the properties of Row and Col
public CellType Type;
public GameCell(Position Pos)
{
this.Position = new Position(Pos);
this.Type = CellType.Empty;
}
}
Game.cs:
public class Game
{
public const int R = 6;
public const int C = 9;
public static GameCell[,] Board;
public static PlaceHolder Holder;
public static Label Log;
public static CellType CurrentType;
public static string[] Images = new string[] { "red", "empty", "blue" };
public static void Set(PlaceHolder Holder, Label Log)
{
Game.Reset();
Game.Holder = Holder;
Game.Log = Log;
}
public static void CreateControls()
{
for (int i = 0; i < Game.R; i++)
{
for (int j = 0; j < Game.C; j++)
{
Game.Board[i, j].ImageUrl = Game.ImageUrlByType(Game.Board[i, j].Type);
Game.Board[i, j].ID = i + "_" + j;
Game.Board[i, j].Click += new ImageClickEventHandler(Image_Click);
Game.Holder.Controls.Add(Game.Board[i, j]);
}
}
}
public static void Image_Click(object sender, EventArgs e)
{
int row = 0;
int col = int.Parse(((GameCell)sender).ID[2].ToString());
if (Game.Board[row, col].Type == CellType.Empty)
{
while (row < Game.R - 1 && Game.Board[row, col].Type == CellType.Empty)
{
row++;
}
Game.SetImage(row, col);
Game.CurrentType = (CellType)(-(int)Game.CurrentType);
Log.Text = col + " " + row + " " + Game.CurrentType;
Game.Board[row, col].Enabled = false;
}
}
public static void SetImage(int r, int c)
{
Game.Board[r, c].Type = Game.CurrentType;
Game.Board[r, c].ImageUrl = Game.ImageUrlByType(Game.CurrentType);
}
public static void Reset()
{
Game.Board = new GameCell[R, C];
Game.CurrentType = CellType.Blue;
for (int i = 0; i < Game.R; i++)
{
for (int j = 0; j < Game.C; j++)
{
Game.Board[i, j] = new GameCell(new Position(i, j));
}
}
}
public static string ImageUrlByType(CellType t)
{
return "includes/images/" + Game.Images[(int)t + 1] + ".png";
}
}
The controls are rendered, and are clickable when first launching the project, but after I do click one of them, no controls are added to my Holder (MyBoard).
Why is this happening?
Edit:
I found the solution. passing MyBoard as an argument to CreateControls did it. Maybe the reference wasn't reliable anymore (I saved it in an object) after page_load.
Every time your Page_Load is called, you recreate your controls. They are stored in static members, which means that all users of this web site share a common board. Is this really what you want? Would it not be better to store the Game in the Session? (of course I don't know your requirements, but I always try to avoid static members in web applications).
A possible solution could be to check whether it is not a postback, before you recreate the controls.
if (!IsPostback)
{
}
I would like to toggle the display row for a table in javascript. How do I do this?
<script type="text/javascript" language="javascript">
function vehicleSelected() {
var autoSelect = document.getElementById('vehicleSelect');
var strAuto = autoSelect.options[autoSelect.selectedIndex].value;
var rowAuto = document.getElementById(strAuto);
for (var i = 4; i < tableList.rows.length; i++) {
//I am not sure how to access the id for comparison to rowAuto
if (//table row == strAuto) {
rowAuto.style.display = '';
} else {
rowAuto.style.display = 'none';
}
}
}
</script>
<table id="tableList">
<tr id="optionA"><td>Display Row A</td></tr>
<tr id="optionB"><td>Display Row B</td></tr>
<tr id="optionC"><td>Display Row C</td></tr>
<tr id="optionD"><td>Display Row D</td></tr>
</table>
First, consider jquery. It's a big help for things like this.
Second, if you're not going to use jquery, then what you want to do is something like this:
function vehicleSelected() {
var autoSelect = document.getElementById('vehicleSelect');
var strAuto = autoSelect.options[autoSelect.selectedIndex].value;
var rows = document.getElementById('tableList').getElementsByClassName('TR');
for (var i = 0; i < rows.length; i++) {
rows[i].style.display='none'; // note: better to use a css class here
}
var selectedRow = document.getElementById(strAuto); // assuming that the values are the same as the row Id's.
selectedRow.style.display = ''; // again, better to use a Css style.
}
You could do it easily with jQuery:
function vehicleSelected() {
var autoSelect = //...
var strAuto = //...
$("#tableList tr").hide().filter("#" + strAuto).show();
}
If I correctry understood you, this should help you.
var table = document.getElementById('tableList');
for(var i=0; i<table.rows.length; i++){
if (table.rows[i].attributes["id"].nodeValue == strAuto) {
table.rows[i].style.display = '';
} else {
table.rows[i].style.display = 'none';
}
}