Is it OK to call a method in the #functions{} section in razor pages directly from HTML? This seems to work fine and it's much easier than calling an API, but I was wondering if there is a downside to this (security, performance, etc)?
For example, in the code...
#functions {
public class Tickets: PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
public void OnGet()
{
}
public string GetTickets(int Top) //--> THIS IS THE METHOD I AM CALLING
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}
}
And the HTML...
<div class="card alert-warning" v-for="ticket in tickets">
<div class="card-body">
<h4 class="card-title">{{ticket.TicketSubject}}</h4>
Card link
Another link
</div>
</div>
#section Scripts {
<script>
new Vue({
el: '#app',
data: {
dismissSecs: 5,
dismissCountDown: 5,
tickets: #Html.Raw(Model.GetTickets(100)), //-->THIS WORKS, BUT IT IS OK TO USE LIKE THIS?
xx: ''
}
})
</script>
}
It works because I believe the method is called when the page is being rendered and you have constant value being passed in. It would not automatically do something like update client-side without AJAX code being involved.
But to answer you question I think the 'more correct' approach is to set a binding property in the OnGet and reference that client-side
public class Tickets : PageModel
{
public ApplicationDbContext _db { get; set; }
public Tickets(ApplicationDbContext db)
{
_db = db;
}
[BindProperty]
public string TicketData { get; set; }
public void OnGet()
{
TicketData = GetTickets(100);
}
public string GetTickets(int Top)
{
var data = _db.Tickets.OrderByDescending(x => x.CreatedAt).Take(Top);
var jdata = JsonConvert.SerializeObject(data.ToList());
return jdata;
}
}
Related
I'm trying to create a component similar to Response.Write("") back in the day for helping me diagnose Blazor lifecycle issues. This is what I have so far, but nothing is printing to the UI. However, when I remove the component and directly write to the UI, I get no problems.
<div>
#foreach (string line in Lines)
{
<div>#line</div>
}
</div>
#code {
[Parameter]
public List<string> Lines
{
get
{
return _lines;
}
set
{
_lines = value;
}
}
In my page component I have an instance of the Debug component like:
<Debug #ref="LocalDebug" />
#code {
protected Debug LocalDebug { get; set }
protected void Function() {
Debug.Lines.Add("Print something");
}
}
You doing it in a wrong way, you can achieve functionality like this
This is your Debug.razor component
<div>
#foreach (string line in Lines)
{
<div>#line</div>
}
</div>
#code {
private List<string> Lines = new();
public void AddLine(string line){
Lines.Add(line);
StateHasChanged();
}
}
This is how you can use it in razor page
<Debug #ref="LocalDebug" />
This is code behind in razor page
#code {
private Debug LocalDebug;
protected void Function() {
LocalDebug.AddLine("Add something");
}
}
I am using Blazor server-side to make a chat room.
For the style of receiving the message and sending the message is different, I made a model named MsgModel
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorApp1
{
public class MsgModel: ComponentBase
{
public string MsgText { get; set; }
}
}
The razor components ReceiveMsg.razor and the SendMsg.razor are base on this model.
#inherits MsgModel
<h3>ReceiveMsg</h3>#MsgText
#inherits MsgModel
<h3>SendMsg</h3>#MsgText
In the index.razor, I wanna input the message text and display it immediately.
#page "/"
#foreach (MsgModel _MsgModel in MsgList)
{
if (_MsgModel.GetType() == typeof(ReceiveMsg))
{
<ReceiveMsg></ReceiveMsg>
}
else
{
<SendMsg></SendMsg>
}
}
<div id="inputDiv">
<EditForm Model="_InputMsgModel" OnValidSubmit="#SubmitText">
<InputText #bind-Value="_InputMsgModel.MsgText" />
</EditForm>
</div>
#code{
protected MsgModel _InputMsgModel { get; set; } = new MsgModel();
protected List<MsgModel> MsgList { get; set; } = new List<MsgModel>();
protected void SubmitText()
{
SendMsg _SendMsg = new SendMsg();
_SendMsg.MsgText = _InputMsgModel.MsgText;
MsgList.Add(_SendMsg);
}
}
Now the problem is: in the for block, I should transfer the _MsgModel to the component. Meanwhile, I don't know how to transfer it yet.
Would you please help me? Thank you.
Finally, I found a strange and stupid way to solve this.
I add these code into the MsgModel:
[Parameter]
public MsgModel TransferModel
{
set
{
CopyAll(value, this);
}
}
private void CopyAll<T>(T source, T target)
{
var type = typeof(T);
foreach (var sourceProperty in type.GetProperties())
{
if (sourceProperty.Name != "TransferModel")
{
var targetProperty = type.GetProperty(sourceProperty.Name);
targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
}
}
}
And change the for block like this:
#foreach (Models.MsgModel _MsgModel in MsgList)
{
if (_MsgModel.GetType() == typeof(ReceiveMsg))
{
<ReceiveMsg ShowFullImage="#ShowFullImage" TransferModel="_MsgModel"></ReceiveMsg>
}
else
{
<SendMsg ShowFullImage="#ShowFullImage" TransferModel="_MsgModel"></SendMsg>
}
}
What a stupid way it is!
In Asp.Net MVC we can add class conditionally as following code:
<div class="choice #(Model.Active?"active":"")">
</div>
How can do this by using tagHelper and by remove else part in condition.
Ability to add a conditional css class by following tagHelper provides.
this code like AnchorTagHelper asp-route-* for add route values acts.
[HtmlTargetElement("div", Attributes = ClassPrefix + "*")]
public class ConditionClassTagHelper : TagHelper
{
private const string ClassPrefix = "condition-class-";
[HtmlAttributeName("class")]
public string CssClass { get; set; }
private IDictionary<string, bool> _classValues;
[HtmlAttributeName("", DictionaryAttributePrefix = ClassPrefix)]
public IDictionary<string, bool> ClassValues
{
get {
return _classValues ?? (_classValues =
new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase));
}
set{ _classValues = value; }
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var items = _classValues.Where(e => e.Value).Select(e=>e.Key).ToList();
if (!string.IsNullOrEmpty(CssClass))
{
items.Insert(0, CssClass);
}
if (items.Any())
{
var classes = string.Join(" ", items.ToArray());
output.Attributes.Add("class", classes);
}
}
}
in _ViewImports.cshtml add reference to taghelper as following
#addTagHelper "*, WebApplication3"
Use tagHelper in View:
<div condition-class-active="Model.Active" condition-class-show="Model.Display">
</div>
result for Active = true and Display = true is:
<div class="active show">
</div>
There's no default way to do what you're asking. You would have to write a TagHelper that did that logic for you. Aka
[HtmlTargetElement(Attributes = "asp-active")]
public class FooTagHelper : TagHelper
{
[HtmlAttributeName("asp-active")]
public bool Active { get; set; }
public override void Process(TagHelperOutput output, TagHelperContext context)
{
if (Active)
{
// Merge your active class attribute onto "output"'s attributes.
}
}
}
And then the HTML would look like:
<div class="choice" asp-active="Model.Active"></div>
im building a site using MVC4 and i want to display a navigation bar at the top of my _ViewStart according to my Database.
How can i do so? can i use a contoroller ActionResult that fired once the index page is loaded?
or how can i triger it by a partial view
my current ActionResult returning partial view is:
public ActionResult NavigationBar()
{
var entities = new CakesDBEntities();
var articles = entities.Articles;
List<NavBarModel> navBarList = articles.Select(nb => new NavBarModel { Title = nb.title, Url = nb.url }).ToList();
return View(navBarList);
}
my model:
namespace SimplyCakes20131009.Models
{
public class NavBarModel
{
public string Title { get; set; }
public string Url { get; set; }
}
}
my partial view:
#model IEnumerable<SimplyCakes20131009.Models.NavBarModel>
#foreach (var bar in Model)
{
<li>
#Html.ActionLink(bar.Title, bar.Url)
</li>
}
How can i integrate the nav bar to my _ViewStart?
A better option would be to use the _Layout.cshtml. _ViewStart is just calls the _Layout.cshtml.
You probably don't need partial View here. You can use a Child Action that renders PartialView results.
In your
_Layout.cshtml :
You can have
#{ Html.RenderAction("Navigation", "Home"); }
This points to the HomeController and Navigation Action
Additional Note: Html.RenderAction better because it is much faster than the Html.Action.
It can handle large amount of HTML efficiently as it will directly send the result to the Response. Html.Action just returns a strings with the result.
Navigation Action has its Navigation View which is pretty much equivalent to what you had in your view.
Home/Navigation.cshtml :
#model IEnumerable<MvcApplication1.Controllers.NavViewModel>
#foreach (var nav in Model)
{
<li>#Html.ActionLink(nav.Title, nav.Url)</li>
}
HomeController.cs :
Note that you probably inject the DB access as dependency to support the testability.
public class HomeController : Controller
{
private readonly ICakesRepository _cakesRepository;
//additional constructor to support testability.
public HomeController(ICakesRepository cakesRepository) {
_cakesRepository = cakesRepository;
}
//this can be removed if you the above with IOC/DI wire-up
public HomeController() {
_cakesRepository = new CakesRepository();
}
[ChildActionOnly]
[HttpGet]
public ActionResult Navigation() {
var articles = _cakesRepository.GetArticles();
var navBarList = articles.Select(nb => new NavViewModel { Title = nb.Title, Url = nb.Url });
return PartialView(navBarList);
}
}
Additional supporting classes :
public class NavViewModel {
public string Title { get; set; }
public string Url { get; set; }
}
public interface ICakesRepository {
IEnumerable<Articles> GetArticles();
}
public class CakesRepository : ICakesRepository {
public IEnumerable<Articles> GetArticles() {
//call to a db
//fake db data
return new List<Articles>() {
new Articles(){Title = "Title1", Url = "http://urlone.com"},
new Articles(){Title = "Title2", Url = "http://urltwo.com"},
new Articles(){Title = "Title3", Url = "http://urlthree.com"}
};
}
}
public class Articles {
public string Title { get; set; }
public string Url { get; set; }
}
Is there any standard practice to display errors in a view? Currently it is being displayed from TempData.
I implemented a derived a class from Base Controller and used that derived class in every one of my controller. Then assign error or success messages from controller.
public class TestController : Controller
{
public string ErrorMessage
{
get { return (string) TempData[CommonHelper.ErrorMessageKey]; }
set
{
if (TempData.ContainsKey(CommonHelper.ErrorMessageKey))
{
TempData[CommonHelper.ErrorMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.ErrorMessageKey,value);
}
TempData.Remove(CommonHelper.SuccessMessageKey);
}
}
public string SuccessMessage
{
get { return (string)TempData[CommonHelper.SuccessMessageKey]; }
set
{
if(TempData.ContainsKey(CommonHelper.SuccessMessageKey))
{
TempData[CommonHelper.SuccessMessageKey] = value;
}
else
{
TempData.Add(CommonHelper.SuccessMessageKey, value);
}
TempData.Remove(CommonHelper.ErrorMessageKey);
}
}
}
CommonHelper Class
public class CommonHelper
{
public const string SuccessMessageKey = "successMessage";
public const string ErrorMessageKey = "errorMessage";
public static string GetSuccessMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
public static string GetErrorMessage(object data)
{
return data == null ? string.Empty : (string) data;
}
}
Then created a partial view having this
#using Web.Helpers
#if (!string.IsNullOrEmpty(CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetSuccessMessage(TempData[CommonHelper.SuccessMessageKey])
</div>
}
else if (!string.IsNullOrEmpty(CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])))
{
<div class="alert alert-success">
#CommonHelper.GetErrorMessage(TempData[CommonHelper.ErrorMessageKey])
</div>
}
And in every view, the partial view is rendered.
<div>
#Html.Partial("_Message")
</div>
Here is a pretty common implementation of displaying errors.
Controller
public class UserController : Controller
{
[HttpPost]
public ActionResult Create(User model)
{
// Example of manual validation
if(model.Username == "Admin")
{
ModelState.AddModelError("AdminError", "Sorry, username can't be admin")
}
if(!ModelState.IsValid()
{
return View(model)
}
}
}
Model
public class User
{
[Required]
public string Username {get; set;}
public string Name {get; set; }
}
View
#Html.ValidationSummary(true)
#using(Html.BeginForm())
{
// Form Html here
}
You don't need all of the infrastructure you created. This is handled by the framework. If you need a way to add success messages you can checkout the Nuget Package MVC FLASH
I prefer to use ModelState.AddModelError()