So my problem is like this. In sitecore view i can write someting like this:
#Html.Sitecore().Field(PictureID.ToString(), Model.Person.Item, cssClass: "img-responsive");
which will get me the HtmlString for the field with ID PictureID on the item Model.Person.Item and in that HtmlString i will have a cssClass added. So the result will be something like
<img src="xxxx" class="img-responsive"/>
For this View the Model will be RenderingModel. But what if i have a Person as a model and this person would have a property called Picture which will be retrieved somewhere in the code behind so that in the view i would have
#Model.Picture
How do i connect the view, controller and model so that from the view i send somehow the cssClass that i want that Picture to have when the HtmlString is returned.
You need to pass the class name to the renderField pipeline.
So if Model.Picture is a HtmlString containing the output of the renderField pipeline the class name cannot be decided dynamically in the View but need to be set in the Model (it can be discussed if this is a nice approach).
Anyway when you create your model you can pass the class name into the renderField pipeline as follows:
var renderFieldArgs = new RenderFieldArgs
{
Item = this.Item,
FieldName = "PICTURE FIELD ID"
};
TypeHelper.CopyProperties(parameters, new {#class = "img-responsive"} );
CorePipeline.Run("renderField", renderFieldArgs);
return new HtmlString(renderFieldArgs.Result.FirstPart);
This will mean that Model.Picture always will have the same class name.
Otherwise you could have a method on your Model class accepting a class name string as parameter. Something like
public HtmlString GetPictureMarkup(string className)
{
var renderFieldArgs = new RenderFieldArgs
{
Item = this.Item,
FieldName = "PICTURE FIELD ID"
};
TypeHelper.CopyProperties(parameters, new {#class = className} );
CorePipeline.Run("renderField", renderFieldArgs);
return new HtmlString(renderFieldArgs.Result.FirstPart);
}
Hope it helps.
Related
I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);
I want to display a text with 2 links inside the text in MVC view!
The text link are dynamic from server side and looks like this in controller:
Model.Message = "You have used up all your credits for this month (see your credit balance {FirstLink}). You can buy credits {SecondLink}";
In view I have something like this
#{
var messageToShow = #Model.Message;
var newText = Html.ActionLink(Model.LinkText, Model.LinkAction, Model.LinkController, Model.LinkRouteValues).ToString();
var link = Html.ActionLink(Model.SecondLinkText, Model.SecondLinkAction, Model.SecondLinkController, Model.LinkRouteValues).ToString();
}
#if (!string.IsNullOrEmpty(Model.LinkText))
{
messageToShow = messageToShow.Replace("{FirstLink}", newText);
}
#if (!string.IsNullOrEmpty(Model.SecondLinkText))
{
messageToShow = messageToShow.Replace("{SecondLink}", link);
}[![enter image description here][1]][1]
#Html.Raw(messageToShow)
This is working like it is but I have to add the class to it like this
#Html.ActionLink(Model.SecondLinkText, Model.SecondLinkAction, Model.SecondLinkController, Model.LinkRouteValues, new { #class = "link" })
When adding the ,new{ #class = "link"} I got syntax error since the closing } is badly interpreted by razor engine.
Can you help out or maybe suggest a better solution?
Thanks
In order to solve the syntax error, your view model would need to include a property for the html attributes, either
public IDictionary<string, object> LinkAttributeValues { get; set; }
if LinkRouteValues is typeof RouteValueDictionary, or
public object LinkAttributeValues { get; set; }
if LinkRouteValues is typeof object
And then you code becomes
Html.ActionLink(Model.SecondLinkText, Model.SecondLinkAction,
Model.SecondLinkController, Model.LinkRouteValues, Model.LinkAttributeValues)
Note that both your #if (!string.IsNullOrEmpty(Model....)) lines so of code are pointless since if either LinkText or SecondLinkText were null or an empty string, the previous Html.ActionLink(..) lines of code would have thrown an exception.
One option would be to simply use
<span>You have used up all your credits for this month (see your credit balance</span>
#Html.ActionLink(Model.LinkText, Model.LinkAction, Model.LinkController, Model.LinkRouteValues, new { #class = "link" })
<span>). You can buy credits</span>
#Html.ActionLink(Model.SecondLinkText, Model.SecondLinkAction, Model.SecondLinkController, Model.LinkRouteValues, new { #class = "link" })
or if you want to set the message text in the controller, you could have 2 properties, say string Message and string Message2 and use <span>#Model.Message1</span>....
You don't need to prefix an '#' before the ActionLink when you're inside a code block. Remove that and I'm sure the syntax error will disappear.
I have an MVC app where users fill in a 4-step form then go to a "confirm" screen. On the confirm screen, if they select to modify their info I use RedirectToAction to take them back to the first step view, and I pass a URL parameter "modify=true", which tells the controller to use the session object already created as opposed to creating a new object from the DB and displaying an empty form. But once they submit the form for step 1 I want to send them from my controller to the step 2 view along with the "modify=true" parameter. There doesn't seem to be a way to return a viewmodel to a view and also pass a query string parameter. How can I accomplish this?
I have considered adding a bool to the viewmodels to signify "inReview" but i use different viewmodels for each of these views and they're all pretty clean, it seems like this bool would muck things up a bit.
I have also considered adding the bool to viewbag or viewdata, but then i'd be using the submit button to pass that value and the "modify=true" parameter would drop off the URL, possibly confusing the user and definitely confusing the code.
Thanks
If you use the Html.BeginForm() helper (without parameters) it will automatically append existing query string parameters to the generated form action attribute. If you use some of the other overloads such as Html.BeginForm("SomeAction", "SomeController", FormMethod.Post) then you're gonna lose those parameters. This could be easily fixed by writing a custom helper that will take into account those parameters:
public static class HtmlHelpers
{
public static IDisposable BeginRequestForm(this HtmlHelper html, string action, string controller, FormMethod method)
{
var builder = new TagBuilder("form");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var routeValues = new RouteValueDictionary();
var query = html.ViewContext.HttpContext.Request.QueryString;
foreach (string key in query)
{
routeValues[key] = query[key];
}
builder.MergeAttribute("action", urlHelper.Action(action, controller, routeValues));
builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
html.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
return new MvcForm(html.ViewContext);
}
}
and then use in your view (after bringing it into scope of course):
#using (Html.BeginRequestForm("SomeAction", "SomeController", FormMethod.Post))
{
...
}
You can either use ViewBag or your view model. You just need to pass the value somehow to the view:
ViewBag.modify = true;
return View(model);
Then in your view:
Html.BeginForm("MyAction", "MyController", new { modify = ViewBag.modify })
I've been scouring the web for a way to do this.
I want to generate a hyperlink to an action from my controller and put it in a string. I need to be able to define the label and give it html attributes. I can get Url.Action(...) working but that method doesn't let me define the label on the link.
HtmlHelper.GenerateLink(...) looks promising but I can't find any concrete examples on how to use it.
The link should look something like this:
View
Add this property to your base controller:
protected HtmlHelper Html
{
get
{
var viewContext = new ViewContext( ControllerContext, new WebFormView( Request.CurrentExecutionFilePath ),
new ViewDataDictionary(), new TempDataDictionary(), Response.Output )
{
RouteData = ControllerContext.RouteData
};
return new HtmlHelper( viewContext, new ViewPage() );
}
}
and then call it from anywhere:
var link = Html.ActionLink( "Click Me", "action" );
try this
string str = string.Concat("View"
and then pass this in ViewData and call it in view
<%= str%>
there are a few ways to do this - here are 2:
Link name here
Html.ActionLink(article.Title,
"Login", // <-- Controller Name.
"Item", // <-- ActionMethod
new { id = "<arguments here" }, // <-- Route arguments.
null // <-- htmlArguments .. which are none. You need this value
// otherwise you call the WRONG method ...
// (refer to comments, below).
)
there are other overloads of each available
Perhaps a little more information on why you would want to do this would be a little more helpful. If you return a string that contains HTML it will by default be HTML encoded and rendered useless on the client. If you have a custom view where this will be rendered why not build the link there using #Html.ActionLink?
I guess I am trying to figure out the benefit of doing it in the controller rather than the view...
I want to render the same view after a successful action (rather than use RedirectToAction), but I need to modify the model data that is rendered to that view. The following is a contrived example that demonstrates two methods that that do not work:
[AcceptVerbs("POST")]
public ActionResult EditProduct(int id, [Bind(Include="UnitPrice, ProductName")]Product product) {
NORTHWNDEntities entities = new NORTHWNDEntities();
if (ModelState.IsValid) {
var dbProduct = entities.ProductSet.First(p => p.ProductID == id);
dbProduct.ProductName = product.ProductName;
dbProduct.UnitPrice = product.UnitPrice;
entities.SaveChanges();
}
/* Neither of these work */
product.ProductName = "This has no effect";
ViewData["ProductName"] = "This has no effect either";
return View(product);
}
Does anyone know what the correct method is for accomplishing this?
After researching this further, I have an explanation why the following code has no effect in the Action:
product.ProductName = "This has no effect";
ViewData["ProductName"] = "This has no effect either";
My View uses HTML Helpers:
<% Html.EditorFor(x => x.ProductName);
HTML Helpers uses the following order precedence when attempting lookup of the key:
ViewData.ModelState dictionary entry
Model property (if a strongly typed view. This property is a shortcut to View.ViewData.Model)
ViewData dictionary entry
For HTTP Post Actions, ModelState is always populated, so modifying the Model (product.ProductName) or ViewData directly (ViewData["ProductName"]) has no effect.
If you do need to modify ModelState directly, the syntax to do so is:
ModelState.SetModelValue("ProductName", new ValueProviderResult("Your new value", "", CultureInfo.InvariantCulture));
Or, to clear the ModelState value:
ModelState.SetModelValue("ProductName", null);
You can create an extension method to simplify the syntax:
public static class ModelStateDictionaryExtensions {
public static void SetModelValue(this ModelStateDictionary modelState, string key, object rawValue) {
modelState.SetModelValue(key, new ValueProviderResult(rawValue, String.Empty, CultureInfo.InvariantCulture));
}
}
Then you can simply write:
ModelState.SetModelValue("ProductName", "Your new value");
For more details, see Consumption of Data in MVC2 Views.
The values are stored in ModelState.
This should do what you want:
ModelState.SetModelValue("ProductName", "The new value");
I wouldn't suggest doing that though... the correct method would be to follow the PRG (Post/Redirect/Get) pattern.
HTHs,
Charles
EDIT: Updated to reflect the better was of setting the ModelState value as found by #Gary
This will trigger the model to re-evaluate under simple conditions:
ModelState.Clear();
model.Property = "new value";
TryValidateModel(model);
Perform ModelState.Clear() before you change the model.
...
ModelState.Clear()
dbProduct.ProductName = product.ProductName;
dbProduct.UnitPrice = product.UnitPrice;
...