Is it possible to have separate uihint templates, one for display and the other for edit? If yes, how to achieve this?
For ex:
public class PartyRole
{
[Required]
[UIHint("DropDownList")]
public int PartyRoleTypeId { get; set; }
}
I am using EditForModel() and DisplayForModel().
In edit, I am showing the property as drop down. But in display I should show it as simple text.
Is it possible to have separate uihint templates, one for display and the other for edit?
No, this is not possible. It should be the same name. The EditorTemplate is located in ~/Views/Shared/EditorTemplates and the display template in ~/Views/Shared/DisplayTemplates. If for some reason you absolutely needed to have different names for the templates you could pass them as parameter to the helpers instead of using UIHint:
#Html.EditorFor(x => x.Foo, "SomeEditorTemplate")
#Html.DisplayFor(x => x.Foo, "SomeDisplayTemplate")
Related
For POCOs with lots of fields I find it really convenient to have a simple form/component binding structure like this
< input type="text" bind="#Person.FirstName" />
< input type="text" bind="#Person.LastName" />
< input type="text" bind="#Person.Address" />
Where I then have save or create new buttons/events to deal with object at a time situations. In other words the entire object is bound to an editable component making life super easy to bring objects from the DB for editing and back into the db.
What I find annoying are situations where I have to both display an object's details but make sure parts of it are locked/un-editable based on certain conditions. Let's say I can't change LastName for some business logic reason. This:
< input type="text" bind="#Person.LastName" disabled="#somecondition" />
is unsafe as users can potentially inspect the page, modifying the disabled flag and still cause the two way binding to be enabled and changes overwritten in the existing save event. The workarounds I find are annoying e.g., you can use if code blocks to switch between textboxes and plain-text binding, or you just do all the business logic in the event which introduced more logic for error reporting.
Here is a compromise that I think "works":
if(some_protective_condition) { < input type="text" bind="#Person.Address" /> }
else { < span>#Person.Addressv< /span>}
If I understand correctly most of these workarounds essentially play with the DOM or some aspect of visibility ensuring the editable control does not get rendered. What is the coding pattern would you guys use for these situations of interface level locking?
EDIT/TLDR: Looking for the best/safe least code pattern to maintain two-way binding, display a value and make it editable/non-editable in certain situations.
Suggestions welcome as I am trying to build good solid long term habits.
Tried several techniques. Looking for the best option if I have missed something.
I think what your looking for is InputBase. Creating a component that inherits from InputBase is going to give you access to additional features like validation styles (#CssClass) on top of two-way binding. You can use the #attributes on the input inside your component to add readonly and disabled attributes according to your POCO.
For InputBase have a peek at my answer here. Disabling the input by adding #attributes="GetAttributes()" to the html input.
[Parameter] public bool IsReadOnly { get; set; } = false;
[Parameter] public bool IsDisabled { get; set; } = false;
Dictionary<string,object> GetAttributes()
{
// Required should be handled by the DataAnnotation.
var dict = new Dictionary<string, object>();
if (IsReadOnly)
{
dict.Add("readonly", true);
}
if (IsDisabled)
{
dict.Add("disabled", true);
}
return dict;
}
I think the 'Blazor Way' to integrate business rules into your views is to use AuthorizeView...
<AuthorizeView Policy="CanEditAddress">
<Authorized>
< input type="text" bind="#Person.Address" />
</Authorized>
<NotAuthorized>
< span>#Person.Addressv< /span>
</NotAuthorized>
</AuthorizeView>
The docs show how to populate authorization policies using cascading parameters...
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-7.0#expose-the-authentication-state-as-a-cascading-parameter
If your rules are not really 'business rules' but are 'presentation rules' (like your forms have an edit mode vs a display mode) then the above pattern is still a good way to go. That is, I would create a class named 'PresentationView' that is the equivalent of AuthorizationView but for presentation rules.
I am binding a model to view.
public class Validation
{
[Display("Please enter in <h5>day(s)</h5>")]
public string Message { get; set; }
}
Here Message property will be set as Please enter in <h5>day(s)</h5>.
In view am binding model property to LabelFor.
#Html.LabelFor(m => m.Message)
Expecting output as
Please enter in day(s)
But actual output is
Please enter in <h5>day(s)</h5>
I want part of the string to vary in size and cosmetics, so applying CSS to the entire label is not what I'm looking for.
Your display string "<h5>some text</h5>" will be rendered as text, not HTML. MVC will encode the < > characters to < >, causing them to be displayed as-is.
You shouldn't want to do this like this anyway.
The proper way would be to apply the display string to contain the text you want:
[Display("some text")]
Then create a CSS class:
label.fancyLabel {
/* your style here that makes a label look like a h5 */
}
Then apply that class to the label (from .NET MVC - How to assign a class to Html.LabelFor?):
#Html.LabelFor(m => m.Message, new { #class= "fancyLabel" })
As for your edit, if you want to just render the Display attribute's text as literal HTML, you'll have to:
Introduce your own HTML helper.
In there, write a label (and make sure to set the for= attribute).
Read the DisplayAttribute's value.
#Html.Raw() that value as the label text.
Use #Html.YourLabelHelper(m => m.Message) to render it.
But using HTML on your models like that isn't really advisable. Views and templates should decide how to render a model, not the other way around.
I have a view "Register Application", containing 20 fields. The user registers. After user registered I have an option for user to update components that were submitted.
for example: view Register has sections "Contact Information" , "Address Information" ...etc currently have 5 sections.
so in the Register view I create a viewmodel #model ViewModels.RegisterVMwith all the fields.
and for edit contact information, I have its own #model ViewModels.ContactInformationVM
Now, here is my question since both views will have the same markup code I decided to create a partial view for Contact Information so I can reuse the markup code and will be able to manage it in one place instead of two places.
So in the Register view
#model ViewModels.RegisterVM
.......
#Html.Partial("~/Views/Shared/widget/_ContactInformation.cshtml", #Model)
and in the Contact Information view I want to reuse this partial view
#model ViewModels.ContactInformationVM`
#Html.Partial("~/Views/Shared/widget/_ContactInformation.cshtml", #Model)
Both views have its own viewmodel and the partial view will only be able to accept one viewmodel
No idea what viewmodel I should declare in the partial view
I know I can just copy the code from the partial view and place in the Register view and in the Contact Information view and it would work and solve the issue.. but was wondering if there a better approach to avoid having same code in multiple files.
I hope it makes sense what I am asking. Thanks for reading.
What you want to do here is create a composite view model that has each section as a separate property. For example:
public class RegisterVM
{
public ContactInformationVM ContactInformation { get; set; }
public AddressInformationVM AddressInformation { get; set; }
...
}
Then in your partial(s), you reference the sub-model:
_ContactInformation.cshtml
#model ViewModels.ContactInformationVM
<!-- contact info fields here, for example: -->
#Html.EditorFor(m => m.FirstName)
...
Then, in your register view, you use RegisterVM as your model and load the partials for each section:
#model ViewModels.RegisterVM
#Html.Partial("_ContactInformation", Model.ContactInformation)
#Html.Partial("_AddressInformation", Model.AddressInformation)
...
Now, you can reuse these components at will.
How do I post from one controller into another view?
I have a Review model and a Product model. The Review form is displayed in the Product view through a widget, but how do I submit the form itself? Right now, it doesn't do anything. I can submit through review/create, but not through the Product View.
Or am i suppose to do the post in the widget?
You can achieve it if you put code like below on components/ReviewWidget.php . I supposed you have Review as model and its respective controller and views file on default locations.
<?php
class ReviewWidget extends CWidget{
public function init() {
return parent::init();
}
public function run(){
$model = new Review;
if (isset($_POST['Review'])) {
$model->attributes = $_POST['Review'];
$model->save();
}
$this->renderFile(Yii::getPathOfAlias('application.views.review'). '/_form.php',array(
'model' => $model,
));
}
}
Then, call above widget on any where on view like below ,
<?php $this->widget('ReviewWidget'); ?>
It will handle item creation only. You have to create code to item update by yourself.
In your controller action you must use function renderPartial
$this->renderPartial('//views/reviw/_form',array('data' => $data ) );
First argument of this function is used to determine which view to use:
absolute view within a module: the view name starts with a single slash '/'. In this case, the view will be searched for under the
currently active module's view path. If there is no active module,
the view will be searched for under the application's view path.
absolute view within the application: the view name starts with double slashes '//'. In this case, the view will be searched for
under the application's view path. This syntax has been available
since version 1.1.3.
aliased view: the view name contains dots and refers to a path alias. The view file is determined by calling
YiiBase::getPathOfAlias(). Note that aliased views cannot be themed
because they can refer to a view file located at arbitrary places.
relative view: otherwise. Relative views will be searched for under the currently active controller's view path.
Also you can use this function in your views. But the most convenient way to reuse views is to create widgets.
How to set the Label Text which is available on _Layout.cstml from different Controller.
For Example
If I want to navigate from Dashboard to ProductList Page. Then My Label Text on _Layout Page should be "Product List". which I am setting from ProductList Controller Index Method.
Thanks in advance.
Use the ViewBag.xyz in _Layout.cshtml. Pass the viewdata from controller action and it will be shown on your html page.
Note:- Please confirm me if you means something else. I will update my answer if this doesn't means what you looking for.
it's look like you want to set the ViewBag.Xyz different different for every controller.
I recommanded you to use ActionFilterAttribute to make it work. The code will something like this.
public class DashboardCustomData : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.xyz = "This is my Dashboard page";
base.OnActionExecuting(filterContext);
}
}
Now put this Actionfilter to every controller. You need to use different different ActionFilter for every controller which have different title. After doing this you never need to set it manually from every controller.
If you don't like to write the multiple filter then try this one.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controllerName = filterContext.RouteData.Values["Controller"].ToString();
string ActionName = filterContext.RouteData.Values["Action"].ToString();
if (controllerName == "Dashboard")
{
filterContext.Controller.ViewBag.xyz = "This is my Dashboard page";
}
else
{
}
base.OnActionExecuting(filterContext);
}
You make BaseViewModel class with that prop, set _Layout's model as this type and derive other models from it. Now you can access that prop from any derived model
You use ViewBag or ViewData
You make a #section in Layout view and populate it within Controller's views
If you need Layout's fields in many controllers then usage of #1 looks preferred.
Why does not use this simple approach:
#{
ViewBag.Name = "...";
Layout = "_Layout.cstml";
}
And in your layout set your lable text with #ViewBag.Name
Layout
<label>#ViewBag.Label</label>
Then just assign a value to ViewBag.Label in your controllers, and it will automatically change.
public ActionResult OtherPage()
{
ViewBag.Label = "Other Label";
}
Solution you have suggested works when I am on the same Controllers View but here I want to Change the Text of Controller on Master Page.
In Asp.net We use Master Page Reference on Child Page then we can
easily Change the Text, but how do i do same in MVC... Hope you
understood the scenario. –
If you look at what is generated when you start a new project with the "Internet Template" in MVC 4, you have a way to do it already working for you:
In the element of _Layout, look for <title>#ViewBag.Title - My ASP.NET MVC Application</title>, then in all the different views, you have something like this:
#{
ViewBag.Title = "Contact";
}
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
All you need to do is to move/duplicate ViewBag.Title out of the HEAD intot the BODY, or create your own ViewBag variable, and set it's values from your different views. Obviously you could just as well set the value from the controller, but I don't see the need, as this really has to do exclusively with the user interface, which is the Views job.
Finally, if I may, don't try to duplicate in MVC what you're used to with ASP.NET. The paradigms are way too differents, chances are that you are going to waste time while there might be an easy MVC way to achieve the same thing.
It seems to me this is just an example of it.....