Component not updating when one-way databound property updated - .net-core

I'm having a heck of a time understanding why my component's UI will not re-render when a simple one-way databound property (List) is updated. I have other routable components re-rendering using the same approach without issue, but this non-routable component does not seem to want to behave the same way? It is my understanding that the component should update WITHOUT having to call StateHasChanged() on either onclick events.
#inherits Aivia.Bases.AltitudePage;
#inject IAlertService _alertService
#inject IJSRuntime _js;
#if (dsAlerts != null && dsAlerts.Any())
{
<div class="alerts mt-3">
<div class="container">
#foreach (var dto in dsAlerts)
{
<div class="alert alert-primary p-3" role="alert">
<div class="row g-0">
<div class="col-auto icon">
<i class="fa-solid fa-comments"></i>
</div>
<div class="col">
<div class="title">
#dto.Title
</div>
<div class="description">
#dto.Description
</div>
<div class="actions">
<button type="button" class="btn btn-primary" onclick="#(() => Read(dto))">
<i class="fa-regular fa-circle-check me-2"></i>
Dismiss
</button>
</div>
</div>
</div>
</div>
}
</div>
</div>
}
#code {
private IEnumerable<dtoAlert> dsAlerts = Enumerable.Empty<dtoAlert>();
protected override void OnInitialized()
{
GetAlerts();
}
private void GetAlerts()
{
this.dsAlerts = _alertService.queryUnreadAlerts(this.UserID).OrderBy(x => x.CreatedOn).ToList();
}
private void Read(dtoAlert dto)
{
_alertService.Read(this.UserID, dto.ID);
GetAlerts();
}
}

onclick="#(() => Read(dto))" simply needed to be #onclick="#(() => Read(dto))"

Related

Check if IEnumerable list in View Model is null in View

I just wanted to check if it's possible to check if an IEnumerable list is null in the view when passed from the controller via the view model. Or should I only create conditions in the controller?
My code below does not show the else condition.
Apologies if this is painfully obvious to everyone but I'm still only learning and thanks in advance for any pointers in the right direction.
My controller:
[Area("User")]
[Authorize]
public class TrayController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public OfferTrayVM OfferTrayVM { get; set; }
public TrayController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IActionResult Index()
{
var claimsIdentity = (ClaimsIdentity)User.Identity;
var claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
OfferTrayVM = new OfferTrayVM()
{
ListOfferTray = _unitOfWork.OfferTray.GetAll(u => u.ApplicationUserId == claim.Value,
includeProperties: "Trade")
};
return View(OfferTrayVM);
}
public IActionResult Delete(int trayId)
{
var tray = _unitOfWork.OfferTray.GetFirstOrDefault(u => u.Id == trayId);
_unitOfWork.OfferTray.Remove(tray);
_unitOfWork.Save();
return RedirectToAction("Index", "Home", new { area = "User" });
}
}
My view model:
public class OfferTrayVM
{
public IEnumerable<OfferTray> ListOfferTray { get; set; }
}
My view:
#model QPQ.Models.ViewModels.OfferTrayVM
<form method="post">
<br />
<div class="container">
<div class="card">
<div class="card-header bg-dark text-light ml-0">
<div class="row">
<div class="col-6 pt-2">
Summary Confirmation
</div>
<div class="col-6 text-end">
</div>
</div>
</div>
#if(Model.ListOfferTray != null)
{
<div class="card-body">
#foreach (var item in Model.ListOfferTray)
{
<div class="row">
<div class="col-12 col-lg-4 pt-md-3">
<h5><strong>#item.Trade.CompanyName - Have offered to trade: </strong></h5>
<p><small>#item.Trade.Description</small></p>
<hr>
<h5><strong>#item.Trade.CompanyName - Have requested in Return: </strong></h5>
<p><small>item.Trade.InReturnFor</small></p>
</div>
<div class="col-12 col-lg-4 pt-md-3">
<h5><strong>You are Offering in return:</strong></h5>
<p><small>#item.OfferInReturn</small></p>
</div>
<div class="col-12 col-lg-4 text-center row">
<div class="col-sm-4 text-right pt-2">
<a asp-action="Delete" asp-route-trayId="#item.Id" class="btn btn-danger">
<i class="bi bi-trash-fill">Delete</i>
</a>
</div>
</div>
</div>
<hr />
}
</div>
}
else
{
<div class="card-body">
<h3>You do not have any existing offers. Please click<a asp-action="index" asp-area="Home">here</a>to continue.</h3>
</div>
}
<div class="card-footer">
<div class="card-footer row">
<div class="col-sm-12 col-lg-4 col-md-6 offset-lg-8 offset-md-6 ">
<a class="btn btn-success form-control">Summary</a>
</div>
</div>
</div>
</div>
</div>
</form>

How to Pin Mudblazor Card

I have created this card list with several mudblazor components.
this is the code for this card list.
<MudGrid>
#foreach (var pincard in pincards)
{
<MudItem xs="6" md="3">
<div class="dashboard-tile-card">
<div class="dashboard-tile-card-header">
<span> #pincard</span>
<MudIconButton Icon="#Icons.TwoTone.PushPin"/>
</div>
<div class="dashboard-tile-icon ">
<img src="/img/finance.svg" width="50%" />
</div>
<div class="dashboard-card-action">
<a class="text-white">...</a><MudIcon Style="#($"color:{Colors.Grey.Lighten5};")" Icon="#Icons.Filled.ArrowForward"></MudIcon>
</div>
</div>
</MudItem>
}
</MudGrid>
#code{
List<string> pincards = new List<string>();
protected override async Task OnInitializedAsync()
{
pincards.Add("Mycard1");
pincards.Add("Mycard2");
pincards.Add("Mycard3");
pincards.Add("Mycard4");
}
}
The purpose of this top right corner pin button is to provide an unpinning option for each card if we want. when we press that pin button, it should be unpinned or removed from this list. then I need to know how to do this ?? appreciate your help
First of all, you can create your card with the component MudCard from MudBlazor it will be easier
To answer your question . Create a function
private void RemovePinCard(string cardId){
pincards.Remove(cardId);
}
<MudGrid>
#foreach (var pincard in pincards)
{
<MudItem xs="6" md="3">
<div class="dashboard-tile-card">
<div class="dashboard-tile-card-header">
<span> #pincard</span>
<MudIconButton Icon="#Icons.TwoTone.PushPin" OnClick="#(()=>RemovePinCard(pincard))"/>
</div>
<div class="dashboard-tile-icon ">
<img src="/img/finance.svg" width="50%" />
</div>
<div class="dashboard-card-action">
<a class="text-white">...</a><MudIcon Style="#($"color:{Colors.Grey.Lighten5};")" Icon="#Icons.Filled.ArrowForward"></MudIcon>
</div>
</div>
</MudItem>
}
</MudGrid>

Pass input with route id to controller method together

I want to create input with game amount. User will write how many copy of concrete game wants to buy. Then two parameters will pass to AddToCart method. First will be gameId like below (it works well) and user amount input. How to pass these two values together into controller method?
View:
#model IEnumerable
<link rel="stylesheet" href="~/css/productcards.css">
<div class="container">
<div class="row">
#foreach (var game in Model)
{
<div class="col-md-3">
<div class="product-grid">
<div class="product-image">
<a asp-controller="Game" asp-action="ShowDetails" asp-route-productId=#game.GameId>
<img class="pic-1" src="~/images/#game.ImagePath">
</a>
</div>
<div class="product-content">
<h3 class="title">
<a asp-controller="Game" asp-action="ShowDetails" asp-route-productId=#game.GameId>#game.Title</a>
</h3>
<div class="price">#game.Price.ToString() zł</div>
<a asp-controller="Cart" asp-action="AddToCart" asp-route-gameId="#game.GameId" class="btn btn-primary"><i class="fa fa-cart-plus"></i> KUP</a>
</div>
</div>
</div>
}
</div>
</div>
Controller:
public RedirectToActionResult AddToCart(int gameId)
{
var selectedGame = _appDbContext.Games.FirstOrDefault(x => x.GameId == gameId);
if(selectedGame != null)
{
_cart.AddToCart(selectedGame, 1);
}
return RedirectToAction("Index");
}
Change your view like below:
#foreach (var game in Model)
{
<form method="post">
<div class="col-md-3">
<div class="product-grid">
<div class="product-content">
<h3 class="title">
<a asp-controller="Game" asp-action="ShowDetails" asp-route-productId=#game.GameId>#game.Title</a>
</h3>
<div class="price">#game.Price.ToString() zł</div>
<div class="amount"><input name="amount" type="number" /></div> #*add this line*#
</div>
</div>
</div>
<div class="col-md-3">
#*change <a> to <input> and add a <form>*#
<input type="submit" asp-route-gameId="#game.GameId" asp-controller="Cart" asp-action="AddToCart" class="btn btn-primary" value="KUP"/>
</div>
</form>
}
Action:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddToCart(int gameId,int amount)
{
//do your stuff...
}
Result:

Converting Semantic-Ui-React to Semantic-UI; event handler

I am trying to successfully express Semantic-UI code with the same functions I have used in Semantic-UI-react code. Any help would be appreciated.
This is what I have:
class Preview extends React.Component {
componentDidMount() {
const update = () => {
this.dest.textContent = this.src.innerHTML.replace(/</g,
'\n<');
};
setInterval(update, 300);
update();
}
render() {
return (
<div>
<div ref={(src) => Object.assign(this, { src })}>
<Demo />
</div>
<pre ref={(dest) => Object.assign(this, { dest })}>
</pre>
</div>
)
}
}
export class Demo extends Component { constructor(){
super();
this.localStorageClear.bind(this); }
localStorageClear = (e) => {
e.preventDefault();
window.localStorage.clear();
};
render() {
return (
<div id = "soundcloud-player">
<Container className='col'>
<div className='col-left js-playlist toggle'>
<div className='inner'>
</div>
</div>
<div className='col-right'>
<div className = 'main'>
<Input size='massive' icon='search' input = {{ className:
'input-search js-search' }} placeholder='Search for a song
or artist...'/>
<Icon className='js-submit'/>
<Button onClick={(e) => this.localStorageClear(e)}
className='clear' content='Clear Playlist'/>
<Button content='Show/Hide Playlist' id='toggle'
className='hide-toggle'/>
<Card className='js-search-results search-results'/>
</div>
</div>
</Container>
</div>
The code written in the Preview Component is specifically written to convert the code written inside of the Demo Component. The Demo Component should convert to what is shown below:
<div class="ui container col">
<div class="col-left js-playlist toggle">
<div class="inner">
</div>
</div>
<div class="col-right">
<div class="main">
<div class="ui massive icon input">
<input placeholder="Search for a song or artist..." class="js-search input-search">
<i class="search icon js-submit"></i>
</div>
<button onclick="localStorageClear();" class="clear">Clear Playlist</button>
<button class="hide-toggle" href="#" id="toggle">Show/Hide Playlist</button>
<div class="search-results js-search-results ui cards">
</div>
</div>
</div>
The actual output of the code at the top is:
<div id="soundcloud-player">
<div class="ui container col">
<div class="col-left js-playlist toggle">
<div class="inner">
</div>
</div>
<div class="col-right">
<div class="main">
<div class="ui massive icon input input-search">
<input placeholder="Search for a song or artist..." type="text">
<i aria-hidden="true" class="search icon">
</i>
</div>
<i aria-hidden="true" class="icon js-submit">
</i>
<button class="ui button clear" role="button">Clear Playlist
</button>
<button id="toggle" class="ui button hide-toggle" role="button">Show/Hide Playlist
</button>
<div class="ui card js-search-results search-results">
</div>
</div>
</div>
</div>
</div>
I'm trying to figure out why the localStorageClear function does not show up for the first button in the actual output. Is there wrong I am doing at the top inside of the Semantic-UI-React code inside of the Demo Component?
The way you are setting up your handler function is not correct. You are also binding in your constructor AND inline with an arrow function inside of the onClick event for the button. You only need to bind in one place.
Take a look at the codesandbox example I made so you can see how to declare a class method handler function and use it with a click event. Notice that there is no constructor here or arrow function to bind on the onClick event? That is because the binding is happening on the class method. handleClick = () => {}
class App extends React.Component {
handleClick = e => {
console.log(e.target + " was clicked.");
// Do whatever functionality you need here.
// In your example you do not show that it matters what the element is,
// so you don't need to pass the event (e) into your class method.
};
render() {
return (
<Container>
<Divider hidden />
<Button content="Click Me" onClick={this.handleClick} />
<Divider hidden clearing />
<Message info>
Look in your console and you will see that the click function is
working.
</Message>
</Container>
);
}
}
Here is a working codesandbox example.

Accordion doesn't automatically count

My accordion doesn't automatically count up, the number stays at 1 for some reason. I am using a CMS called Umbraco on version 7.7.2.
Here's my code:
#if(#Model.Content.GetPropertyValue("titleAccordeon") != "")
{
<section class="block block__accordion">
<div class="container">
<div class="row">
<div class="block__heading col-md-12">
<h3>#Model.Content.GetPropertyValue("titleAccordeon")</h3>
<p>#Model.Content.GetPropertyValue("introAccordeon")</p>
</div>
#if(Model.Content.Accordion != null && Model.Content.Accordion.Any())
{
foreach (var item in Model.Content.Accordion)
{
var guid = Guid.NewGuid();
<div class="accordion panel-group col-md-12" role="tablist" aria-multiselectable="true">
<div class="accordion__item">
<a class="accordion__item__header" role="button" data-toggle="collapse" data-target="#accordion-#guid" aria-expanded="false" aria-controls="accordion-#guid">
#item.Title
</a>
<div class="collapse" id="accordion-#guid">
<div class="accordion__item__body">
#item.Description
</div>
</div>
</div>
</div>
}
}
</div>
</div>
</section>
And this is the result on one of the pages:
All the accordions have a different control-id thanks to the guid. Any idea why it doesn't count up?
How is it auto numbering? I don't see an OL or anything that would auto-number. Seems like the number is just part of the item.Title? If that is this case, you could do something like this.
#if(Model.Content.Accordion != null && Model.Content.Accordion.Any())
{
var counter == 1;
foreach (var item in Model.Content.Accordion)
{
var guid = Guid.NewGuid();
<div class="accordion panel-group col-md-12" role="tablist" aria-multiselectable="true">
<div class="accordion__item">
<a class="accordion__item__header" role="button" data-toggle="collapse" data-target="#accordion-#guid" aria-expanded="false" aria-controls="accordion-#guid">
#(counter + ". " + #item.Title)
</a>
<div class="collapse" id="accordion-#guid">
<div class="accordion__item__body">
#item.Description
</div>
</div>
</div>
</div>
#counter++;
}
}
I managed to get the code working. I moved one of the div's outside the if code and it worked. Not sure why the div made the accordions count though. Thanks to hardba11 and ADyson for trying to help!
<div class="accordion panel-group col-md-12" role="tablist" aria-multiselectable="true">
#if(Model.Content.Accordion != null && Model.Content.Accordion.Any())
{
foreach (var item in Model.Content.Accordion)
{
var guid = Guid.NewGuid();
<div class="accordion__item">
<a class="accordion__item__header" role="button" data-toggle="collapse" data-target="#accordion-#guid" aria-expanded="false" aria-controls="accordion-#guid">
#item.Title
</a>
<div class="collapse" id="accordion-#guid">
<div class="accordion__item__body">
#item.Description
</div>
</div>
</div>
}
}
</div>

Resources