ControlList which contains 2 items returns first items Text element for both items - atata

I have the following code:
public ControlList<PaymentItem, _> PaymentGroup { get; private set; }
[ControlDefinition("div[#id='grant-details']//div", ContainingClass = "payment-details", ComponentTypeName = "Payment Item")]
public class PaymentItem : Control<_>
{
[FindByXPath("//a[contains(#class, 'test-payment-details-toggle')]")]
public Link<_> Caret { get; private set; }
[FindByXPath("//strong[text() = 'Special Instructions:']/parent::p")]
public Text<_> SpecialInstructions { get; private set; }
}
When I run, the HTML in the source code has 2 div's that match the PaymentItem, the first one of them has the SpecialInstructions Text object with a string value of "None" while the second has some random text, but PaymentGroup[0].SpecialInstructions and PaymentGroup[1].SpecialInstructions both return the value of "None" - it seems to be performing the FindByXPath from the document root instead of the //div with the matching class - what am I doing wrong?

The problem is in incorrect XPath in [FindByXPath("//strong[text() = 'Special Instructions:']/parent::p")]. Either replace // in the beginning with .//, or just remove it (.// will be prepended under the hood by default).
// - finds an element from the root of HTML document.
.// - finds an element inside the element of parent Atata component.
Additionally, I would simplify [FindByXPath("//a[contains(#class, 'test-payment-details-toggle')]")] to [FindByClass("test-payment-details-toggle")]. Link<TOwner> refers to <a> in its control definition anyway, so you just need to add a class selector.

Related

How do I teach Automapper to project X to IContainer<Y>?

For the same reasons as this question, I would like to teach automapper to project into INC<> of vmodel below (and not back in this case), during something like SomeContext.GetIQueryable<model>().ProjectTo<vmodel>()
public interface INC<T> { T Value { get; set; } }
public class NC<T> : INC<T> { public T Value { get; set; } }
public class model { public int value { get; set; } }
public class vmodel { public IC<String> Value { get; set; } }
I'm trying to do it generically in here: https://gist.github.com/flavourous/9a2668daa41cfedb8359b26eac319df2
Here's me trying to do it explicitly: https://gist.github.com/flavourous/d35bac140bb2883ebd82846a2b75ec7e
Here's the problems I'm hitting:
Cannot cast inside nongeneric ProjectUsing of ForAllMaps
When calling ProjectUsing inside ForAllMaps, the argument is of type Expression<Func<object,object>>.
ProjectUsing((object src)=>new NC<String>("test")) works fine, but any cast on object src give me Rewriting child expression..is not allowed because it would change the meaning of the expression.
How to generate an expression mapping to the inner type?
AM needs to map the ProjectUsing expression to a query expression for the IQueryable it translates from the source IQueryable, and I want to express the conversion expression of X->Y that it would normally do if the INC<> wasn't there.
Mapper.Configuration.ExpressionBuilder.GetMapExpression<,>() looks like an option but I can't get a ExpressionBuilder inside Initialise?

DropDownListFor not selecting the right value on an Edit screen

I'm using a dropdown list for a form. It works and saves just fine when I create a form. But when I load it into the Edit screen, the correct option isn't being selected by default. Instead, the first option is being selected.
I put a breakpoint at the line in the View to see if the id/value for the property is right, which it is. I'm just not sure if it's the code in the View that's wrong or how I'm passing it through the controller.
I used DropDownList before and it worked okay, but for validation purposes I switched to DropDownListFor and I immediately encountered this problem.
View (Edit)
#Html.DropDownListFor(model => model.Item_ID, (SelectList)ViewBag.Item_ID, new { #class = "form-control" })
Controller
List<ItemInfo> itemIDItems = dbContext.Items.OrderBy(item => item.Item_Title).ToList<ItemInfo>();
ViewBag.Item_ID = new SelectList(itemIDItems, "Item_ID", "Item_Title", object.Item_ID);
Model (Object, generated through EF)
public int Obj_ID { get; set; }
...
public Nullable<int> Item_ID { get; set; }
...
public virtual Items Items { get; set; }
Model (Items, generated through EF)
public int Item_ID { get; set; }
public string Item_Title { get; set; }
You need to change the name of the ViewBag property so it does not match the name of the property your binding to. For example your GET method should be
var model = // get your existing model
List<ItemInfo> itemIDItems = dbContext.Items.OrderBy(item => item.Item_Title).ToList<ItemInfo>();
ViewBag.ItemList = new SelectList(itemIDItems, "Item_ID", "Item_Title");
return View(model);
Note that the 4th parameter of the SelectList constructor is unnecessary (it's ignored by the DropDownListFor() method) because its the value of the property your binding to that determines which option is selected.
And the view will be
#Html.DropDownListFor(m => m.Item_ID, (SelectList)ViewBag.ItemList, new { #class = "form-control" })
If the value of Item_ID matches the value of one of the options, then that option will be selected.

Orchard ContentItem's AutoroutePart DisplayUrl

I have a custom content type built through the UI (e.g. not via a module) that has a couple of fields on it, one of which is a ContentItemPicker. I managed to get everything with the front-end working for this with the exception of finding the friendly URL off of the ContentItem from the Model's collection of items. I'm seeing some examples where I'm supposed to use Url.ImageDisplayUrl([ContentItem]), but that gives me this error: 'System.Web.Mvc.UrlHelper' has no applicable method named 'ItemDisplayUrl' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
My using statements at the top are as follows:
#using Orchard.ContentPicker.Fields
#using Orchard.Utility.Extensions;
#using System.Linq
#using Orchard.ContentManagement;
#using Orchard.Mvc.Html;
#using Orchard.Utility.Extensions;
I'd assume I'm missing something with those, but can't seem to figure out what. The way I'm building out my view is below, and the URL I am trying to get is off of tab.ContentItem.HomepageTab.NavigationItem:
/** #Model.Items is a collection of my custom content types that were created through the UI **/
foreach (var tab in #Model.Items)
{
var t = new SliderTab
{
DisplayOrder = tab.ContentItem.HomepageTab.DisplayOrder.Value,
ButtonText = tab.ContentItem.HomepageTab.ButtonText.Value,
Description = tab.ContentItem.HomepageTab.Description.Value,
ImageUrl = tab.ContentItem.HomepageTab.Image.Url,
Title = tab.ContentItem.TitlePart.Title,
ContentItem = tab.ContentItem,
TabText = tab.ContentItem.HomepageTab.TabText.Value
};
/** HomepageTab is the custom content type created in the Orchard UI which has a ContentPickerField associated with it. The name on that is NavigationItem, so I just need the friendly URL off of a ContentPickerField's associated ContentItem **/
if (tab.ContentItem.HomepageTab.NavigationItem != null && tab.ContentItem.HomepageTab.NavigationItem.Ids != null)
{
//this is way, super hacky - getting the actual friendly URL would be better
t.NavigateUrl = "/Contents/Item/Display/" + tab.ContentItem.HomepageTab.NavigationItem.Ids[0];
}
tabs.Add(t);
}
** Edit **
I have a class declaration for HomepageTab at the top which does not correlate to the tab.ContentItem.HomepageTag as that is dynamic off the ContentItem property. It is structured like this:
public class HomepageTab
{
public dynamic DisplayOrder { get; set; }
public string ImageUrl { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ButtonText { get; set; }
public dynamic ContentItem { get; set; }
public string TabText { get; set; }
public string NavigateUrl { get; set; }
public string TabId
{
get { return "t" + this.DisplayOrder.ToString(); }
}
}
Thoughts?
tab.ContentItem.HomepageTab.NavigationItem is your content item picker field, but expressed this way, it's a dynamic object, so the compiler can get all sorts of confused if you try to use it without casting it. So first I'd recommend casting:
var pickerField = tab.ContentItem.HomepageTab.NavigationItem as ContentPickerField;
if (pickerField != null) {
Then you can get the first and only item in the field (caution, we're likely causing a select N+1 issue here, see below):
var firstItem = pickerField.ContentItems.FirstOrDefault();
Finally, we can ask for the display URL for that item:
if (firstItem != null) {
var url = Url.ItemDisplayUrl(firstItem);
This should work just fine. Be careful however: as I said above, getting the collection of items for each tab may trigger one new database query per tab, degrading performance. To avoid that problem, you could pre-fetch the related content items with a technique similar to what I describe in this post: https://weblogs.asp.net/bleroy/speeding-up-pages-with-lots-of-media-content-items
Instead of pre-fetching images, what you'd do here is first get a list of all the related ids (that's free, those ids come stored in the fields, which are stored with the content items you already have). Then you'd build a local cache of ids to items using a GetMany. And finally you'd use that cache instead of the ContentItems collection to look-up items from the ids.
I hope this makes sense.

categorize ServiceStack methods

Imagine I have two "areas" in my API, inventory and orders. I can quite easily group all methods related to inventory into "/inventory/" and to orders "/orders/" routes.
However, when I go to the root page of API where all methods are shown (IndexOperations.html) all methods are mixed together into one big list.
Is there any way to group methods from different areas on that list? For example show something like this on the operations index page.
Inventory
Method1
Method2
Orders
Method1
Method2
Group your operations:
If you group your DTOs into a static class as shown below, then ordering will be taken care of automatically assuming you want the groups alphabetically.
public static class UserOperations
{
[Route("/Users","POST")]
public class CreateUserRequest
{
public string Name { get; set; }
public int Age { get; set; }
}
...
}
public static class DuckOperations
{
[Route("/Ducks","POST")]
public class CreateDuckRequest
{
public string Name { get; set; }
public int Age { get; set; }
}
...
}
Alternatively specify the sort:
The ServiceStack MetadataFeature in v4.09+ provides access to the IndexPageFilter which lets you specify specify the Sort function that is applied to the index pages' OperationNames, where the OperationName is the full type name of the DTO.
var metadata = Plugins.First(x => x is MetadataFeature) as MetadataFeature;
// This is the default sort, replace with one that groups
metadata.IndexPageFilter = (page) => page.OperationNames.Sort((a,b) => b.CompareTo(a));
I hope this helps.

ASP.NET MVC DropDownListFor not selecting the correct value

I'm new in the ASP.NET Framework, I've read the fundamental and have some understanding(theory) on the framework but not much in practice.
I'm struggling with the dropdownlistfor helper method, it comes down to having a weird behavior when i attempt to change the value of the selected item programatically.
In my controller i have the Index action method that receives a parameter of type Tshirt, inside this action method i set the property named Color of the Tshirt object with a value of 2.
In the view (strongly typed) i have a list of colors and pass this list as an argument for the constructor of the ColorViewModel class that will be in charge of returning SelectListItems for my list of colors.
In the view I then set the Selected property of my ColorViewModel object with the value coming from model.Color, now i have set everything so that when i call
#Html.DropDownListFor(x => x.Color, cvm.SelectItems, "Select a color")
I will have my dropdown displayed with the selected item.
When the request(GET) is performed the page is rendered by the browser and the dropdownlist appears with the correct value selected that was established with the value 2 in the Index action method, the dropdown displays "Blue" this is correct.
Then if i select a different item in the dropdownlist (RED, having an id of one) and submit the form, the Save action method is called and i know that the model is reaching the action method with a model.Color=1, which is correct.
Then i decide to redirect to the index action method passing the model object, the index action method changes the Color property back to 2, so when the page is rendered it should display again the value of Blue in the dropdown, but it doesn't, it displays Red.
if you comment out the following line in the Save action method you will get a different behavior.
//tshirt.Color = 3;
i know this logic im following doesnt make much sense from a bussines logic perspective, im just trying to understand what i am doing wrong to not get the expected result.
Given the following model
public class Color
{
public int Id { get; set; }
public string Description { get; set; }
}
I Create the following view model
public class ColorViewModel
{
public int Selected { get; set; }
private List<Color> Colors;
public IEnumerable<SelectListItem> SelectItems { get { return new SelectList(this.Colors, "Id", "Description", this.Selected); } }
private ColorViewModel() { }
public ColorViewModel(List<Color> colors)
{
this.Colors = colors;
}
}
This is my Controller
public class HomeController : Controller
{
[HttpGet()]
public ActionResult Index(Tshirt tshirt)
{
tshirt.Color = 2;
Tshirt t = new Tshirt();
t.Color = tshirt.Color;
return View(t);
}
[HttpPost()]
public ActionResult Save(Tshirt tshirt)
{
//tshirt.Color = 3;
return RedirectToAction("Index", tshirt);
//return View("Index",tshirt);
}
}
And Finally my View
#{
List<Color> colors = new List<Color>(){
new Color(){Id=1, Description="Red"},
new Color(){Id=2, Description="Blue"},
new Color(){Id=3, Description="Green"}
};
ColorViewModel cvm = new ColorViewModel(colors) { Selected = Model.Color };
}
#using(#Html.BeginForm("Save","Home")){
#Html.DropDownListFor(x => x.Color, cvm.SelectItems, "Select a color")
<input type="submit" />
}
I have uploaded the complete code: VS Solution
Because when you redirect, you are passing the updated model
return RedirectToAction("Index", tshirt);

Resources