I want to include data (fetching from a database) in the _layout file of my asp.net mvc core project.
Situation:
_Layout page
#if (SignInManager.IsSignedIn(User))
{
Html.Action("Modules", "Layout")
}
Controllers/LayoutController.cs
using Microsoft.AspNetCore.Mvc;
namespace project.Controllers
{
public class LayoutController : Controller
{
...
public ActionResult Modules()
{
///Return all the modules
return PartialView("_Modules", moduleAccess.ToList());
}
}
}
Views/Shared/_Modules.cshtml
#model IEnumerable<project.Models.Module>
<div class="two wide column">
<div class="ui menu" id="modules">
#foreach (var item in Model)
{
<a class="item">
#Html.DisplayFor(modelItem => item.Name)
</a>
}
</div>
When going the webpage I get the following error:
'IHtmlHelper<dynamic>' does not contain a definition for 'Action' and the best extension method overload 'UrlHelperExtensions.Action(IUrlHelper, string, object)' requires a receiver of type 'IUrlHelper'
What am I doing wrong? How can I get the data in the layout page?
In ASP.NET Core instead of Html.Action use View Components: #await Component.InvoceAsync.
You can still use #await Html.RenderPariantAsync and pass there some data from the model if you want.
Solution with view components
ViewComponents/ModuleListViewComponent.cs
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace ViewComponents
{
public class ModuleListViewComponent : ViewComponent
{
...
public async Task<IViewComponentResult> InvokeAsync()
{
return View(moduleAccess.ToList());
}
}
}
Views/Shared/Components/ModuleList/Default.cshtml
#model IEnumerable<project.Models.AdminModels.Module>
<div class="two wide column">
<div class="ui left vertical labeled icon menu stackable" id="modules">
#foreach (var module in Model)
{
<a class="item">
#module.Name
</a>
}
</div>
</div>
Views/Shared/_Layout.cshtml
#if (SignInManager.IsSignedIn(User))
{
#await Component.InvokeAsync("ModuleList")
}
Related
My angular component has a tree and several nodes. When I double click on a node the click event runs a web api and retrieves data for an id that will be used to create a dynamic form using npm package: #rxweb/reactive-dynamic-forms. Once the data request is 'completed' a button appears and when clicked it opens the form with appropriate fields for the id selected. I would like to eliminate the need for this secondary button click. I've tried several suggestions but just cannot get anything to work.
I'm using Infragistics controls and bootstrap for the form.
html:
<div class="column-layout my-pane-layout">
<div *ngIf = "isShowFormButton" >
<button #open igxButton="raised" igxRipple="white" (click)="form.open()">Run</button>
<igx-dialog #form [closeOnOutsideSelect]="true" >
<igx-dialog-title>
<div class="dialog-container">
<igx-icon>vpn_key</igx-icon>
<div class="dialog-title">Form</div>
</div>
</igx-dialog-title>
<form class="input-group-form" [formGroup]="dynamicForm.formGroup" (ngSubmit)="onSubmit()">
<div class="container">
<div class="controls" viewMode="horizontal" [rxwebDynamicForm]="dynamicForm" [uiBindings]="uiBindings">
</div>
<button igxButton="raised" type="submit" igxRipple class="button" [disabled]="!dynamicForm.formGroup.valid">
<igx-icon>
directions_run
</igx-icon>
<span>Submit</span>
</button>
</div>
</form>
<div igxDialogActions>
<!-- <button igxButton (click)="form.close()">CANCEL</button> -->
<button igxButton (click)="form.close()">Submit</button>
</div>
</igx-dialog>
</div>
<h6 class="h6">
Levels
</h6>
<igx-tree #tree class="tree" selection="None" >
<igx-tree-node *ngFor="let level1 of myData" [data]="level1">
{{ level1.Name }}
<igx-tree-node *ngFor="let level2 of level1.levels" [data]="level2">
{{ level2.Name }}
<igx-tree-node *ngFor="let level3 of level2.levelplus" [data]="level3" (dblclick)="onDoubleClick($event,level3)">
{{level3.Name }}
</igx-tree-node>
</igx-tree-node>
</igx-tree-node>
</igx-tree>
</div>
XYZ.component.ts:
export class XYZComponent implements OnInit {
#ViewChild('form') dialog: IgxDialogComponent;
myData: any[];
public tree: IgxTreeComponent;
public selectedNode;
public ID: number = 2;
isShowRunButton: boolean = false;
public dynamicForm!: DynamicFormBuildConfig;
public dynamicFormConfiguration!: DynamicFormConfiguration;
constructor(private dynamicFormBuilder:RxDynamicFormBuilder){}
ngOnInit() {
populate tree with data here ...
}
public onDoubleClick(event,node) {
console.log(node);
event.stopPropagation();
this.runParameters(node.Id);
}
public runParameters(Id) {
this.aSer.getApi(Id).subscribe({next:(data: any[]) => {this.myData = data;},
error: err => {console.log(err); },
complete: () => {
this.dynamicForm =
this.dynamicFormBuilder.formGroup(this.myData,this.dynamicFormConfiguration);
this.isShowFormButton = true;
//this.dialog.open();
}
});
}
public onSubmit() {
console.log(this.dynamicForm.formGroup);
this.isShowFormButton= false;
//this.dialog.open();
}
}
If I uncomment out the 'this.dialog.open()' the code throws the following error:
TypeError: Cannot read properties of undefined (reading 'open')
Many postings say that I need to use a #ViewChild but it seems that it cannot find that reference : #ViewChild('form') dialog: IgxDialogComponent;
Any help would be much appreciated. Code works fine with the 'Run' button click but I want to eliminate that extra step.
Recently, I came across an issue and was able to fix it by cleaning the solution. But now I have the same issue and cleaning the solution does not fix my bug.
In my project, I use modals to display forms. So, I created a modal component with an EditForm to generate a new entity in my database.
<div class="modal">
<div class="modal-body">
<div class="row">
<div class="col s12">
<h5>New Item</h5>
</div>
</div>
<div class="row">
<EditForm Model="MyEntity" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
<DataAnnotationsValidator />
<ValidationSummary></ValidationSummary>
<div class="input-field fixed col s12">
<InputText id="name" #bind-Value="MyEntity.Name" />
<label class="active" for="name">Name</label>
</div>
<div class="col s12">
<button class="right btn" type="submit">Create</button>
</div>
</EditForm>
</div>
</div>
<div class="modal-footer">
<button class="modal-close btn-flat">Close</button>
</div>
</div>
#code {
[Parameter]
public MyEntityClass MyEntity {get; set;}
[Parameter]
public EventCallback<Microsoft.AspNetCore.Components.Forms.EditContext> OnValidSubmit { get; set; }
[Parameter]
public EventCallback<Microsoft.AspNetCore.Components.Forms.EditContext> OnInvalidSubmit { get; set; }
}
At the index page I would like to use the component like this:
#page "/mypage"
<MyProject.Pages.Shared.MyModalComponent MyEntity="_newMyEntity" OnValidSubmit="HandleValidSubmit" InvalidSubmit="HandleOnInvalidSubmit" />
#*
Some more HTML Code ...
*#
#code{
private MyEntityClass _newMyEntity = new MyEntityClass();
void HandleValidSubmit()
{
// Write to Database ...
}
void HandleOnInvalidSubmit()
{
// Display Errormessage to User ...
}
}
Blazor doesn't render the component, it just renders as HTML markup with the variable names:
Now to the strange part: On a different page I built a table component to display complex data and there all works fine! Did I miss something?
I'm using .NET Core 3.1 Blazor Server Side with Visual Studio 2019 Enterprise Version 16.4.4
I found the reason for this weird behaviour: Visual Studio sometimes doesn't set the build action type to "Content" on creating a new blazor component. After changing and rebuilding all works fine.
I'm currently trying to use a UserControl in my master page; however my ascx file doesn't like my code, two parts specifically:
#Html (Doesn't exist in current context)
Model. (Doesn't exist in current context)
.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ConveyancingUserControl.ascx.cs" Inherits="xxx.Controllers.ConveyancingUserControl" %>
<div id="cwContainer" class="ui-widget-content">
<div id="cwHead">
<p class="cwhTitle">Conveyancing Quotation</p>
</div>
<div id="cwBody">
<%using (Html.BeginForm("Home", "Xxxx", FormMethod.Post, new { onsubmit = "document.getElementById('xxxxBusy').style.display = 'inline';", #class = "xxxxForm" }))
{%>
<table>
<tr>
<td>What are you doing?</td>
<td> <%: #Html.DropDownListFor(Quote => Model.sessionQuote.quoteType, Model.sessionQuote.quoteTypeList, new { style = "width:150px" })%> </td>
</tr>
</table>
<%} %>
</div>
<div id="cwFoot"></div>
</div>
ascx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Mvc;
namespace ASP4HFWClaimsPortal.Controllers
{
public partial class ConveyancingUserControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
.Master:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<xxx.Models.XxxxSession>" %>
<!-- The main body of each page -->
<div id="MainBody" runat="server" >
<ul class="xxxxMenu">
<li class="xx"> <%: Html.ActionLink("Home", "Home", "x", null, new { onclick="document.getElementById('xx').style.display = 'inline';", #class="x", #title="Home" })%></li>
<li class="x"> <%: Html.ActionLink("About", "About", "Xxxx", null, new { onclick="document.getElementById('xx').style.display = 'inline';", #class="x", #title="About" })%></li>
</ul>
<section class="content-wrapper main-content clear-fix">
<!-- Additional BODY content for each individual page is inserted here -->
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</section>
<uc1:conveyancingusercontrol ID="ConveyancingUserControl1" runat="server" />
</div>
Web.config:
<controls>
<add src="~/Controllers/ConveyancingUserControl.ascx" tagName="ConveyancingUserControl" tagPrefix="uc1"/>
</controls>
I realise that my ascx file needs some kind of reference to find my model I'm just not sure how to do this... or if it's even possible. With regards to the 'Html.' issue, I guess this is something similar.
Apologies if these are silly questions... ASP seems to be one of those skills i jump into every now and again so I never get time to really explore it.
#Html is Razor syntax. You need to use the <%, <: etc syntax for ASP.
Model exists in MVC, you need to look at code-behind to populate your controls.
In my view model, I have a list of objects. I iterate these objects, and create controls for each of them. In the situation below, I want to show people a textbox and a button for each object. When the user clicks the button, a post is made, and I can save the data in my controller.
In the UI, a user can change the form they want, and click save.
My problem is the model is null when it's posted to the controller..
My Razor code:
using (Html.BeginForm())
{
foreach (var contributor in Model.Contributor)
{
#Html.HiddenFor(model => contributor.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => contributor.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => contributor.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
}
My view model code
public class ProfileModel
{
public string Message { get; set; }
public List<PublisherModel> Publisher { get; set; }
public List<ContributorModel> Contributor { get; set; }
public ContributorModel NewContributor { get; set; }
}
My controller code
[HttpPost]
public ActionResult Mine(ProfileModel model, string newuser)
{
//
}
How to fix it?
I guess I have to expand my view model with a way to store the changes in some way. But I really can't see how.
Right now all the properties in the ProfileModel is null when it reaches the controller.
Any ideas?
Basically the problem is that default model binder is unable to bind collection items correctly in foreach loop. In other words, it names the element incorrectly and that's why the collection displays as null in parameters.
I'm sure there are all kinds of different workarounds, helpers and stuff but I'm not familiar with those, so I just use for loop instead of foreach, this way the elements are named correctly.
Try this:
#for (int i = 0; i < Model.Contributor.Count(); i++)
{
#Html.HiddenFor(model => Model.Contributor[i].Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.Contributor[i].FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.Contributor[i].FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
I suggest you to use a debugging tool to see if elements have correct name attribute, in your case they should look like Contributor[0].Id, Contributor[0].FirstName etc.
You can use PartialView for Contributor object.
PartialView:
#model Contributor
using (Html.BeginForm("ContributorUpdate", "YourController"))
{
#Html.HiddenFor(model => Model.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
View will be:
#foreach (var contributor in Model.Contributor)
{
#{Html.RenderPartial("Conributor", contributor);}
}
And controller code:
[HttpPost]
public ActionResult Mine(Conributor conributor, string newuser)
{
//
}
I'm new to this and have no idea how it must work.
I have a partial view in a foreach in my view that lists all news comments for that news article.
I have a textarea with a post button where the user can submit further comments on this news article.
The new news article must be appended to the list, without doing a location.reload. I was told do use AJAX, not JSON.
Here's my controller:
[HttpGet]
[NoCache]
public ActionResult SetCommentOnNews(int newsId, string newsComment) ??
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
ZincService.NewsService.SetCommentOnNews(newsId, newsComment, currentUser.UserId);
return Json(new { success = true }, JsonRequestBehavior.AllowGet); ??
}
<div class="news-comment-content" id="news-comment-content">
<% if (Model.Results != null) {
foreach (var newsItem in Model.Results.NewsComments) %>
<% { %>
<% Html.RenderPartial("~/Views/Home/SetCommentOnNews.ascx", newsItem); %>
<% } %>
</div>
my partial:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Zinc.Web.Areas.News.ViewModels.Home.NewsCommentsViewModel>" %> //this also not right
<div class="news-post-list-item">
<div class="news-post-user-info-wrapper">
<div class="avatar">
<img width="52" height="52" alt="Avatar" src="/ThemeFiles/Base/images/User/user-avatar.png"/>
</div>
<div class="who-and-when-box">
<%: newsItem.CommentDate %>
<br />
<br />
<%: ViewBag.UserName %>
</div>
<div class="news-comment"><%: newsItem.NewsComment %></div>
<div class="clear"></div>
</div>
<div class="clear"></div>
</div>
<div class="header">
<h3>
Leave a comment
</h3>
</div>
<div>
<textarea id="textareaforreply" rows="3" cols="160"></textarea>
</div>
<div>
Post
</div>
<script type="text/javascript">
function PostNewsComment(newsId) {
$("post-button").click(function () {
var jqxhr = $.getJSON("<%= //Url.Action("SetCommentOnNews", "Home", new { area = "News" }) %>?newsId=" + newsId + "&newsComment=" + $("#textareaforreply").text(), function (data) {
if (data.success) {
alert($("#textareaforreply").text());
$('#news-comment').append($("#textareaforreply").text());
}
});
}
</script>
The above JS is what I have and must inject HTML in to the list using AJAX?
I have NO idea how to do this. Can some one help please?
Thanks
To inject HTML into a list using AJAX I would use Knockoutjs with templates instead of partial views. Knockout can be used to render the information in the browser. Views are for rendering server side, which does not jibe well with AJAX.
What do you mean when you say, "I was told do use AJAX, not JSON". AJAX uses JSON as a method for serializing the data that is sent over the network. Are you referring to the JQuery methods ajax versus getJSON? getJSON is just a wrapper around the ajax method that configures it specifically to retrieve JSON using the HTTP GET verb. Either will work fine, but ajax does give you more control in configuring the request.