#Html tags are not displayed in a div? - asp.net-mvc-4

I'm working on asp.Net MVC4 project, and I need to create ActionLink for each element of a list.
Here is what I have done:
<div class="display-field" id="kalamsDiv">
#foreach(Elections.Domain.Models.Kalam kalam in Model.Kalams)
{
Html.ActionLink(#kalam.Name, "Details", "Kalam", #kalam.KalamID, "");
<br />
}
</div>
And in the model of the view I have a list of "Kalam" object:
public virtual ICollection<Kalam> Kalams { get; set; }
But I'm not seeing any ActionLink, the div is empty, I can only see the line breaks : <br />.
What is the mistake I'm making?
Thanks :)

Use # before Html.ActionLink and remove them before kalam variable. Create an anonymous object for routeValues and pass null for htmlAttributes.
<div class="display-field" id="kalamsDiv">
#foreach(Elections.Domain.Models.Kalam kalam in Model.Kalams)
{
#Html.ActionLink(kalam.Name, "Details", "Kalam", new { ID = kalam.KalamID }, null);
<br />
}
</div>

Related

Position Blazor component inside user content

I have a requirement to dynamically put a Blazor component inside user-provided content. Essentially, the component is supposed to extend user-provided markup with some UI elements.
Let's say the user provides some content that can have a "greeting-container" element in it and the component should insert a greeting button inside that element.
My current solution is to call a JavaScript function to move the DOM element in OnAfterRenderAsync (full code below). It seems to work fine, but manipulating DOM elements seems to be discouraged in Blazor since it can affect the diffing algorithm. So I have a couple of questions on this:
How bad is it to move DOM elements like this? Does it cause performance issues, functional issues or some undefined behavior?
Is there a better way to achieve the same result without using JavaScript? I was considering using the RenderTreeBuilder for this, but it seems like it might not be designed for this purpose since it's recommended to use hardcoded sequence numbers, which doesn't seem possible when dealing with dynamic content not known at compilation time.
Current solution code:
Greeter.razor
#page "/greeter"
#inject IJSRuntime JSRuntime;
<div>
#((MarkupString)UserContentMarkup)
<div id="greeting">
<button #onclick="ToggleGreeting">Toggle greeting</button>
#if (isGreetingVisible) {
<p>Hello, #Name!</p>
}
</div>
</div>
#code {
[Parameter]
public string UserContentMarkup { get; set; }
[Parameter]
public string Name { get; set; }
private bool isGreetingVisible;
private void ToggleGreeting()
{
isGreetingVisible = !isGreetingVisible;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JSRuntime.InvokeVoidAsync("moveGreetingToContainer");
}
}
_Host.cshtml
window.moveGreetingToContainer = () => {
var greeting = document.getElementById("greeting");
var container = document.getElementById("greeting-container");
container.appendChild(greeting);
}
UserContentTest.razor
#page "/userContentTest"
#inject IJSRuntime JSRuntime;
<h3>Testing user content</h3>
<Greeter UserContentMarkup=#userContentMarkup Name="John"></Greeter>
#code {
private string userContentMarkup = "Some <b>HTML</b> text followed by greeting <div id='greeting-container'></div> and <i>more</i> text";
}
Expected result (after clicking "Toggle greeting"):
<div>
Some <b>HTML</b> text followed by greeting
<div id="greeting-container">
<div id="greeting">
<button>Toggle greeting</button>
<p>Hello, John!</p>
</div>
</div> and <i>more</i> text
</div>
Great question - and yes, using JS to move the dom elements is very bad as Blazor doesn't see the change you made to the dom.
What you can do is switch over to using a RenderFragment and more specifically RenderFragment<RenderFragment> which is markup that will be supplied with more markup as a parameter.
On the second line, I am invoking the UserContentMarkup method (which is a RenderFragment<RenderFragment>) and passing in the <div id=greeting> content as the context parameter.
Note: It is wrapped in a <text> element which is actually a way to embed HTML in C# in a Razor file. It does not render a <text> element to the page.
Greeter.razor
<div>
#UserContentMarkup(
#<text>
<div id="greeting">
<button #onclick="ToggleGreeting">Toggle greeting</button>
#if (isGreetingVisible) {
<p>Hello, #Name!</p>
}
</div>
</text>
)
</div>
#code {
[Parameter]
public RenderFragment<RenderFragment> UserContentMarkup { get; set; }
[Parameter]
public string Name { get; set; }
private bool isGreetingVisible;
private void ToggleGreeting()
{
isGreetingVisible = !isGreetingVisible;
}
}
UserContentTest.razor
Here you can see two ways to consume Greeter - using markup in the page, or using a code method.
<h3>Testing user content</h3>
#* Using markup to supply user content - #context is where the button goes *#
<Greeter Name="John">
<UserContentMarkup>
Some <b>HTML</b> text followed by greeting
<div id='greeting-container'>#context</div> and <i>more</i> text
</UserContentMarkup>
</Greeter>
#* Using a method to supply the user content - #context is where the button goes *#
<Greeter Name="John" UserContentMarkup=#userContent />
This code method can be confusing - it is a RenderFragment<RenderFragment> which means it has to be a method that accepts a RenderFragment as its only parameter, and returns a RenderFragment - the RenderFragment being returned in this case is markup wrapped in <text> to make it clear it is markup.
#code
{
RenderFragment<RenderFragment> userContent
=> context => #<text>Some stuff #context more stuff</text>;
}
Try it out here : https://blazorrepl.com/repl/QuPPaMEu34yA5KSl40

How update view depending on user selection on Asp.Net core

I want to be able to display a form which changes depending on the value of a select on Dot.Net Core. I've seen many things like dynamic forms, View Components, razor and partials and also there is a lot of info out there but very confusing. Any info about the proper way to do what I want would be very appreciated.
I Have Categories>SubCategories>>Products
An order can be for one Category>>SubCategory only. So I want to display a select and depending on what category the user selects for the new Order, the products that I have to display. So I dont want to pick the user selection and go back to the controller and back to the view again and so on. I want to be able to dynamically display data according to the user selection.
Here an extract of the code just to briefly figure out what Im doing..I am not pasting the classes like Products,etc..
public class OrderCreateViewModel
{
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<Branch> Branches { get; set; }
public int BranchId { get; set; }
public int CategoryId { get; set; }
}
Controller :
[HttpGet]
public IActionResult Create()
{
//Create vm
IEnumerable<Branch> branchList = _branchDataService.GetAll();
IEnumerable<Category> categoryList = _categoryDataService.GetAll();
OrderCreateViewModel vm = new OrderCreateViewModel
{
Categories = categoryList,
Branches = branchList
};
return View(vm);
}
View:
#model OrderCreateViewModel
<p>Create New Order </p>
<form asp-controller="Order" asp-action="Create" method="post">
<div class="form-group">
<label >Select Category</label>
<select class="form-control col-md-2" asp-for="CategoryId"
asp-items="#(new SelectList(Model.Categories ,"CategoryId","Name"))">
</select>
</div>
<div class="form-group">
<label>Select Branch</label>
<select class="form-control col-md-2" asp-for="BranchId"
asp-items="#(new SelectList(Model.Branches,"BranchId","Name"))">
</select>
</div>
<div >
<input type="submit" value="Save" />
</div>
</form>
Im just filling the select on the viewside and depending on what the user picks, the products I want to display. I am not passing the product list yet because I don't know where the "filter" for that particular Category takes place.
Hope you understand the idea of what i need :)
You've got two options here:
# 1 Use a Partial View And AJAX to get your data
Go have a look at the link below, it describes exactly what you want to achieve.
Populating Dropdown using partial view
# 2 Use Javascript to populate your second select:
First off you need to persist your data when the page loads
At the start of your view, add this:
#{
<text>
<script>
var data = "#Newtonsoft.Json.JsonConvert.SerializeObject(Model)";
</script>
</text>
}
Next use your on change event on the branch selector:
At the bottom of your view, do the following in the page ready event:
<script>
(function ()
{
var sltBranch = document.getElementsByName("BranchId")[0];
var sltCat = document.getElementsByName("CategoryId")[0]
sltCat.onchange = function () {
var dataAsObj = JSON.parse(data);
var branches = "";
for (i = 0; i < dataAsObj.Branches.length; i++)
{
if (dataAsObj.Branches[i].CategoryId == sltCat.value)
{
branches += "<option value='" + dataAsObj.Branches[i].BranchId + "'>" + dataAsObj.Branches[i].BranchName + "</option>"; //Whatever The branch name may be
}
}
sltBranch.innerHTML = branches;
}
}
)(document, window);
</script>
I would however advise you to follow option 1 as it is a lot more future proof strategy. This would mean that you need to change your view model etc, but if you plan on making extensive use of this strategy you need to make it more robust with something like option 1.
Good luck anyhow - happy coding.

kendo editor not responding after multiple requests to the same page in IE

I have a very weird bug. I have a page on MVC that displays two editors and gets passed a model with the value for both editors. The model is as follows:
public class BulletinsModel
{
[AllowHtml]
[Display(Name = "Some Bulletin")]
public string SomeBulletin { get; set; }
[AllowHtml]
[Display(Name = "Other Bulletin")]
public string OtherBulletin { get; set; }
}
I then, defined a view which receives this view model and maps it to two kendo editors.There is also some javascript code to make a post to update the information.
#model BulletinsModel
<div id="settings">
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(m => m.SomeBulletin, new { #class = "col-md-6 text-left" })
#(Html.Kendo().EditorFor(m => m.SomeBulletin).Encode(false).Name("Some_Bulletin"))
#Html.LabelFor(m => m.OtherBulletin, new { #class = "col-md-6 text-left" })
#(Html.Kendo().EditorFor(m => m.OtherBulletin).Encode(false).Name("Other_Bulletin"))
</div>
</div>
</div>
My code for my action method that renders this view is as follows (nothing fancy):
[HttpGet]
public PartialViewResult Index()
{
ViewBag.ActiveSectionName = "Bulletins";
var bulletinModel = GetBulletinsModel();
return PartialView("_Bulletins",bulletinModel);
}
However, my issue is that after hitting the Index action a couple of times, the editors become non responsive and I cannot edit the information on them. This only happens on IE, as I have not been able to replicate the issue in other browsers.
EDIT: I have just noticed that the editor is frozen. In order to be able to edit what's inside of the editor I need to click on any option of the toolbar to make it responsive once again. Why is that?
Turns out that the issue is happening with IE as detailed in this post:
Adding, removing, adding editor -> all editors on page become read only in IE. The problem is with the iframes inside the editor. I was loading my page with an Ajax request to which I had to add the following code before making the request to make it work.
function unloadEditor($editor) {
if ($editor.length > 0) {
$editor.data('kendoEditor').wrapper.find("iframe").remove();
$editor.data('kendoEditor').destroy();
}
}
unloadEditor($('#myEditor'));

Correct way to show an image that comes from a controller

I have a controller that loads an image file:
//
// GET: /MyController/Image/id
public ActionResult Image(string id)
{
var dir = #"C:\Temp\Images";
var path = Path.Combine(dir, id + ".png");
return File(path, "image/png");
}
In a separate view from action MyController/Page/id I have hastily hashed up some html to show the image:
<img src="../image/#Model.Name" />
And then also with a link:
<a href="../image/#Model.Name">
<img src="../image/#Model.Name" />
</a>
It works, but I am looking for a better way using #Html so resharper can help with navigation and validation, for the two above. I've tried this:
#Html.RenderAction("Image", "MyController", new { id = Model.Name })
But this doesn't compile, it says Expression must return a value to render.
I think you're just looking for Url.Action(), which returns an URL to an Action:
<img src="#Url.Action("Image", "ImageController", new { id = Model.ImageID })" />
You can use Url.Action() in the src attribute
<img src='#Url.Action("Image", "ImageController", new { imageId = Model.Id })' alt='MyImage' />

How to access data in partialview loaded using Ajax

I'm currently building a website where I have to update two separate targets from a single Ajax.BeginForm. I got it working by using an additional container to container the two separate targets. As in:
Original Form
#model Mod1
#using (Ajax.BeginForm("LoadData", new AjaxOptions{UpdateTargetID = "Div1"}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
<div id="Div1"></div>
// ...
<input type="submit" value="GO!" />
}
Code File
public ActionResult LoadData(int sel1)
{
// loading data from database
return PartialView(mod1);
}
Partial View
#model Mod2
<select id="sel2" name="sel2">
#foreach (var item in Model.SelectItems)
{
<option value="#item.Value">#item.Text</option>
}
</select>
#foreach (var item in Model.CheckBoxItems)
{
<label>#item.Text<input type="checkbox" id="chk1" name="chk1" value="#item.Value"></label>
}
For the processing method, I have tried:
public ProcessData(Mod1 mod1, string[] chk1, int sel2)
However I am unable to retrieve the values for either chk1 or sel2 upon form submission. examination of chk1 and sel2 in Debug mode, chk1 is null while sel2 is 0 (no such value in the select options). Can anyone please offer some insight into the reason for this and also how I can go about solving it. Thank you in advance.
If I understand you correctly you can do what you want y having two submit buttons on the same form, each calling a separate action method. That way each submit button will have access to all the fields in the form. For a detailed explanation of how you can do that see my answer here:
How to use ajax link instead of submit button for form?
Edit
In response to comment: the action method LoadData should return a partial view that contains the other two controls and have the whole begin form included in it:
#using (Ajax.BeginForm("LoadData", new AjaxOptions{
UpdateTargetID = "Div1",
InsertionMode = InsertionMode.Replace
}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
<div id="Div1">
</div>
<div id="Div2">
</div>
Move this to another partial view:
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
// ...
<input type="submit" value="GO!" />
}