PagedList.Mvc ellipsis button does not work - asp.net-mvc-5

I am using PagedList.Mvc for paging in my MVC 5 app.
Question: The ellipsis button, which is after page#10 in screen shot below, does not do anything when clicked. Is that how its supposed to be, or I can make the ellipsis button work so clicking it would display the next set of pages?
The html helper being used in the View for displaying this pager is as below.
#Html.PagedListPager(Model, page => Url.Action("Index",
new { page, sortOrder = ViewBag.CurrentSort, SearchText = ViewBag.SearchText }))

The solution that worked is to hide the ellipsis button.
SOLUTION
This solution involves hiding the ellipsis button. For this, you would need to make sure that the property of DisplayEllipsesWhenNotShowingAllPageNumbers under PagedListRenderOptions class is set to false, since its true by default. Its this setting that causes the pager to show the ellipsis button.
The code snippet given below will go into your View or PartialView html, and you will need to change some of the custom parameters like sortOrder and action name etc.
Hide Ellipsis button when Pager is ajaxified
#Html.PagedListPager(Model,
page => Url.Action("GetOrderDetails", new { page, sortOrder = ViewBag.CurrentSort,
, command = "paging", PageSize = ViewBag.PageSize }),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new PagedListRenderOptions { DisplayEllipsesWhenNotShowingAllPageNumbers = false},
new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "gridTable",
OnBegin = "OnBegin", OnSuccess = "OnSuccess" }))
Hide Ellipsis button when Pager is non-ajaxified
#Html.PagedListPager(Model, page => Url.Action("Index", new { page,
sortOrder = ViewBag.CurrentSort, command = "paging", PageSize = ViewBag.PageSize}),
new PagedListRenderOptions { DisplayEllipsesWhenNotShowingAllPageNumbers = false })
ANOTHER SOLUTION
Download the source code from GitHub at https://github.com/troygoode/PagedList and open the solution in Visual Studio.
In HtmlHelper.cs class add the following 2 methods.
private static TagBuilder PreviousEllipsis(IPagedList list, Func<int, string> generatePageUrl, PagedListRenderOptions options, int firstPageToDisplay)
{
var targetPageNumber = firstPageToDisplay - 1;//list.PageNumber - 1;
var previous = new TagBuilder("a")
{
InnerHtml = string.Format(options.EllipsesFormat, targetPageNumber)
};
previous.Attributes["rel"] = "prev";
if (!list.HasPreviousPage)
return WrapInListItem(previous, options, "PagedList-skipToPrevious","disabled");
previous.Attributes["href"] = generatePageUrl(targetPageNumber);
return WrapInListItem(previous, options, "PagedList-skipToPrevious");
}
private static TagBuilder NextEllipsis(IPagedList list, Func<int, string> generatePageUrl, PagedListRenderOptions options, int lastPageToDisplay)
{
var targetPageNumber = lastPageToDisplay + 1;// list.PageNumber +1;
var next = new TagBuilder("a")
{
InnerHtml = string.Format(options.EllipsesFormat, targetPageNumber)
};
next.Attributes["rel"] = "next";
if (!list.HasNextPage)
return WrapInListItem(next, options, "PagedList-skipToNext", "disabled");
next.Attributes["href"] = generatePageUrl(targetPageNumber);
return WrapInListItem(next, options, "PagedList-skipToNext");
}
In same HtmlHelper.cs, replace an existing method of PagedListPager with following code. There are only 2 changes in this code and these are the 2 lines inserted just before the commented line of //listItemLinks.Add(Ellipses(options));. There are 2 places in the method where this line appeared in original code, and these have been commented and replaced by calls to the new methods defined in above code snippet.
///<summary>
/// Displays a configurable paging control for instances of PagedList.
///</summary>
///<param name = "html">This method is meant to hook off HtmlHelper as an extension method.</param>
///<param name = "list">The PagedList to use as the data source.</param>
///<param name = "generatePageUrl">A function that takes the page number of the desired page and returns a URL-string that will load that page.</param>
///<param name = "options">Formatting options.</param>
///<returns>Outputs the paging control HTML.</returns>
public static MvcHtmlString PagedListPager(this System.Web.Mvc.HtmlHelper html,
IPagedList list,
Func<int, string> generatePageUrl,
PagedListRenderOptions options)
{
if (options.Display == PagedListDisplayMode.Never || (options.Display == PagedListDisplayMode.IfNeeded && list.PageCount <= 1))
return null;
var listItemLinks = new List<TagBuilder>();
//calculate start and end of range of page numbers
var firstPageToDisplay = 1;
var lastPageToDisplay = list.PageCount;
var pageNumbersToDisplay = lastPageToDisplay;
if (options.MaximumPageNumbersToDisplay.HasValue && list.PageCount > options.MaximumPageNumbersToDisplay)
{
// cannot fit all pages into pager
var maxPageNumbersToDisplay = options.MaximumPageNumbersToDisplay.Value;
firstPageToDisplay = list.PageNumber - maxPageNumbersToDisplay / 2;
if (firstPageToDisplay < 1)
firstPageToDisplay = 1;
pageNumbersToDisplay = maxPageNumbersToDisplay;
lastPageToDisplay = firstPageToDisplay + pageNumbersToDisplay - 1;
if (lastPageToDisplay > list.PageCount)
firstPageToDisplay = list.PageCount - maxPageNumbersToDisplay + 1;
}
//first
if (options.DisplayLinkToFirstPage == PagedListDisplayMode.Always || (options.DisplayLinkToFirstPage == PagedListDisplayMode.IfNeeded && firstPageToDisplay > 1))
listItemLinks.Add(First(list, generatePageUrl, options));
//previous
if (options.DisplayLinkToPreviousPage == PagedListDisplayMode.Always || (options.DisplayLinkToPreviousPage == PagedListDisplayMode.IfNeeded && !list.IsFirstPage))
listItemLinks.Add(Previous(list, generatePageUrl, options));
//text
if (options.DisplayPageCountAndCurrentLocation)
listItemLinks.Add(PageCountAndLocationText(list, options));
//text
if (options.DisplayItemSliceAndTotal)
listItemLinks.Add(ItemSliceAndTotalText(list, options));
//page
if (options.DisplayLinkToIndividualPages)
{
//if there are previous page numbers not displayed, show an ellipsis
if (options.DisplayEllipsesWhenNotShowingAllPageNumbers && firstPageToDisplay > 1)
listItemLinks.Add(PreviousEllipsis(list, generatePageUrl, options, firstPageToDisplay));
//listItemLinks.Add(Ellipses(options));
foreach (var i in Enumerable.Range(firstPageToDisplay, pageNumbersToDisplay))
{
//show delimiter between page numbers
if (i > firstPageToDisplay && !string.IsNullOrWhiteSpace(options.DelimiterBetweenPageNumbers))
listItemLinks.Add(WrapInListItem(options.DelimiterBetweenPageNumbers));
//show page number link
listItemLinks.Add(Page(i, list, generatePageUrl, options));
}
//if there are subsequent page numbers not displayed, show an ellipsis
if (options.DisplayEllipsesWhenNotShowingAllPageNumbers && (firstPageToDisplay + pageNumbersToDisplay - 1) < list.PageCount)
listItemLinks.Add(NextEllipsis(list, generatePageUrl, options, lastPageToDisplay));
//listItemLinks.Add(Ellipses(options));
}
//next
if (options.DisplayLinkToNextPage == PagedListDisplayMode.Always || (options.DisplayLinkToNextPage == PagedListDisplayMode.IfNeeded && !list.IsLastPage))
listItemLinks.Add(Next(list, generatePageUrl, options));
//last
if (options.DisplayLinkToLastPage == PagedListDisplayMode.Always || (options.DisplayLinkToLastPage == PagedListDisplayMode.IfNeeded && lastPageToDisplay < list.PageCount))
listItemLinks.Add(Last(list, generatePageUrl, options));
if(listItemLinks.Any())
{
//append class to first item in list?
if (!string.IsNullOrWhiteSpace(options.ClassToApplyToFirstListItemInPager))
listItemLinks.First().AddCssClass(options.ClassToApplyToFirstListItemInPager);
//append class to last item in list?
if (!string.IsNullOrWhiteSpace(options.ClassToApplyToLastListItemInPager))
listItemLinks.Last().AddCssClass(options.ClassToApplyToLastListItemInPager);
//append classes to all list item links
foreach (var li in listItemLinks)
foreach (var c in options.LiElementClasses ?? Enumerable.Empty<string>())
li.AddCssClass(c);
}
//collapse all of the list items into one big string
var listItemLinksString = listItemLinks.Aggregate(
new StringBuilder(),
(sb, listItem) => sb.Append(listItem.ToString()),
sb=> sb.ToString()
);
var ul = new TagBuilder("ul")
{
InnerHtml = listItemLinksString
};
foreach (var c in options.UlElementClasses ?? Enumerable.Empty<string>())
ul.AddCssClass(c);
var outerDiv = new TagBuilder("div");
foreach(var c in options.ContainerDivClasses ?? Enumerable.Empty<string>())
outerDiv.AddCssClass(c);
outerDiv.InnerHtml = ul.ToString();
return new MvcHtmlString(outerDiv.ToString());
}
Then, rebuild the code and copy over the dlls to your bin folder. After this you will find that the ellipsis button is enabled, and will take you to the page just before the first page of current set of page numbers, or the page just after this current set. I tested this and it worked.

Related

Silverstripe 4 PaginatedList get Page-number - Link (Backlink)

In Silverstripe 4 I have a
DataObject 'PublicationObject',
a 'PublicationPage' and
a 'PublicationPageController'
PublicationObjects are displayed on PublicationPage through looping a PaginatedList. There is also a Pagination, showing the PageNumbers and Prev & Next - Links.
The Detail of PublicationObject is shown as 'Dataobject as Pages' using the UrlSegment.
If users are on the Detail- PublicationObject i want them to get back to the PublicationPage (the paginated list) with a Back - Link.
Two situations:
The user came from PublicationPage
and
The User came from a Search - Engine (Google) directly to the
DataObject- Detail.
I have done this like so:
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
Thats not satisfying.
Question:
How do i get the Pagination - Link ( e.g: ...publicationen?start=20 ) when we are on the Detail - DataObject? How can we find the Position of the current Dataobject in that paginatedList in correlation with the Items per Page? (The Page- Link This Dataobject is on)
<?php
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\View\Requirements;
use SilverStripe\Core\Convert;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DataObject;
use SilverStripe\ErrorPage\ErrorPage;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Backtrace;
class PublicationPageController extends PageController
{
private static $allowed_actions = ['detail'];
private static $url_handlers = array(
);
public static $current_Publication_id;
public function init() {
parent::init();
}
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
// HERE I WANT TO FIND THE POSITION OF THE DATAOBJECT IN THE PAGINATEDLIST OR RATHER THE PAGE - LINK THIS DATAOBJECT IS IN
//$paginatedList = $this->getPaginatedPublicationObjects();
//Debug::show($paginatedList->find('URLSegment', Convert::raw2sql($request->param('ID'))));
//Debug::show($paginatedList->toArray());
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
public function getPaginatedPublicationObjects()
{
$list = $this->PublicationObjects()->sort('SortOrder');
return PaginatedList::create($list, $this->getRequest()); //->setPageLength(4)->setPaginationGetVar('start');
}
}
EDIT:
is there a more simple solution ? than this ? :
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
//*******************************************************
// CREATE BACK - LINK
$paginatedList = $this->getPaginatedPublicationObjects();
$dO = PublicationObject::get();
$paginationVar = $paginatedList->getPaginationGetVar();
$sqlQuery = new SQLSelect();
$sqlQuery->setFrom('PublicationObject');
$sqlQuery->selectField('URLSegment');
$sqlQuery->setOrderBy('SortOrder');
$rawSQL = $sqlQuery->sql($parameters);
$result = $sqlQuery->execute();
$list = [];
foreach($result as $row) {
$list[]['URLSegment'] = $row['URLSegment'];
}
$list = array_chunk($list, $paginatedList->getPageLength(), true);
$start = '';
$back = '';
$i = 0;
$newArray = [];
foreach ($list as $k => $subArr) {
$newArray[$i] = $subArr;
unset($subArr[$k]['URLSegment']);
foreach ($newArray[$i] as $key => $val) {
if ($val['URLSegment'] === Convert::raw2sql($request->param('ID'))) {
$start = '?'.$paginationVar.'='.$i;
}
}
$i = $i + $paginatedList->getPageLength();
}
$back = Controller::join_links($this->Link(), $start);
// END CREATE BACK - LINK
//*****************************************************
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'MyLinkMode' => 'active',
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
You can simply pass the page number as a get var in the URL from the PublicationPage. The detail() method can then grab the get var and if there's a value for the page number passed in, add it to the back link's URL.
In the template:
<% loop $MyPaginatedList %>
Click me
<% end_loop %>
In your controller's detail() method:
$pageNum = $request->getVar('onPage');
if ($pageNum) {
// Add the pagenum to the back link here
}
Edit
You've expressed that you want this to use the page start offset (so you can just plug it into the url using ?start=[offset]), and that you want an example that also covers people coming in from outside your site. I therefore propose the following:
Do as above, but using PageStart instead of CurrentPage - this will mean you don't have to re-compute the offset any time someone clicks the link from your paginated list.
If there is no onPage (or pageOffset as I've renamed it below, assuming you'll use PageStart) get variable, then assuming you can ensure your SortOrder values are all unique, you can check how many items are in the list before the current item - which gives you your item offset. From that you can calculate what page it's on, and what the page offset is for that page, which is your start value.
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
$paginatedList = $this->getPaginatedPublicationObjects();
// Back link logic starts here
$pageOffset = $request->getVar('pageOffset');
if ($pageOffset === null) {
$recordOffset = $paginatedList->filter('SortOrder:LessThan', $publication->SortOrder)->count() + 1;
$perPage = $paginatedList->getPageLength();
$page = floor($recordOffset / $perPage) + 1;
$pageOffset = ($page - 1) * $perPage;
}
$back = $this->Link() . '?' . $paginatedList->getPaginationGetVar() . '=' . $pageOffset;
// Back link logic ends here
//.....
}

How to add a Web Part into a SitePages/Home.aspx using CSOM

has anyone managed to add a Web Part into a Wiki page using CSOM?
Background: Home.aspx is a Wiki page and all its WPs are located in the rich text zone (in fact a "WikiField" column). Technically they are located in a hidden "wpz" web part zone and in addition to this there is always a placeholder with WP's ID in the WikiField column.
I've modified the existing server-side code seen on http://blog.mastykarz.nl/programmatically-adding-web-parts-rich-content-sharepoint-2010/ and http://640kbisenough.com/2014/06/26/sharepoint-2013-moving-webparts-programmatically-to-rich-content-zone/ into this:
using System;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.WebParts;
public class Class1
{
void DeployWebPart(ClientContext clientContext, string zone, string pattern, string position)
{
List library = clientContext.Web.GetList("/sites/site/SitePages/");
clientContext.Load(library);
clientContext.ExecuteQuery();
CamlQuery query = CamlQuery.CreateAllItemsQuery(100);
ListItemCollection itemColl = library.GetItems(query);
clientContext.Load(itemColl, items => items.Include(i => i.Id, i => i.DisplayName, i => i["WikiField"]));
clientContext.ExecuteQuery();
ListItem item = itemColl.Where(i => i.DisplayName == "Home").First();
clientContext.ExecuteQuery();
File page = item.File;
LimitedWebPartManager lwm = page.GetLimitedWebPartManager(PersonalizationScope.Shared);
string xmlWebPart = #"<webParts>...</webParts>";
WebPartDefinition wpd = lwm.ImportWebPart(xmlWebPart);
WebPartDefinition realWpd = lwm.AddWebPart(wpd.WebPart, "wpz", 0);
List targetList = clientContext.Web.GetList("/sites/site/Announcements/");
clientContext.Load(targetList, l => l.Views);
clientContext.Load(realWpd);
clientContext.ExecuteQuery();
string wpId = String.Format("g_{0}", realWpd.Id.ToString().Replace('-', '_'));
if (zone == "wpz")
{
string htmlcontent = String.Format(CultureInfo.InvariantCulture, "<div class=\"ms-rtestate-read ms-rte-wpbox\" contenteditable=\"false\"><div class=\"ms-rtestate-notify ms-rtestate-read {0}\" id=\"div_{0}\"></div><div id=\"vid_{0}\" style=\"display:none;\"></div></div>", new object[] { realWpd.Id.ToString("D") });
string content = item["WikiField"] as string;
System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(System.Text.RegularExpressions.Regex.Escape(pattern));
if (position == "before")
{
content = regex.Replace(content, (htmlcontent + pattern), 1);
}
else
{
content = regex.Replace(content, (pattern + htmlcontent), 1);
}
item.Update();
clientContext.ExecuteQuery();
}
}
}
Everything works fine until the last item.Update() and clientContext.ExecuteQuery() are being invoked. Before the Update() the new placeholder gets properly inserted into the WikiField contents. But after the Update() the WikiField contents reverts back to its original state (!)
Note: As an alternative it is possible to add the WP into another zone (eg. "Bottom"). In this case the WP gets displayed on the page. But it has two major drawbacks: The newly created zone is not well formatted and the WP cannot be moved or even deleted.
Thanks for any input on this.
The following example demonstrates how to add web part into Enterprise Wiki page:
public static void AddWebPartIntoWikiPage(ClientContext context, string pageUrl, string webPartXml)
{
var page = context.Web.GetFileByServerRelativeUrl(pageUrl);
var webPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared);
var importedWebPart = webPartManager.ImportWebPart(webPartXml);
var webPart = webPartManager.AddWebPart(importedWebPart.WebPart, "wpz", 0);
context.Load(webPart);
context.ExecuteQuery();
string marker = String.Format(CultureInfo.InvariantCulture, "<div class=\"ms-rtestate-read ms-rte-wpbox\" contentEditable=\"false\"><div class=\"ms-rtestate-read {0}\" id=\"div_{0}\"></div><div style='display:none' id=\"vid_{0}\"></div></div>", webPart.Id);
ListItem item = page.ListItemAllFields;
context.Load(item);
context.ExecuteQuery();
item["PublishingPageContent"] = marker;
item.Update();
context.ExecuteQuery();
}
Usage
var webPartXml = System.IO.File.ReadAllText(filePath);
using (var ctx = new ClientContext(webUri))
{
AddWebPartIntoWikiPage(ctx, wikiPageUrl,webPartXml);
}
Result

Add tab to the tabpanel on mvc

I have a menu and some menu items.when I clcik to menu item I create new panle codebehind and add it to main tabpanel.so far so good ,but it seems for every click on the menu,panel created from the begining,plus,change place of the the tabs.how can I solve this.
here is the my Index.cshtml
<body>
#Html.X().ResourceManager()
#(
Html.X().Viewport()
.Layout(LayoutType.Border)
.Items(
Html.X().Panel()
.Region(Region.West)
.Title("main menu")
.Width(200)
.Collapsible(true)
.Split(true)
.MinWidth(175)
.MaxWidth(400)
.MarginSpec("5 0 5 5")
.Layout(LayoutType.Accordion)
.Items(
Html.X().MenuPanel()
.Collapsed(true)
.Icon(Icon.Note)
.AutoScroll(true)
.Title("menu")
.ID("PNL34")
.BodyPadding(0)
.Menu(menu => {
menu.Items.Add(Html.X().MenuItem().ID("1a").Text("test1").Icon(Icon.Anchor)
.DirectEvents(m => { m.Click.Url = "Desktop/AddTab";
m.Click.ExtraParams.Add(new { conid = "TabPanel1" ,pnlid="tabpnl10",viewname="Urunler"});
}));
menu.Items.Add(Html.X().MenuItem().ID("2a").Text("test2").Icon(Icon.Anchor)
.DirectEvents(m =>
{
m.Click.Url = "Desktop/AddTab";
m.Click.ExtraParams.Add(new { conid = "TabPanel1", pnlid = "tabpnl11", viewname = "Siparisler" });
}));
})
)
,
Html.X().TabPanel()
.ID("TabPanel1")
.Region(Region.Center)
.Title("E-TICARET")
.MarginSpec("5 5 5 0")
))
and codebehind controller
public ActionResult AddTab(string conid,string pnlid,string viewname)
{
var cmp = this.GetCmp<Panel>(pnlid);
var cmp2 = this.GetCmp<TabPanel>(conid);
if (cmp.ActiveIndex==-1)
{
var result = new Ext.Net.MVC.PartialViewResult
{
ViewName = viewname,
ContainerId = conid,
RenderMode = RenderMode.AddTo,
WrapByScriptTag = false
};
cmp2.SetActiveTab(pnlid);
return result;
}
else
{
return null;
}
}
This is not going to work.
if (cmp.ActiveIndex == -1)
In WebForms it is retrieved from the Post data. There is no a WebForms-like Post in MVC. You should send all the required information with a request.
Also if you don't need a tab to be rendered if it is already exists, just stop a request. You can determine on client if a tab is already there or not.

Dropdown field - first item should be blank

Using sharepoint build in lookup column and it set to required field. SharePoint automatically selects the first item in the dropdown box (kinda misleading for the end users).
Is there a way to display blank or Null for the first row of this drop down box?
(I am open to any solution. I prefer javascript type solution)
For Choice fields, the default value is configured in the column settings. If the "Default value" input box is populated, delete the value in order to use no default value.
Edit
For Lookup fields, the field seems to change dramatically if it is required. Fields that are NOT required have a "(None)" value by default. However, toggling the field to required will remove the "(None)" value and the first value is selected automatically.
One thing I found, is that if you use JavaScript to add the null value to the dropdown and then try to press OK you get an error page: "An unexpected error has occurred." As a workaround, I wrote some more code to do a quick validation that the field has a value before the form is submitted. If the field has no value, then it will prompt the user and cancel the submit. (Note: this code is only attached to the OK buttons so you may get errors while editing EditForm.aspx.. just choose a value for your lookup field and you'll be able to edit like normal)
Anyways, onto the code... I think the only line you'll need to change is var fieldTitle = 'Large Lookup Field'; to update it to the name of your field.
<script type="text/javascript">
function GetDropdownByTitle(title) {
var dropdowns = document.getElementsByTagName('select');
for (var i = 0; i < dropdowns.length; i++) {
if (dropdowns[i].title === title) {
return dropdowns[i];
}
}
return null;
}
function GetOKButtons() {
var inputs = document.getElementsByTagName('input');
var len = inputs.length;
var okButtons = [];
for (var i = 0; i < len; i++) {
if (inputs[i].type && inputs[i].type.toLowerCase() === 'button' &&
inputs[i].id && inputs[i].id.indexOf('diidIOSaveItem') >= 0) {
okButtons.push(inputs[i]);
}
}
return okButtons;
}
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value',value);
if (typeof(optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option,options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
oDropdown.options.selectedIndex = 0;
}
function WrapClickEvent(element, newFunction) {
var clickFunc = element.onclick;
element.onclick = function(event){
if (newFunction()) {
clickFunc();
}
};
}
function MyCustomExecuteFunction() {
// find the dropdown
var fieldTitle = 'Large Lookup Field';
var dropdown = GetDropdownByTitle(fieldTitle);
if (null === dropdown) {
alert('Unable to get dropdown');
return;
}
AddValueToDropdown(dropdown, '', '', 0);
// add a custom validate function to the page
var funcValidate = function() {
if (0 === dropdown.selectedIndex) {
alert("Please choose a value for " + fieldTitle + ".");
// require a selection other than the first item (our blank value)
return false;
}
return true;
};
var okButtons = GetOKButtons();
for (var b = 0; b < okButtons.length; b++) {
WrapClickEvent(okButtons[b], funcValidate);
}
}
_spBodyOnLoadFunctionNames.push("MyCustomExecuteFunction");
</script>
In response Kit Menke, I've made a few changes to the code so it will persist previous value of the dropdown. I have added the following lines of code to AddValueToDropdown()....
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var selectedIndex
if (oDropdown.options.selectedIndex)
selectedIndex = oDropdown.options.selectedIndex;
else
selectedIndex = -1;
// original code goes here
// changed last line of code (added "selectedIndex+1")
oDropdown.options.selectedIndex = selectedIndex+1;
}
To improve on top of Aaronster's answer: AddValueToDropdown can be done that way:
var injectedBlankValue = false;
function AddValueToDropdown(oDropdown, text, value, optionnumber) {
for (i = 0; i < oDropdown.options.length; i++) {
option = oDropdown.options[i];
if(option.getAttribute('selected')) // If one is already explicitely selected: we skip the whole process
return;
}
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value', value);
if (typeof (optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option, options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
// changed last line of code (added 'selectedIndex+1')
oDropdown.options.selectedIndex = 0;
injectedBlankValue = true;
}
This is needed for document libraries where "add" and "set properties" are two distinct pages.
And funcValidate starts with:
var funcValidate = function () {
if (!injectedBlankValue)
return true;
All these changes is to make the whole thing work with document libraries.

How to show ID field as readonly in Edit Form, of a sharepoint list?

I need to show the ID field in the Edit Form of a sharepoint list.
There is a way to do it ? I tried a calculated field and nothing.
I know that I can see the ID field in the view, and if I show as a Access Mode.
I'm using WSS3.0
You can add the ID field to the form using some JavaScript in a CEWP.
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>
<script type="text/javascript">
$(function() {
// Get the ID from the query string
var id = getQueryString()["ID"];
// Find the form's main table
var table = $('table.ms-formtable');
// Add a row with the ID in
table.prepend("<tr><td class='ms-formlabel'><h3 class='ms-standardheader'>ID</h3></td>" +
"<td class='ms-formbody'>" + id + " </td></tr>");
})
function getQueryString() {
var assoc = new Array();
var queryString = unescape(location.search.substring(1));
var keyValues = queryString.split('&');
for (var i in keyValues) {
var key = keyValues[i].split('=');
assoc[key[0]] = key[1];
}
return assoc;
}
</script>
There is an alternative method that doesn't use the jQuery library if you prefer to keep things lightweight.
You can do this by creating a custom edit form quite easily. I usually stick it into an HTML table rendered within a webpart. There may be a better way of doing that but it's simple and it works.
The key line you'll want to look at is spFormField.ControlMode. This tells SharePoint how to display the control (Invalid, Display, Edit, New). So what you'll want to do is check if your spField.InternalName == "ID" and if it is, set the ControlMode to be Display.
The rest is just fluff for rendering the rest of the list.
Hope this helps.
HtmlTable hTable = new HtmlTable();
HtmlTableRow hRow = new HtmlTableRow();
HtmlTableCell hCellLabel = new HtmlTableCell();
HtmlTableCell hCellControl = new HtmlTableCell();
SPWeb spWeb = SPContext.Current.Web;
// Get the list we are going to work with
SPList spList = spWeb.Lists["MyList"];
// Loop through the fields
foreach (SPField spField in spList.Fields)
{
// See if this field is not hidden or hide/show based on your own criteria
if (!spField.Hidden && !spField.ReadOnlyField && spField.Type != SPFieldType.Attachments && spField.StaticName != "ContentType")
{
// Create the label field
FieldLabel spLabelField = new FieldLabel();
spLabelField.ControlMode = _view;
spLabelField.ListId = spList.ID;
spLabelField.FieldName = spField.StaticName;
// Create the form field
FormField spFormField = new FormField();
// Begin: this is your solution here.
if (spField.InteralName == "ID")
{ spFormField.ControlMode = SPControlMode.Display; }
else
{ spFormField.ControlMode = _view; }
// End: the end of your solution.
spFormField.ListId = spList.ID;
spFormField.FieldName = spField.InternalName;
// Add the table row
hRow = new HtmlTableRow();
hTable.Rows.Add(hRow);
// Add the cells
hCellLabel = new HtmlTableCell();
hRow.Cells.Add(hCellLabel);
hCellControl = new HtmlTableCell();
hRow.Cells.Add(hCellControl);
// Add the control to the table cells
hCellLabel.Controls.Add(spLabelField);
hCellControl.Controls.Add(spFormField);
// Set the css class of the cell for the SharePoint styles
hCellLabel.Attributes["class"] = "ms-formlabel";
hCellControl.Attributes["class"] = "ms-formbody";
}
}

Resources