Generate the Tree structure in the Asp.Net Mvc - asp.net

I create a tree structure in Asp.Net MVC Core along with the possibility of operating on this tree, i.e. adding branches, leaves, editing, etc. And I have a question whether this implementation of generating this tree is ok? Are there better ways to do this?
Branch generation method called from the controller:
public ActionResult GenerateNodes(int? parentId)
{
var listOfNodes = _context.NodeEntitie.Where(x => x.ParentNodeId == parentId);
return PartialView(listOfNodes);
}
Generated view:
#model IEnumerable<Tree10.Models.Node>
#if (Model.Any())
{
foreach (var element in Model)
{
<li class="listElement" onclick="Select(this)" id="#element.Id">+#element.Name</li>
if (element.HasChildrens())
{
<ol>
#Html.Action("GenerateNodes", "Node", new { parentId = element.Id })
#Html.Action("GenerateLeafs", "Leaf", new { parentId = element.Id })
</ol>
}
}
}
The method of generating leaves from the controller:
public ActionResult GenerateLeafs(int parentId)
{
var listOfLeafs = _context.LeafEntities.Where(x => x.ParentId == parentId);
return PartialView(listOfLeafs);
}
Generated view:
#model IEnumerable<Tree10.Models.Leaf>
#if (Model.Any())
{
foreach (var element in Model)
{
<li class="listElement" onclick="Select(this)" id="l #element.Id">#element.Name</li>
}
}

ehh ok it was stupid. I did a better solution
#{
void ShowTree(int? parentId)
{
var newNodes = Model.Where(x => x.ParentNodeId == parentId);
<ul>
#foreach(var element in newNodes)
{
<li>#element.Name</li>
if (element.SubNodes.Any())
{
ShowTree(element.ParentNodeId);
}
}
</ul>
}
ShowTree(null);
}

Related

I cant using Html.RenderAction in .cshtml

FRONTEND
#{
<div>
#Html.RenderAction("UrunOzellikTipWidget", "Admin");
#Html.RenderAction("UrunOzellikDegerWidget", "Admin");
</div>
}
BACKEND
public ActionResult UrunOzellikEkle()
{
return View(Context.Baglanti.Urun.ToList());
}
public PartialViewResult UrunOzellikTipWidged(int? katID)
{
if (katID != null)
{
var data = Context.Baglanti.OzellikTip
.Where(x => x.KategoriID == katID)
.ToList();
return PartialView(data);
}
else
{
var data = Context.Baglanti.OzellikTip.ToList();
return PartialView(data);
}
}
public PartialViewResult UrunOzellikDegerWidget(int? tipID)
{
if (tipID != null)
{
var data = Context.Baglanti.OzellikDeger
.Where(x => x.OzellikTipID == tipID)
.ToList();
return PartialView(data);
}
else
{
var data = Context.Baglanti.OzellikDeger
.ToList();
return PartialView(data);
}
}
**ERROR CODE:Severity Code Description Project File Line Suppression State Error CS0029 Cannot implicitly convert type 'void' to 'object'
**
#Html.RenderAction renders the result and, instead of returns it as string, writes it directly to the response and returns Void. So you will have to use it inside a C# block, denoted as #{ }, and end it with a semicolon at the end, just like how you invoke/call a void function.
So your front-end needs to be changed to:
<div>
#{
Html.RenderAction("UrunOzellikTipWidget", "Admin");
Html.RenderAction("UrunOzellikDegerWidget", "Admin");
}
</div>
The other's answer (removing the redundant ampersands) won't work because those 2 RenderAction are wrapped inside a <div />. If you remove the ampersand, they're treated as just regular HTML. It will output as:
Revise your code in FRONTEND to the following (remove the redundant ampersands from both Html.RenderAction statements).
<div>
#{ Html.RenderAction("UrunOzellikTipWidget", "Admin"); }
#{ Html.RenderAction("UrunOzellikDegerWidget", "Admin") };
</div>

Recursive ViewComponent causes infinite loop

I'm trying to make a recursive category tree using a ViewComponent, but I seem to get stuck in an infinite loop, and end up with HTTP Error 502.3 - Bad Gateway after a minute or two.
My thinking has been like this:
In the main View, the component is invoked with an empty List<ViewModelProductCategory> as a parameter, to indicate that I want to get the root categories first.
The ViewComponent query the database for categories with ParentId == null and returns it to Default.cshtml.
In Default.cshtml, the component is invoked again, but this time with a list of children categories as parameter.
I'm invoking the ViewComponent like this:
#await Component.InvokeAsync("SelectCategories",
new List<MyStore.Models.ViewModels.ViewModelProductCategory> { })
The ViewComponent class looks like this:
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyStore.Models;
using MyStore.Models.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyStore.Areas.Admin.ViewComponents
{
public class SelectCategoriesViewComponent : ViewComponent
{
private readonly MyStoreContext _context;
private readonly IMapper _mapper;
public SelectCategoriesViewComponent(MyStoreContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<IViewComponentResult> InvokeAsync(List<ViewModelProductCategory> catList)
{
List<ViewModelProductCategory> VM = await GetCategoriesAsync(catList);
return View(VM);
}
private async Task<List<ViewModelProductCategory>> GetCategoriesAsync(List<ViewModelProductCategory> catList)
{
List<ViewModelProductCategory> VM = new List<ViewModelProductCategory>();
if (catList.Count() == 0)
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories
.Where(x => x.ParentId == null)
.ToListAsync());
}
else
{
VM = catList;
}
return VM;
}
}
}
The ViewComponent's Default.cshtml looks like this:
#model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul style="list-style:none;padding-left:0px;">
#if (Model != null)
{
foreach (var item in Model)
{
<li style="margin-top:4px;padding:0px;">
#Html.HiddenFor(m => item.Id)
#Html.CheckBoxFor(m => item.Checked)
#Html.LabelFor(m => item.Id, item.Title)
<ul>
#await Component.InvokeAsync("SelectCategories", item.Children)
</ul>
</li>
}
}
</ul>
Where is/are the error/s?
Edit
The comment from Tarek.Eladly made me realize I had a bad logic flaw in the GetCategoriesAsync-method. I have made these changes to my code, and now it works:
Invoking the ViewComponent first time:
#await Component.InvokeAsync("SelectCategories",
new
{
root = true,
catList = new List<MyStore.Models.ViewModels.ViewModelProductCategory>()
})
The ViewComponent-methods:
public async Task<IViewComponentResult> InvokeAsync(bool root, List<ViewModelProductCategory> catList)
{
List<ViewModelProductCategory> VM = await GetCategoriesAsync(root, catList);
return View(VM);
}
private async Task<List<ViewModelProductCategory>> GetCategoriesAsync(bool root, List<ViewModelProductCategory> catList)
{
List<ViewModelProductCategory> VM = new List<ViewModelProductCategory>();
if (root)
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories
.Where(x => x.ParentId == null)
.ToListAsync());
}
else
{
VM = catList;
}
return VM;
}
Invoking the ViewComponent in Default.cshtml:
#await Component.InvokeAsync("SelectCategories",
new
{
root = false,
catList = item.Children
})
:)
if i am right just take a look at (.Where(x => x.ParentId == null)) in GetCategoriesAsync you always get the root category and never check anything else.

Checkbox retains state after postback

how come my model retains the state? why is it like this?
Isn't it supposed to be refreshed since i am trying to send a brand new instance of the model?
Here is an example:
If i delete anything on the middle, after postback, the checkbox will still be checked
Here are my codes:
Here is my postback code:
[HttpPost]
public ActionResult Index(IEnumerable<Employee> emp)
{
EmployeeContext test = new EmployeeContext();
if (emp.Count(x => x.selected) == 0)
{
return View(test.Employees);
}
else
{
foreach (Employee del in emp)
{
if (del.selected)
{
Employee dummy = test.Employees.Single(x => x.id == del.id);
test.Employees.Remove(dummy);
test.SaveChanges();
}
}
return View(test.Employees);
}
}
What should i do to remove that state? i read something about ModelState.Remove so my idea is that use a loop to clear each of my checkboxes but i am not sure if that will be good when it comes to performance. What should i do?
Fixed by changing them to Redirect:
[HttpPost]
public ActionResult Index(IEnumerable<Employee> emp)
{
EmployeeContext test = new EmployeeContext();
if (emp.Count(x => x.selected) == 0)
{
return RedirectToAction("Index");
}
else
{
foreach (Employee del in emp)
{
if (del.selected)
{
Employee dummy = test.Employees.Single(x => x.id == del.id);
test.Employees.Remove(dummy);
test.SaveChanges();
}
}
return RedirectToAction("Index");
}
}
Full Credits to : Stephen Muecke for the PRG pattern.
if you didn't want model state keep it's value in the view, you don't need to pass the model into the view
just change this
return View(test.Employees);
into
return View();

when returning a view from Child Ajax Form it redirects to child forms url instead of refreshing the child ajax form only

Child ajax form redirects to the URL http://www.AppName/Email/Email/AttachmentList instead of refreshing the AttachmentList view only.
Here is the child form (View Name: Attachment List)
#foreach (var attachment in Model.Attachments)
{
<li>
#using (Ajax.BeginForm("RemoveAttachment", "Email", new { area = "Email" }, new FSIAjaxOptions { UpdateTargetId = "uploadedFilesSection" }, new { #id = "fromEditRole" + attachment.IdString }))
{
#Html.HiddenFor(model => attachment.IdString)
<span><a `enter code here`href="#Url.Content("~/File/GetFile")?folder=EmailAttachments&file=#(attachment.AttachName)" target="_blank">#attachment.OriginalName</a></span>
<button type="submit" class="imageButton removeButton" title="Remove"></button>
}
</li>
}
Here is the Controller Action
[Authorize]
[HttpPost]
public ActionResult RemoveAttachment(FormCollection fm)
{
EmailViewModel model = SessionData.GetCurrentObject<EmailViewModel>();
if ((fm.AllKeys.Contains("attachment.IdString")) && !string.IsNullOrEmpty(fm["attachment.IdString"]))
{
var attachment = model.Attachments.Where(x => x.IdString == fm["attachment.IdString"]).FirstOrDefault();
if (attachment != null)
{
FileHelper.RemoveFile("EmailAttachments","",attachment.AttachName);
Directory.Delete(WebConfig.SharedFileContainer + #"\EmailAttachments\" + attachment.IdString, true);
model.Attachments.Remove(attachment);
}
SessionData.SaveCurrentObject<EmailViewModel>(model);
}
return View("AttachmentList", "Email", new { area = "Email" });
}
public ActionResult AttachmentList()
{
EmailViewModel email = SessionData.GetCurrentObject<EmailViewModel>();
if (Request.IsAjaxRequest())
{
return PartialView(email);
}
else
{
return View("AttachmentList", "~/Views/Shared/Wrapper.cshtml", email);
}
}
Make sure you have the ajax js file referenced.

Translate to razor Syntax from MVC 2.0 code

I am trying to convert the popular asp.net MVC 2.0 solr.net sample app code to Razor syntax. I am not able to understand the last line ... Kindly help
<% Html.Repeat(new[] { 5, 10, 20 }, ps => { %>
<% if (ps == Model.Search.PageSize) { %>
<span><%= ps%></span>
<% } else { %>
#ps
<% } %>
<% }, () => { %> | <% }); %>
[update]
Source for Html.Repeat extension
HtmlHelperRepeatExtensions.cs
For this to work you will have to modify the Html.Repeat extension method to take advantage of Templated Razor Delegates as illustrated by Phil Haack. And then:
#{Html.Repeat(
new[] { 5, 10, 20 },
#<text>
#if (item == Model.Search.PageSize)
{
<span>#item</span>
}
else
{
<a href="#Url.SetParameters(new { pagesize = item, page = 1 })">
#item
</a>
}
</text>,
#<text>|</text>
);}
UPDATE:
According to your updated question you seem to be using a custom HTML helper but as I stated in my answer you need to updated this helper to use the Templated Razor Delegates syntax if you want it to work. Here's for example how it might look:
public static class HtmlHelperRepeatExtensions
{
public static void Repeat<T>(
this HtmlHelper html,
IEnumerable<T> items,
Func<T, HelperResult> render,
Func<dynamic, HelperResult> separator
)
{
bool first = true;
foreach (var item in items)
{
if (first)
{
first = false;
}
else
{
separator(item).WriteTo(html.ViewContext.Writer);
}
render(item).WriteTo(html.ViewContext.Writer);
}
}
}
or if you want to have the helper method directly return a HelperResult so that you don't need to use the brackets when calling it:
public static class HtmlHelperRepeatExtensions
{
public static HelperResult Repeat<T>(
this HtmlHelper html,
IEnumerable<T> items,
Func<T, HelperResult> render,
Func<dynamic, HelperResult> separator
)
{
return new HelperResult(writer =>
{
bool first = true;
foreach (var item in items)
{
if (first)
first = false;
else
separator(item).WriteTo(writer);
render(item).WriteTo(writer);
}
});
}
}
and then inside your view:
#Html.Repeat(
new[] { 5, 10, 20 },
#<text>
#if (item == Model.Search.PageSize)
{
<span>#item</span>
}
else
{
<a href="#Url.SetParameters(new { pagesize = item, page = 1 })">
#item
</a>
}
</text>,
#<text>|</text>
)
Possibly something like:
}, () => { <text>|</text> });

Resources