Angular code is not working in bootstrap modal - asp.net-mvc-4

I am using angularJs, twitter bootstrap with asp.net mvc4. I didn't use angular-strap. I am trying to load a partial view in bootstrap modal. The partial view contains angular code.
Here is my code where I am calling the bootstrap modal:
#model IEnumerable<IMS.Domain.Model.GetAllRequisitions_Result>
#{
ViewBag.Title = "Requisition List";
Layout = "~/Views/Shared/MasterPage.cshtml";
}
Launch demo modal
<div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-body">
</div>
</div>
<script type="text/javascript">
function loadCreateForm() {
var content = '#Url.Action("Create", "Requisition", new { area = "IMS" })';
$('div.modal-body').load(content);
}
</script>
Here is my code for 'Create' action of 'Requisition' controller:
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div ng-app="">
<label>Sample: <input type="text" ng-model="sample" /></label>
<br />
<label>Show Value: {{sample}}</label>
</div>
<div class="ContainerRowButton">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
For ng-model="sample" when I am trying to show data by {{sample}} it stays {{sample}}. It's like my 'Create' action is not getting angularJs.
I've rendered all necessary scrips in the 'MasterPage.cshtml'. My MasterPage.cshtml contains:
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/others")
And here is the code of my 'Bundle.config.cs' file:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/others").Include(
"~/Content/bootstrap/js/bootstrap.js",
"~/Scripts/jquery.dataTables.js",
"~/Scripts/dataTables_bootstrap.js",
"~/Scripts/bootstrap-dropdown.js",
"~/Scripts/jquery.dataTables.columnFilter.js",
"~/Scripts/Custom/CustomScript.js",
"~/Scripts/jquery.validationEngine-en.js",
"~/Scripts/jquery.validationEngine.js",
"~/Scripts/angular.js"
));
What wrong am I doing? Need help ...

Adding to what #Muctadir said, you need to tell angular to check your modal partial for angular elements. You do that by calling $compile before injecting the partial into the DOM. The problem is that you have to be able to get access to the $compile service and you can't do that inside of your loadCreateForm(), you can only do that from an angular module. So, you need to organize your code in such a way that you can get what you need. Here's my suggestion:
Create a directive to handle your modal and its contents:
.directive('myModal', function($compile){
return {
replace:false,
link: function(scope, element, attrs){
var content = '#Url.Action("Create", "Requisition", new { area = "IMS" })';
elem.load(content, null, function(){
$compile(elem.contents())(scope);
});
}
};
});
That will load the content and then compile the contents using the current scope, but only after the content has been loaded from the server. Hope that helps.

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 .

data-ajax-update and data-ajax-mode="replace" not working in dotnet core jquery unobtrusive ajax

I have an anchor which should replace a grid with a partial view .
<a class="btn btn-primary"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="content"
data-ajax-url="#Url.Action("add","user")"> Create User </a>
<div class="row table-area">
<div class="col-md-12" id="content">
#Html.AjaxGrid(Url.Action("results", "user"))
</div>
</div>
I see it calls the user action with partial view but it never updates the section with id="content".
Here is my controller method -
[Route("add")]
public IActionResult AddUser()
{
return PartialView("Partials/AddUser",new RegisterViewModel());
}
Ideally it should replace the grid content with the partial view altogether but it is not replacing . The response status is 200 and I can see that the contents are being returned in response . Anybody has any idea what is the issue here ?
Change data-ajax-update="content" to data-ajax-update="#content"
Rather than using data-ajax-url, use the asp-controller and asp-action and the #content should work.
<a class="btn btn-primary" asp-controller="user" asp-action="add"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="#content">Create User</a>

Materialize css modal not showing

I've got a following code:
<?php if(UserModel::getInstance()->isLoggedIn()) { ?>
<a class="modal-trigger waves-effect waves-light red btn right" href="#modalNewTopic">+ Add New Topic</a>
<?php } ?>
<?php if(UserModel::getInstance()->isLoggedIn()) { ?>
<div id="modalNewTopic" data-ignore="true" class="modal">
<div class="modal-content">
<h4>Add New Topic</h4>
</div>
<div class="modal-footer">
asdf
</div>
</div>
When I click on a button nothing is showed up. No errors were triggered in javascript console. What could be the problem?
You need to initialise the modal plugin before you can use it. Add this to your JS file:
$(document).ready(function() {
$('.modal').modal();
});

HtmlBeginCollectionItem Get Current Item

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
});