HtmlBeginCollectionItem Get Current Item - asp.net-mvc-4

I need:
Acess /Client/Create
Add dynamically Partial Views (/Product/Card) and bind them to Client.Products
In each Partial View when i click in a button open a bootstrap modal windows where i can set Product's information
Close the modal and reflect changes of modal reflect in the Card's Product.
The problem is: how to change product informations in another view(other than Card) and reflect to the product of the card?
#using (Html.BeginCollectionItem("Products"))
{
#Html.HiddenFor(model => model.ClientID)
#Html.HiddenFor(model => model.ProductID)
<div class="card">
<img class="card-img-top" src="http://macbook.nl/wp-content/themes/macbook/images/png/iphone318x180.png" alt="Grupo Logo">
<div class="card-block">
<h4 class="card-title">#Model.Name</h4>
<p class="card-text">#Model.Desc</p>
<div class="btn-group">
<button type ="button" class="btn btn-primary open-modal" data-path="/Product/Edit/#Model.ProductID">Edit</button>
<button type="button" class="btn btn-primary open-modal" data-path="/Product/Features/#Model.ProductID">Features</button>
</div>
</div>
</div>
}

You can do this is another view (or by dynamically loading another view into a modal. The object has not been created yet, and since you using BeginCollectionItem() to generate new items, any other view you used would not be using the same Guid created by that helper so you would not be able to match up the collection items.
Instead, include the 'other' properties within the partial, but put them in a hidden element that gets displayed as a modal when you click the buttons.
The basic structure of the partial for adding/editing a Product would be
<div class="product">
#using (Html.BeginCollectionItem("Products"))
{
#Html.HiddenFor(m => m.ClientID)
#Html.HiddenFor(m => m.ProductID)
<div class="card">
....
<button type ="button" class="btn btn-primary edit">Edit</button>
</div>
// Modal for editing additional data
<div class="modal">
#Html.TxtBoxFor(m => m.SomeProperty)
#Html.TxtBoxFor(m => m.AnotherProperty)
....
</div>
}
</div>
And then handle the buttons .click() event (using delegation since the partials will be dynamically added to the view) to display the associated modal (assumes you have a <div id="products"> element that is the container for all Products)
$('#products').on('click', '.edit', function() {
var modal = $(this).closest('.product').find('.modal');
modal.show(); // display the modal
});

Related

Dropdown list is not working in the Asp.net Blazor Component

I am trying to learn the new feature in ASP.NET Blazor. I am using Visual Studio 2019. I am trying to create an Ideas Registration form. So the code for dropdownlist i had took from Bootstrap 4. It was not working as expected. Can you please tell me where i am working wrong?
Just a little overwhelmed here, any advice would be much appreciated.
Given Code:
<!-- Card Body -->
<div class="card-body">
<!-- <form -->
<form>
<div class="form-group">
<label for="exampleFormControlInput1">Title</label>
<input type="email" class="form-control" id="exampleFormControlInput1">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Description</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="4"></textarea>
</div>
<!-- Basic dropdown -->
<div class="form-group">
<button class="btn btn-primary dropdown-toggle mr-4" type="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Basic dropdown
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">.Net</a>
<a class="dropdown-item" href="#">Python</a>
<a class="dropdown-item" href="#">Data Science</a>
<div class="dropdown-divider"></div>
</div>
</div>
<!-- Basic dropdown -->
where i am working wrong
According to the official docs](https://getbootstrap.com/docs/4.0/components/dropdowns/#data-toggledropdown-still-required):
Regardless of whether you call your dropdown via JavaScript or instead use the data-api, data-toggle="dropdown" is always required to be present on the dropdown’s trigger element.
I would suggest you should wrap your Basic dropdown in the following structure
<div class="dropdown">
<button data-toggle="dropdown" class="..." > ...</button>
<div class="dropdown-menu ...>
...
</div>
</div>
You didn't add an event handler for selection. At least you should add a #onclick for the toggle button. When clicking this button, show or hide the dropdown-menu.
Finally, if you want to implement the dropdown component with Blazor(without javascript), you should also replace the text content within the toggle button when someone selects a dropdown list item.
A Demo : How to Create A General Dropdown Component
Rather than simply fixing the issue, I think it's much better to create a general dropdown Component so that we can always invoke them in following way:
#{ var list = new List<string>{ ".NET", "Python","Java" }; }
<Dropdown TItem="string" OnSelected="#OnSelected" >
<InitialTip>This is a dropdown list</InitialTip>
<ChildContent>
<DropdownListItem Item="#list[0]">.NET</DropdownListItem>
<DropdownListItem Item="#list[1]">Python</DropdownListItem>
<div class="dropdown-divider"></div>
<DropdownListItem Item="#list[2]">Java</DropdownListItem>
</ChildContent>
</Dropdown>
#code {
private void OnSelected(string selection)
{
Console.WriteLine(selection);
}
}
Here the TItem is a generic type parameter that is the type of each dropdown list item and can be any .NET type.
Demo
How-To
Add a Shared/Dropdown.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle mr-4" data-toggle="dropdown" type="button" #onclick="e => this.show=!this.show "
aria-haspopup="true" aria-expanded="false">
#Tip
</button>
<CascadingValue name="Dropdown" Value="#this">
<div class="dropdown-menu #(show? "show":"")" >
#ChildContent
</div>
</CascadingValue>
</div>
#code {
[Parameter]
public RenderFragment InitialTip{get;set;}
[Parameter]
public RenderFragment ChildContent{get;set;}
[Parameter]
public EventCallback<TItem> OnSelected {get;set;}
private bool show = false;
private RenderFragment Tip ;
protected override void OnInitialized(){ this.Tip = InitialTip; }
public async Task HandleSelect(TItem item, RenderFragment<TItem> contentFragment)
{
this.Tip= contentFragment.Invoke(item);
this.show=false;
StateHasChanged();
await this.OnSelected.InvokeAsync(item);
}
}
Add a Shared/DropdownListItem.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<a class="dropdown-item" Item="#Item" #onclick="e=> Dropdown.HandleSelect(Item, ChildContent)" >#ChildContent(Item)</a>
#code {
[CascadingParameter(Name="Dropdown")]
public Dropdown<TItem> Dropdown {get;set;}
[Parameter]
public TItem Item{get;set;}
[Parameter]
public RenderFragment<TItem> ChildContent {get;set;}
}
Keep in mind that bootstrap dropdown requires bootstrap javascript to be referenced. And the Blazor template doesn't reference it by default.
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
The accepted answer is great. However, as many have pointed out, the dropdown does not close if an option is not selected. The suggestion to create an #unblur event does not solve the case, as #unclick and #unblur do not seem to work together in .NET 5 (I read somewhere that it does works in the new .NET 6) - #unblur prevents #unclick to be triggered.
I found this solution (#onblur prevents #onclick in blazor server side app), changing the #onclick to #onmousedown and then creating the #onblur event (as suggested) has fixed the issue for me.

How to pass a dynamicy changed model to Partial view?

I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .

use same form for user add and update Angular5

i am learning angular can someone please help me on following :
I have one form which save user (i call api on this submit click). i need to use same form for update user , for that i took two buttons i show "submit" button if user is new (i managethis new and old user from ge api call in OnInit()) and another button is "update" button, i show and hide this buttons depending on Get api call for user in onInit().
form demo code :
<form class="form-area" (ngSubmit)="ngSubmit(applicant)" name="applicantForm" #applicantForm="ngForm" ngNativeValidate>
<div> some fileds</div>
<div class="row" style="content:center">
<div class="col-md-12">
<div class="form-row">
<div class="form-group">
<button *ngIf="submitStatus" type="submit" class="btn btn-info" [disabled]="!applicantForm.form.valid">
<i class="fa fa-send-o"></i> Submit</button>
<button *ngIf="updateStatus" type="submit" class="btn btn-info" [disabled]="!applicantForm.form.valid">
<i class="fa fa-send-o"></i> Update</button>
</div>
</div>
</div>
</div>
</form>
question :
i call (ngSubmit)="ngSubmit(applicant)" method on submit button call to save user, but i confused in how i differentiate this both operation to call ngSubmit(applicant).
i need to perform both operation in single function call from controller.
thanks

Not Receiving Multiple Partial View data on Postback

Please can you help on the below query.
I have got a main view and partial view. In main view looping through based on the count of model, the partial views will be rendered. The Partial View is as per below
-----------------Partial View----------------------
#using (Html.BeginCollectionItem("books"))
{
<div id="bookRow" class="bookRow">
<div class='container'>
<div class="align-left">
<h1>Book Name</h1>
<h2>#Html.TextBoxFor(m => m.BookName)</h2>
</div>
<div class="align-right">
<h1>Author</h1>
<h2>#Html.TextBoxFor(m => m.Author)</h2>
</div>
<div class="align-right">
<h1>Publisher</h1>
<h2>#Html.TextBoxFor(m => m.Publisher)</h2>
</div>
<div class="align-right">
#Html.CheckBoxFor(model => model.isConfirm)
#Html.Hidden("Id", #Model.Id)
</div>
Delete
</div>
</div>
}
-------------------Main View----------------------
#model IEnumerable<TDLProject.Models.BookViewModel>
<fieldset>
<legend>Books</legend>
<div id="new-Book">
#foreach (var model in Model)
{
#Html.Partial("_DynamicPartialView", model)
}
</div>
<div>
<input type="button" id="addBook" name="addBook" value="Add Book" />
<br />
</div>
<br />
</fieldset>
#using (Html.BeginForm())
{
<div>
<input type="submit" id="submitButton" value="Submit" />
</div>
}
-------------Controller--------------
[HttpPost]
public ActionResult Contact(IEnumerable<BookViewModel> bookModel)
{
// To do: do whatever you want with the data
return View();
}
The Problem is I am not receiving the data posted from View to the Controller on Click of Submit button. I have multiple partial views of same model on the main view and when click of submit button I need all the partial view data to posted to controller. In Partial View I am using BeginCollectionItem. Please can you help on this.
A form only posts back the name/value pairs of its successful controls, and your not generating the form controls inside the <form> tags.
Change the code to move the #Html.Partial() code inside the <form> (simplified)
#model IEnumerable<TDLProject.Models.BookViewModel>
#using (Html.BeginForm())
{
#foreach (var model in Model)
{
#Html.Partial("_DynamicPartialView", model)
}
<input type="button" id="addBook" name="addBook" value="Add Book" />
<input type="submit" id="submitButton" value="Submit" />
}
You also need to change the POST method signature to
public ActionResult Contact(IEnumerable<BookViewModel> books)
to match the name you use in the #using (Html.BeginCollectionItem("books")) method.

static content in partial view mvc4

I have two views as shown below:
boott.cshtml
#model model
<div>
#if (Model.ViewModel != null)
{
#Html.Partial("_b_table", Model)
}
<span class="d2-fldr">Enter folder name: </span>
<input type="text" name="txtName" class="txtFldrname"/>
<a class="small d2-createfldr" title="Create folder" href="#">
<span class="glyphicon glyphicon-plus d2-ico"></span>
<span class="d2-axn">Create folder</span>
</a>
)
</div>
_boott_table.cshtml
#model model
<div class="#BOOTSTRAP.ROW">
-- some content from model
</div>
The view boott.cshtml is directly rendered from the controller's action "boott".
The partial view _boott_table.cshtml content changes always based on the option we select in partial view.
When we click on the options in partial view, we are calling the same action method "boott" and renders the partial boott.cshtml and send the html back.
However there is some static content in boott.cshtml (spans, input, a tag), they are not part of partial view _boott_table.cshtml and should not get re-rendered when ever we click on the options in _boott_table.cshtml.
How can we handle this situation ?
can somebody advise?/