Here is my issue. On my page I am using several webgrids pulling from various tables in the database. I am having an issue get the column with the data to display. If I comment out that column, the grid will display and have to correct amount of pages for the amount of data in that table. If I put that column back in, I get the name of that column does not exist. This is happening for all the grids. So here is the setup of the code:
On the Controller page:
var cdb = new CommonModel();
IEnumerable<SelectListItem> _for = cdb.Formats
.OrderBy(f => f.FormatName)
.Select(f => new SelectListItem { Value = f.FormatID.ToString(), Text = f.FormatName });
ViewBag.Forma = _for;
On the view page:
#model HomeInventory.Web.Areas.Classes.Video
#{
ViewBag.Title = "Add Movies / Television Shows";
Layout = "~/Views/Shared/_MovieLayout.cshtml";
}
#{
var fgrid = new WebGrid(source: ViewBag.Forma,
defaultSort: "FormatName",
rowsPerPage: 3, canSort: false,
pageFieldName: "pg");
}
#fgrid.GetHtml(tableStyle: "grid", columns: fgrid.Columns(fgrid.Column(format: (Formats) => Html.CheckBox("FormatID")), fgrid.Column("FormatName")))
#Html.ValidationMessageFor(model => model.Formats, "", new { #class = "text-danger" })
If I run the page, I get the following error:
Column "FormatName" does not exist. The table has it listed as FormatName, but I have also tried Format, Formats, and Name with the same result that the column does not exist. It is quite possible I do not have something set up correctly, causing this issue. Does anyone see what I may have missed?
You've used LINQ to select your rows into a collection of SelectListItem objects, so no, there would not be a FormatName column. Your choices would be (from https://msdn.microsoft.com/en-us/library/system.web.mvc.selectlistitem_properties%28v=vs.118%29.aspx):
Disabled
Group
Selected
Text
Value
If you want to use the columns from your table, don't use SelectListItem.
Related
How can I get the name of a variant product via the product repository? So for example t-shirt L. Or only the option.
This is my code:
$cri2 = new Criteria();
$cri2->addFilter(new EqualsFilter('parentId', $itemProductId));
$cri2->addFilter(new EqualsFilter("active", 1));
$cri2->addFilter(new RangeFilter('stock', [ 'gt' => 0 ]));
I need the name of this option:
I believe the confusion is here:
Just note that this is not the name of the variant, it's just it's options concatenated as preview.
You can see inn the picture below that the actual name is null & therefore inherits the parents' name:
If you change the name from the variant product, then it will not be null anymore.
To create an actual name you will have to manually pull its options using associations. Here is an example of a query you might be looking for. After you get the data, just create the name yourself out of the variation data. High chance that this is how Shopware6 does it in the picture provided.
$criteria = (new Criteria()
// loads non-child products
->addFilter(new EqualsFilter('product.parentId', null))
->addAssociations([
'options',
'variation',
'children',
'children.options',
'children.properties',
'children.properties.group'
])
When you want to search for a specific name you can use
$cri2->addFilter(new EqualsFilter('name', 't-shirt L'));
When you want to search for the option name instead you can use
$cri2->addFilter(new EqualsFilter('options.option.name', 't-shirt L'));
When you don't want to search by name, but only what to get the name of a given product:
$product = $this->productRepository->search($cri2, $context)->getFirst();
echo $product->getName();
The finished code is:
$cri = new Criteria();
$cri->addFilter(new EqualsFilter('parentId', "[YOUR PARENT ID]"));
// Add options to associations
$cri->addAssociations([
'options'
]);
// Loop through variant products
foreach ($productRepository->search($cri, $context)->getElements() as &$variantRawItem) {
// Map all options to array
$options = array_map(function ($n) {
return $n->get('translated')['name'];
}, $variantRawItem->get('options')->getElements());
// Create the options title
$optionsTitle = implode(', ', $options);
// $optionsTitle is now e.g.: L, Lila
}
I understand SharePoint lists are like excel, so I was wondering if it was possible to conditionally highlight whole rows/ cells based on the text value of a field.
I have a column in a list called "Status" with 4 options (initial, in progress, completed, awaiting developer resource). I would like to highlight these rows (or even just the status field) a different colour, depending on the value of the status.
Is this possible? Cant find anything relating to this for SP 2016
Cheers
Please use JavaScript to highlight the row based on the Status field:
<script type="text/javascript">
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var statusColors = {
'initial' : '#FFF1AD',
'in progress' : '#FFD800',
'completed' : '#01DF3A',
'awaiting developer resource':'#ff0000'
};
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var status = rows[i]["Status"];
var rowId = GenerateIIDForListItem(ctx, rows[i]);
var row = document.getElementById(rowId);
row.style.backgroundColor = statusColors[status];
}
}
});
});
</script>
And place the code above in a Content Editor Web Part in the list view page, so the list row will render different color based on status:
I use CSOM .NET to load task objects from Project Server 2013, and I need to
filter tasks so that only a subset of them is returned, and
load only a subset of task columns, specified at runtime by the user.
I found this post that shows how to load a dynamic set of columns, and it works nicely for my second requirement. However, I cannot figure out a workable LINQ syntax to combine both column selection and row filtering.
In the example below, I need to load only those "rows" for summary tasks (where t.IsSummary is true), and I want to load only the Name, Start, and Finish columns.
The following code from the referenced post loads just the three columns that I need:
foreach (string fieldName in new List<string>(){"Name","Start","Finish"});
{
ctx.Load(ctx.Tasks,c => c.Include(t => t[fieldName]));
}
ctx.ExecuteQuery();
But when I try to combine where() and include() in the only syntax that makes sense to me, I get InvalidQueryExpressionException on second iteration through the foreach loop: "The query expression is not supported."
foreach (string fieldName in new List<string>(){"Name","Start","Finish"});
{
ctx.Load(ctx.Tasks,
c => c.Where(t => t.IsSummary),
c => c.Include(t => t[fieldName])
);
}
ctx.ExecuteQuery();
I get the same error if I reverse the order of where and include clauses. If I pull the where clause outside of the loop over field names and make it a separate Load call, the summary-task row filtering works, but I lose the dynamic selection of tasks fields. There must be a syntax in LINQ for CSOM that meets both requirements. What is the correct syntax to do this type of query?
The following example demonstrates how to apply select and filter operators in SharePoint CSOM API:
var list = ctx.Web.Lists.GetByTitle(listTitle);
var items = list.GetItems(CamlQuery.CreateAllItemsQuery());
var result = ctx.LoadQuery(items.Where(i => (bool)i["IsSummary"]).Include(i => i["Name"], i => i["Start"], i => i["Finish"]));
ctx.ExecuteQuery();
foreach (var item in result)
{
Console.WriteLine(item["Name"]);
}
So, i believe the following expression is supported in Project Server CSOM API:
var result = ctx.LoadQuery(ctx.Tasks.Where(t => (bool)t["IsSummary"]).Include(t => i["Name"], t => t["Start"], t => t["Finish"]));
ctx.ExecuteQuery();
I answered this myself by using expression trees, which let you filter a set of rows and select a set of columns based on parameters that are only known at runtime. In the example below, I simulate finding out at runtime that I need to filter the tasks on the IsSummary column and that I should retrieve only the five columns Id, Name, Start, IsSubProject, and Finish. Here's the code:
using System.Linq.Expressions;
// Input parms discovered at runtime
string filterColumnName = "IsSummary";
List<string> columnNames = new List<string>(
new[] { "Id", "Name", "Start", "IsSubProject", "Finish" });
// Get the client object for the Published Project matching projGuid
ctx.Load(ctx.Projects, c => c.Where(p => p.Id == projGuid));
ctx.ExecuteQuery();
PublishedProject proj = ctx.Projects.Single();
// Compute the expression tree for filtering the task rows
var taskParm = Expression.Parameter(typeof(PublishedTask), "t");
var predicate = Expression.PropertyOrField(taskParm, filterColumnName);
var filterExpression = Expression.Lambda<
Func<PublishedTask, bool>>(predicate, taskParm);
// Dynamically generate expression tree for each column to be included
var colSelectionList = new List<Expression<
Func<PublishedTask, object>>>();
foreach (var colName in columnNames)
{
var fldExpr = Expression.PropertyOrField(taskParm, colName);
var fldAsObjExpr = Expression.Convert(fldExpr, typeof(object));
var colSelectorExpr = Expression.Lambda<
Func<PublishedTask, object>>(fldAsObjExpr, taskParm);
colSelectionList.Add(colSelectorExpr);
}
// Create query using LoadQuery (Load does not work here)
var taskList = ctx.LoadQuery(proj.Tasks
.Where(filterExpression)
.Include(colSelectionList.ToArray())
);
// Execute the query
ctx.ExecuteQuery();
// taskList now contains just the filtered rows and selected columns
I hope this helps someone else who is stuck on using CSOM to do this for Project Server. I found these two references helpful:
At MSDN
and at Second Life of a Hungarian SharePoint Geek
..Jim
Is it possible to eager load a field when querying content using the ContentManager?
I'm using the ContentManager to retrieve all content items of a specific content type. The content type has a MediaLibraryPickerField on it which is creating a select n+1 issue when I iterate over the results of the query. I'd like to force this data to be loaded upfront (join on initial query). This seems straightforward for a ContentPart but I can't get it to work for a ContentField. Is this possible or is there another way to avoid the select n+1 issue with fields?
Here's what I've tried but it has not effect:
var myQuery = _contentManager.Query(new[] { "MyContentType" })
.WithQueryHints(new QueryHints().ExpandParts<MediaPart>());
I've also tried expanding the record:
var myQuery = _contentManager.Query(new[] { "MyContentType" })
.WithQueryHints(new QueryHints().ExpandRecords<MediaPartRecord>());
Here's how I fixed the problem for a projection page, but the same method, or something simpler, could be applied in your case.
In an alternate template for the Content shape of the projection page, Content-ProjectionPage.cshtml, I did the following, which creates a lookup for media that items will be able to use later:
// Pre-fetch images
var projectionItems = ((IEnumerable<dynamic>)
((IEnumerable<dynamic>)Model.Content.Items)
.First(i => i.Metadata.Type == "List").Items)
.Select(s => (ContentItem)s.ContentItem);
var mediaLibraryFields = projectionItems
.SelectMany(i => i.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField)))
.Cast<MediaLibraryPickerField>();
var firstMediaIds = mediaLibraryFields
.Select(f => f.Ids.FirstOrDefault())
.Where(id => id != default(int))
.Distinct()
.ToArray();
var firstMedia = WorkContext.Resolve<IContentManager>()
.GetMany<MediaPart>(firstMediaIds, VersionOptions.Published, QueryHints.Empty);
var mediaCache = Layout.MediaCache == null
? Layout.MediaCache = new Dictionary<int, MediaPart>()
: (Dictionary<int, MediaPart>) Layout.MediaCache;
foreach (var media in firstMedia) {
mediaCache.Add(media.Id, media);
}
In your case, you don't have to do the complicated drilling into shapes to dig out the fields, as you have access to them directly. I had to do that because the view or a shape table provider is unfortunately the easiest place for me to do that.
Then, when I want to display an image, all I have to do is access my lookup and try to get it from there. In my alternate template MediaLibraryPicker.Summary.cshtml, I do this:
var field = (MediaLibraryPickerField)Model.ContentField;
var imageIds = field.Ids;
if (imageIds.Any()) {
var cm = Model.ContentPart.ContentItem.ContentManager as IContentManager;
var title = cm == null || Model.ContentPart == null
? "" : cm.GetItemMetadata(Model.ContentPart).DisplayText;
var mediaCache = Layout.MediaCache as Dictionary<int, MediaPart>;
var firstImage = mediaCache != null
? mediaCache[imageIds.First()]
: cm.Get(imageIds.First()).As<MediaPart>();
<div class="gallery">
<img src="#Display.ResizeMediaUrl(Path: firstImage.MediaUrl, Width: 132)" class="main" alt="#title"/>
</div>
}
I'm only displaying the first image in the field, here, but you could change that where it does f.Ids.FirstOrDefault(). Just do f.Ids instead and replace the Select with a SelectMany. Also change the summary template so it displays all images after looking them up in the same dictionary.
Once I did that, I had no select N+1, and instead got a single SQL query for all the images on the page.
A site I'm working on has a complicated "mega menu" type navigation. I would like content editors to be able to group menu items by column in the markup. Normally if I want 6 columns I would register 6 menus and name them columns 1-6, however these are dynamic sub-menus that need to be a child of another navigation item inside another menu.
What I'd really like to do is create a new type of nav-menu item (Currently Pages, Links, Categories, as well as my other custom post types and custom taxonomies) where that item would be just for columns. (The fact that I'm using it for columns isn't important here. This could just as easily be dividers, or something else. I just need a new nav-menu item type that I can create special markup with when I'm building the menus.)
Is there a way to create a new nav-menu item type without having to create a custom post type or custom taxonomy that is used only for this purpose?
What I ended up doing for this, was adding a new post type that was hidden everywhere on the site except the nav menus. I then added just a single entry of that post type type, and hid some of the fields.
<?php
function navMenuColumns_init() {
register_post_type('menu_column',
array(
'labels' => array(
'name' => __('Menu Columns'),
'singular_name' => __('Menu Column')
),
'supports' => array('title'),
// Doesn't need to be listed most places as it's not a valid content item on it's own
'public' => false, // Base setting. More specific settings below
'exclude_from_search' => false,
'publicly_queryable' => false,
'show_ui' => false,
'show_in_menu' => false,
'show_in_nav_menus' => true, // The only thing this is used for
'show_in_admin_bar' => false,
'has_archive' => false,
)
);
$_version = (float)get_option('navMenuColumns_version', 0.0);
if ($_version < 1.0) {
navMenuColumns_install10();
}
add_action('admin_footer', 'navMenuColumns_adminFooter');
}
function navMenuColumns_install10() {
$navMenuPost = wp_insert_post(array(
'post_type' => 'menu_column',
'post_title' => 'Column',
'post_status' => 'publish'
), true);
if (!is_wp_error($navMenuPost)) {
update_option('navMenuColumns_version', 1.0);
}
}
function navMenuColumns_adminFooter() {
?><script>
jQuery(document).ready(function($) {
// Hides most of the fields when editing a Menu Column item.
$('#menu-to-edit').on('click', 'a.item-edit', function() {
var $li = $(this).parents('li.menu-item');
if ($li.find('.item-type').text() == 'Menu Column') {
$li.find('p.description').hide();
$li.find('p.link-to-original').hide();
$li.find('p.field-move').show();
}
});
});
</script><?php
}
add_action('init', 'navMenuColumns_init');
?>
This allows the user to add this as a normal menu item. This won't play well with functions that build the menu markup for you, but if you traverse the menu item and build the markup for you, you can target this post type with custom markup.