I'm using MVC 5 along with PagedList.MVC 4.5.0.0, I have data coming back and displaying on my table, along with the pager controls showing up. when I click next though, the pager continues to send page = 1 to my function, see that while debugging.
my page has:
<div class="pagedList" data-otf-target="#contractList">
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }), PagedListRenderOptions.MinimalWithItemCountText)
</div>
my method which sends the data back to the action is
public IPagedList<ContractViewModel> GetAllContracts(int page = 1)
{
var lstcontractViewModel = new List<ContractViewModel>();
using (ContractRepository contractRepos = new ContractRepository(new UnitOfWork()))
{
var activeContractList = contractRepos.All.OrderByDescending(x => x.Id);
foreach (var activeContract in activeContractList)
{
Mapper.CreateMap<DomainClasses.ActiveContract, ActiveContractViewModel>().ForMember(dest => dest.ContractorModel, opts => opts.Ignore());
Mapper.AssertConfigurationIsValid();
lstcontractViewModel.Add(Mapper.Map<ActiveContractViewModel>(activeContract));
}
}
return lstcontractViewModel.ToPagedList(page, 40);
}
and my controller's action is
public ActionResult Index()
{
var contracts = activeaccountController.GetAllContracts();
return View(contracts);
}
as I said everything comes up fine for the 1st page, just when the GetAllContracts method is called, the debugger shows page is always = 1. so paging is always returning just the 1st page of results. i have over 2500 records, so other data is there, as the pager also shows that, pager says "Showing items 1 through 40 of 2546."
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }), PagedListRenderOptions.MinimalWithItemCountText)
try to set new{page = somevalue} and it will send in a param.
Public ActionResult Index(int page)
public IPagedList<ContractViewModel> GetAllContracts(int page = 1)
This means that page is default 1 if no other param is applied.
var contracts = activeaccountController.GetAllContracts(page);
For more info read https://github.com/TroyGoode/PagedList
Related
I am working on an ASP.NET MVC app (ASP.NET NOT ASP.NET Core).
When a View is rendered, the user can click on some buttons on the page to collapse or show divs associated with each button. The div changes its class depending on whether it is collapsed or shown. I am using bootstrap attributes for this, and it works fine.
Now I have a "Save" button on the page. When the user clicks on this button, I need to retrieve the ids and classes of the divs, and pass them TO the Controller (in an array/collection/dictionary whatever).
Is there a way/method in ASP.NET to send to the Controller the attributes (ids, classes, etc) of the DOM elements on the client's browser ?
Thanks
If you want to send some attributes of DOM to Controller, I have a way.
HTML:
<div id="demo-1" class="chosendiv other-className" data-code ="abc">Lorem Ipsum</div>
<div id="demo-2" class="chosendiv other-className" data-code ="xyz">Lorem Ipsum</div>
<div id="demo-3" class="other-className" data-code ="mnt">Lorem Ipsum</div>
<button id="btn-save" onclick="Save()">SAVE</button>
Javascript
<script>
function Save(){
var cds = document.getElementsByClassName('chosendiv');
var finder = [];
if(cds != null){
for(i = 0; i< cds.length; i++){
finder.push({
ID: cds[i].getAttribute('id'),
ClassName: cds[i].getAttribute('class'),
Code: cds[i].getAttribute('data-code')
})
}
}
//
// Send finder to Controller. You can use Ajax...
// A simple ajax call:
//
$.ajax({
url: '/Home/YourAction',
type: 'GET', //<---- you can use POST method.
data:{
myDiv: JSON.stringify(finder)
},
success: function(response){
// Your code
}
})
}
</script>
Your Controller
public class HomeController: Controller
{
public HomeController(){}
[HttpGet]
public void YourAction(string myDiv)
{
//A lot of ways for converting string to Object, such as: creating new class for model, ...
// I use Dictionary Class
List<Dictionary<string, string>> temp = new List<Dictionary<string, string>>();
if(!string.IsNullOrEmpty(myDiv))
{
try
{
temp = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(myDiv);
}
catch { // Do something if it catches error. }
}
// Get a element (at index) from temp if temp.Count()>0
// var id = temp.ElementAt(index)["ID"];
// var className = temp.ElementAt(index)["ClassName"];
// var code = temp.ElementAt(index)["Code"];
//
//Your code
//
}
//......
}
It would be great if my answer could solve your problem.
Based on the answer provided by #Gia Khang
I made few changes in order to avoid the issue of the length of the URL exceeding the maximum limit.
Instead of adding the element's classes to an array using JS, I add them to a string :
function Save() {
var cds = document.getElementsByClassName('chosendiv');
// I use as string instead of an array
var finder = "";
if(cds != null){
for(i = 0; i< cds.length; i++){
finder = finder + "id=" + cds[i].getAttribute('id') + "class=" + cds[i].getAttribute('class') + "data-code=" +cds[i].getAttribute('data-code')
}
}
// Send finder to Controller. You can use Ajax...
// A simple ajax call:
var myURL = "/{Controller}/{Action}"
$.ajax({
url: myURL,
type: "POST",
data: { ids:finder },
success: function (response) {
}
})
}
In the Controller Action I add a parameter named "ids" (this must be the same name as the identifier of the data object in the post request)and I extract the id, class, and data value from the ids string by a method in one of my Models classes (sorry I work with VB.NET not with C# and it will take me a lot of time to convert the code to C#. I use the Split method in VB to split the ids string several times: a first one by using "id=" as delimiter, then spiting each element in the resulting array by the second delimiter "class=", etc. I add the resulting elements to a collection)
The Controller Action looks like this:
public class HomeController: Controller
{
public HomeController(){}
[HttpPost]
public void YourAction(string ids)
{
Models.myClass.splitStringMethod(ids)
Return View()
}
}
I was trying out the newly released xamarin shell. Basically, I am trying out how to map the route to a dynamically generated page. I noticed the RegisterRoute has an overload that requires an object of type RouteFactory. I created a demo class for this:
class NavMan : RouteFactory
{
private readonly string title;
public NavMan(string title) : base()
{
this.title = title;
}
public override Element GetOrCreate()
{
return new ContentPage
{
Title = "tit: " + title,
Content = new Label { Text = title }
};
}
}
Now, in the App.cs I create a register a demo route:
Routing.RegisterRoute("batman", new NavMan("I am batman"));
I have tried several variations of setting up the Shell object. I mostly get blunt null pointers and need to guess what to change.
As of now, my App class's constructor has the following code:
var sea = new Shell();
var theItem = new FlyoutItem
{
IsEnabled = true,
Route = "batman"
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
This gives me a blunt null pointer too. All I am trying for now is to display the page of route "batman". Even a flyout or tab isn't mandatory.
Update
While not the aim, I at least got the app opening with the following:
var sea = new Shell();
Routing.RegisterRoute("batman", new NavMan("I am batman"));
var theItem = new ShellContent {
Title = "hello 20",
Route = "batman2",
Content = new ContentPage {
Content = new Button {
Text = "Something shown",
Command = new Command(async () => await Shell.Current.GoToAsync("//batman"))
}
}
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
On clicking the button, it now shows me the following exception and never calls the GetOrCreate function.
System.Exception: <Timeout exceeded getting exception details>
Update 3
Basically, I am looking for a way to bind a route to ShellContent in a way that it simply displays I mention the route property and it displays a page in the route. It doesn't make sense that I need to mention a route AND mention a content template for the page. The route is already mapped to a page.
I have this page (item) in my Sitecore website that is viewed from a Facebook page tab. It's a rather simple page, but I have an issue with Sitecore giving me the wrong language on first load, then subsequent loads are ok.
This item runs through this controller:
//
// GET: /Portfolio/
public override ActionResult Index()
{
var appId = "*****";
var appSecret = "*****";
// Defaults to en
var requestLanguage = "en";
// Get language from FB
if (Request.Form["signed_request"] != null)
{
if (Request.Url.Host.ToLower().Contains("local")) {
appId = "*****";
appSecret = "*****";
}
var fbUser = new Facebook.FacebookClient
{
AppId = appId,
AppSecret = appSecret
};
var parsedSignedRequest = JObject.Parse(fbUser.ParseSignedRequest(Request.Form["signed_request"]).ToString());
if (parsedSignedRequest != null)
{
requestLanguage = parsedSignedRequest["user"]["locale"].ToString().StartsWith("fr") ? "fr-CA" : "en";
} // Else: Request can't be parsed, something is wrong
} // Else: Probably not in FB
// ?l=***** can bypass language setting
if (!string.IsNullOrWhiteSpace(Request.QueryString["l"]))
{
requestLanguage = Request.QueryString["l"];
}
Context.Language = Language.Parse(requestLanguage);
// Views will need this
ViewBag.requestLanguage = requestLanguage;
ViewBag.appId = appId;
return base.Index();
}
When I debug this, it works perfectly. I'm setting Sitecore's Context.Language to that of what the Facebook user uses (I have french and english content).
Now onto views, I have this master layout that basically just (other than boring html markup) places the placeholder:
#Html.Sitecore().Placeholder("fb-body")
Finally, my view rendering looks like this:
#using Sitecore.Globalization
#using Sitecore.Data.Items
#model RenderingModel
#{
// I checked this and the context language here is always correctly set, even on first load (controller did that)
// Sitecore.Context.Language = Language.Parse(ViewBag.requestLanguage);
var all = "All";
var back = "Back";
var projetTitle = "the project";
var servicesTitle = "services";
// Since my language is correctly set, this works fine
if (Language.Current.Name.ToLowerInvariant().Contains("fr"))
{
all = "Tous les projets";
back = "Retour";
projetTitle = "le projet";
servicesTitle = "services";
}
var portfolio = Model.Item.Parent.Children.FirstOrDefault(x => x.TemplateName == "Portfolio");
var wrongLanguage = portfolio.Language;
var wrongLanguage2 = Model.Item.Language;
}
Here when I pull my portfolio node, it's in the wrong language. If I look in Model.Item.Language, I also get the wrong language.
What am I missing here, is there something else I need to tell Sitecore so that he understands my language? This also sort of looks like a caching issue... Where do I look to solve this?
Thanks!
I am not sure what base controller you have set up, but it seems you make use of the PageContext.Current.PageView either by returning it yourself or inheriting from the SitecoreController. I bumped into some issues trying to reproduce the problem so I ended up with this:
public class TestController : Controller
{
public ActionResult Index()
{
Sitecore.Context.SetLanguage(Language.Parse("nl-NL"), true);
var view = (PageContext.Current.PageView) as RenderingView;
var renderer = view.Rendering.Renderer as ViewRenderer;
renderer.ViewPath = "/Views/Test/Index.cshtml";
return View(view);
}
}
And in my view I render:
#model Sitecore.Mvc.Presentation.RenderingModel
Context language is: #Sitecore.Context.Language<br/>
Model language is: #Model.Item.Language
First time result:
Context language is: nl-NL
Model language is: en
Second time result:
Context language is: nl-NL
Model language is: nl-NL
The problem is that the language resolver first sets the language to the default (being 'en') and when it gets into your controller action it has already fetched the Item with 'en'. So your language adjustment comes in too late. The second time it is loaded in correct language because the adjusted language was persisted in a cookie.
A proper place to set the language would be the LanguageResolver. You can override/extend the LanguageResolver and replace it in the httpRequestBegin pipeline. That way you will also prevent localized content being cached with the default language in the cachekey.
In my razor view, my model has one property composed only by a get.
#model Contoso.MvcApplication.ViewModels.QuizCompletedViewModel
<p>#Model.Property1</p>
By default, this property starts with the value: 10. And all the time has that value in view. I would like when I press clic on a img tag, this property can be updated (because all the time the value persisted), how can I update the property without refresh the page?
Step 1) Modify the view to make the counter addressable by jquery:
#model Contoso.MvcApplication.ViewModels.QuizCompletedViewModel
<p id="my-counter">#Model.Property1</p>
Now, do you need this incremented value on the server?
IF YOU DO NOT need this incremented value sent to the server:
Step 2) Increment the value on the client using javascript/jquery:
$("#my-image").click(function () {
var theValue = parseInt($("#my-counter").html());
theValue = theValue + 10;
$("#my-counter").html(theValue);
});
IF YOU DO NEED TO INCREMENT ON THE SERVER:
Step 2) Create a controller action to handle the increment
public ActionResult Increment(int currentValue)
{
// save to the database, or do whatever
int newValue = currentValue + 10;
DatabaseAccessLayer.Save(newValue);
Contoso.MvcApplication.ViewModels.QuizCompletedViewModel model = new Contoso.MvcApplication.ViewModels.QuizCompletedViewModel();
model.Property1 = newValue;
// If no exception, return the new value
return PartialView(model);
}
Step 3) Create a partial view which will return ONLY the new value
#model Contoso.MvcApplication.ViewModels.QuizCompletedViewModel
#Model.Property1
Step 4) Modify the jquery to post to this new action, which returns the new count, and displays it
$("#my-image").click(function () {
$.get('/MyController/Increment/' + $("#my-counter").html(), function(data) {
$("#my-counter").html(data);
});
});
Code is untested, but I think pretty close, and hopefully this gives the right idea.
Scenario: I have a standard dropdown list and when the value in that dropdownlist changes I want to update another dropdownlist that exists in a tinyMCE control.
Currently it does what I want when I open the page (i.e. the first time)...
function changeParent() {
}
tinymce.create('tinymce.plugins.MoePlugin', {
createControl: function(n, cm) {
switch (n) {
case 'mylistbox':
var mlb = cm.createListBox('mylistbox', {
title: 'Inserts',
onselect: function(v) {
tinyMCE.execCommand("mceInsertContent",false,v);
}
});
<% foreach (var insert in (ViewData["Inserts"] as List<String>)) { %> // This is .NET
yourobject = '<%= insert %>'; // This is JS AND .NET
mlb.add(yourobject, yourobject); // This is JavaScript
<% } %>
// Return the new listbox instance
return mlb;
}
return null;
}
});
<%= Html.DropDownList(Model.Record[184].ModelEntity.ModelEntityId.ToString(), ViewData["Containers"] as SelectList, new { onchange = "changeParent(); return false;" })%>
I am thinking the way to accomplish this (in the ChangeParentFunction) is to call a controller action to get a new list, then grab the 'mylistbox' object and reassign it, but am unsure how to put it all together.
As far as updating the TinyMCE listbox goes, you can try using a tinymce.ui.NativeListBox instead of the standard tinymce.ui.ListBox. You can do this by setting the last argument to cm.createListBox to tinymce.ui.NativeListBox. This way, you'll have a regular old <select> that you can update as you normally would.
The downside is that it looks like you'll need to manually hook up your own onchange listener since NativeListBox maintains its own list of items internally.
EDIT:
I played around a bit with this last night and here's what I've come up with.
First, here's how to use a native list box and wire up our own onChange handler, the TinyMCE way:
// Create a NativeListBox so we can easily modify the contents of the list.
var mlb = cm.createListBox('mylistbox', {
title: 'Inserts'
}, tinymce.ui.NativeListBox);
// Set our own change handler.
mlb.onPostRender.add(function(t) {
tinymce.dom.Event.add(t.id, 'change', function(e) {
var v = e.target.options[e.target.selectedIndex].value;
tinyMCE.activeEditor.execCommand("mceInsertContent", false, v);
e.target.selectedIndex = 0;
});
});
As far as updating the list box at runtime, your idea of calling a controller action to get the new items is sound; I'm not familiar with ASP.NET, so I can't really help you there.
The ID of the <select> that TinyMCE creates takes the form editorId_controlId, where in your case controlId is "mylistbox". Firebug in Firefox is the easiest way to find the ID of the <select> :)
Here's the test button I added to my page to check if the above code was working:
<script type="text/javascript">
function doFoo() {
// Change "myEditor" below to the ID of your TinyMCE instance.
var insertsElem = document.getElementById("myEditor_mylistbox");
insertsElem.options.length = 1; // Remove all but the first option.
var optElem = document.createElement("option");
optElem.value = "1";
optElem.text = "Foo";
insertsElem.add(optElem, null);
optElem = document.createElement("option");
optElem.value = "2";
optElem.text = "Bar";
insertsElem.add(optElem, null);
}
</script>
<button onclick="doFoo();">FOO</button>
Hope this helps, or at least gets you started.
Step 1 - Provide a JsonResult in your controller
public JsonResult GetInserts(int containerId)
{
//some code to get list of inserts here
List<string> somedata = doSomeStuff();
return Json(somedata);
}
Step 2 - Create javascript function to get Json results
function getInserts() {
var params = {};
params.containerId = $("#184").val();
$.getJSON("GetInserts", params, updateInserts);
};
updateInserts = function(data) {
var insertsElem = document.getElementById("183_mylistbox");
insertsElem.options.length = 1; // Remove all but the first option.
var optElem = document.createElement("option");
for (var item in data) {
optElem = document.createElement("option");
optElem.value = item;
optElem.text = data[item];
try {
insertsElem.add(optElem, null); // standards compliant browsers
}
catch(ex) {
insertsElem.add(optElem, item+1); // IE only (second paramater is the items position in the list)
}
}
};
Step 3 - Create NativeListBox (code above provided by ZoogieZork above)
var mlb = cm.createListBox('mylistbox', {
title: 'Inserts'
}, tinymce.ui.NativeListBox);
// Set our own change handler.
mlb.onPostRender.add(function(t) {
tinymce.dom.Event.add(t.id, 'change', function(e) {
var v = e.target.options[e.target.selectedIndex].value;
tinyMCE.activeEditor.execCommand("mceInsertContent", false, v);
e.target.selectedIndex = 0;
});
});
//populate inserts on listbox create
getInserts();