How to use Bootstrap modal in Blazor client app? - razor

I am trying to show bootstrap modal then bind its buttons. But I cannot pass the first step showing the modal. I am using Blazor client template of .net core 3.1. I have a page named Modal.razor which contains the bootstrap modal I found from getbootstrap.com.
#if (Show)
{
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
}
#code {
[Parameter]
public bool Show { get; set; } = false;
}
An I called the modal in the index.razor file
#page "/"
<button #onclick="(()=>switchModal=!switchModal)">Switch Modal</button>
<Modal Show="switchModal"/>
#code{
bool switchModal = false;
}
You might say StateHasChanged should be called here. But even if I copy and paste the modal code in the index.razor, I won't see anything.

There is likely a better way to do this, but here's a working example to get you started:
Page:
#page "/modal-test"
<BlazorApp1.Components.Modal #ref="Modal"></BlazorApp1.Components.Modal>
<button #onclick="() => Modal.Open()">Open Modal</button>
#code {
private BlazorApp1.Components.Modal Modal { get; set; }
}
Component:
<div class="modal #ModalClass" tabindex="-1" role="dialog" style="display:#ModalDisplay">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal" #onclick="() => Close()">Close</button>
</div>
</div>
</div>
</div>
#if (ShowBackdrop)
{
<div class="modal-backdrop fade show"></div>
}
#code {
public Guid Guid = Guid.NewGuid();
public string ModalDisplay = "none;";
public string ModalClass = "";
public bool ShowBackdrop = false;
public void Open()
{
ModalDisplay = "block;";
ModalClass = "Show";
ShowBackdrop = true;
StateHasChanged();
}
public void Close()
{
ModalDisplay = "none";
ModalClass = "";
ShowBackdrop = false;
StateHasChanged();
}
}
Another option to go about this, would be to use JSInterop to call $('#modalId').modal()
You could have each version of the component have a unique id by doing something like this:
<div id="bootstrap-modal-#Guid" then use the saved ID to call .modal() with jQuery.

Building on Kyle's answer, this is my first experiment with Blazor: Making the modal dialog component take any markup or component.
Modal.razor
<div class="modal #modalClass" tabindex="-1" role="dialog" style="display:#modalDisplay; overflow-y: auto;">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">#Title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" #onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
#Body
</div>
<div class="modal-footer">
#Footer
</div>
</div>
</div>
</div>
#if (showBackdrop)
{
<div class="modal-backdrop fade show"></div>
}
#code {
[Parameter]
public RenderFragment Title { get; set; }
[Parameter]
public RenderFragment Body { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
private string modalDisplay = "none;";
private string modalClass = "";
private bool showBackdrop = false;
public void Open()
{
modalDisplay = "block;";
modalClass = "show";
showBackdrop = true;
}
public void Close()
{
modalDisplay = "none";
modalClass = "";
showBackdrop = false;
}
}
Index.razor
#page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<button class="btn btn-primary" #onclick="() => modal.Open()">Modal!</button>
<Modal #ref="modal">
<Title>This is a <em>Title!</em></Title>
<Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Omnes enim iucundum motum, quo sensus hilaretur.
<i>Quis istud possit, inquit, negare?</i>
<mark>Ego vero isti, inquam, permitto.</mark> Duo Reges: constructio interrete.
</p>
<FetchData />
<dl>
<dt><dfn>Stoici scilicet.</dfn></dt>
<dd>An hoc usque quaque, aliter in vita?</dd>
<dt><dfn>Erat enim Polemonis.</dfn></dt>
<dd>Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim.</dd>
</dl>
</Body>
<Footer>
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal" #onclick="() => modal.Close()">Close</button>
</Footer>
</Modal>
#code {
private Modal modal { get; set; }
}

Also building on Kyle's answer, you can sustain the bootstrap fade effect if you place a short delay between the display and class adjustments.
#code {
...
public async Task OpenModal()
{
ModalDisplay = "block;";
await Task.Delay(100);//Delay allows bootstrap to perform nice fade animation
ModalClass = "show";
StateHasChanged();
}
public async Task CloseModal()
{
ModalClass = "";
await Task.Delay(250);
ModalDisplay = "none;";
StateHasChanged();
}
}
I also applied the ModalClass and ModalDisplay variables to the backdrop element too
<div class="modal-backdrop fade #ModalClass" style="display: #ModalDisplay"></div>
I believe bootstrap can better identify the state change that triggers the animation this way

With Kyle solution my Dialog do not close when i click on the backdrop.
I saw that it is a problem of z-index: the modal div has a z-index of 1050, and the backdrop div has 1040, in this way i was not able to click my backdrop.
I have moved the backdrop inside the dialog div and added to the modal-dialog div z-index > 1040 (ES: 1055)
I also added data-dismiss="modal" #onclick="() => Close()" to the backdrop div and now it works as well as the "Close" button.
<div class="modal #ModalClass" tabindex="-1" role="dialog" style="display:#ModalDisplay">
<div class="modal-dialog" role="document" style="z-index:1055">
...
</div>
#if (ShowBackdrop)
{
<div class="modal-backdrop fade show" data-dismiss="modal" #onclick="() => Close()"></div>
}
</div>

for backdrop shadow only add fade class:
<div class="modal fade #ModalClass" tabindex="-1" role="dialog"
style="display:#ModalDisplay">

Kyle's components work well but does anyone know how to add draggable and resizable features to a bootstrap modal using the jqueryUi draggable()/resizeable() functions?
I have this link to a pure javascript solution: DRAG AND RESIZE BOOTSTRAP MODAL that essentially calls the resizeable and draggable functions on the modal divs
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript">
$('.modal-content').resizable({
//alsoResize: ".modal-dialog",
minHeight: 300,
minWidth: 300
});
$('.modal-dialog').draggable();
</script>
I've tried adding this script to my _Host.cshtml page but it has no effect. Any advice on how to do this would be gratefully received...
David
Updated with answer
The answer is to explicitly call a javascript function in the OnAfterRenderAsync override to apply the JQuery UI functions to the modal divs.
E.g.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await jsRuntime.InvokeVoidAsync("setModalDraggableAndResizable");
await base.OnAfterRenderAsync(firstRender);
}
where setModalDraggableAndResizable is a javascript function in the _Hosts.cshtml:
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript">
function setModalDraggableAndResizable() {
$('.modal-content').resizable({
//alsoResize: ".modal-dialog",
minHeight: 300,
minWidth: 300
});
$('.modal-dialog').draggable();
}
</script>
And the modal is now draggable and resizable...
Modal example image

Update: I've converted this answer into a service which can be found here.
I adjusted Kyles and grammophones answers to support our beloved Alert, Prompt, and Confirm from both C# and JavaScript. Tested in the lastest Blazor Server release with Bootstrap 5.
ProjectName.Components.Modal.razor
#using Microsoft.JSInterop
<div class="modal #ModalClass" tabindex="-1" role="dialog" style="display:#ModalDisplay; overflow-y: auto;">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title w-100 text-center" style="padding-left:31px">#Title</h5>
<button type="button" class="close border-0 bg-white" data-dismiss="modal" aria-label="Close" #onclick="() => Close(true)">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body mx-auto text-center">
#Body
#if (MType == ModalType.Prompt){
<input type="text" class="form-control text-center my-2" #bind-value="PromptValue" style="max-width:400px"></input>
}
</div>
<div class="modal-footer justify-content-center">
#if (MType == ModalType.Prompt || MType == ModalType.Confirm)
{
<button type="button" class="btn btn-secondary" data-dismiss="modal" #onclick="() => Close(false)">OK</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal" #onclick="() => Close(true)">Cancel</button>
}
else
{
<button type="button" class="btn btn-secondary" data-dismiss="modal" #onclick="() => Close(false)">Close</button>
}
</div>
</div>
</div>
</div>
#if (ShowBackdrop)
{
<div class="modal-backdrop fade show"></div>
}
#code {
[Inject] IJSRuntime JS { get; set; }
public enum ModalType
{
Alert,
Prompt,
Confirm
}
/// <summary>
/// (Optional) We can setup an instance of this .net object to call directly from JavaScript. See JavaScript Usage section.
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
JS.InvokeVoidAsync("MODAL.SetDotnetReference", DotNetObjectReference.Create(this));
}
private string Title { get; set; }
private string Body { get; set; }
public Guid Guid = Guid.NewGuid();
public string ModalDisplay = "none;";
public string ModalClass = "";
public bool ShowBackdrop = false;
private string PromptValue { get; set; }
private bool ConfirmValue { get; set; }
private ModalType MType { get; set; }
private List<string> MsgIds = new List<string>();
[JSInvokable("Show")]
public async Task<dynamic> Show(ModalType mType, string title, string body)
{
// The JavaScript call MODAL.DotNetReference.invokeMethodAsync is non-blocking
// This means multiple calls to show the modal using invokeMethodAsync will only show the modal once.
// We can solve this by making sure each message waits in line.
string msgId = Guid.NewGuid().ToString();
if (!MsgIds.Contains(msgId))
MsgIds.Add(msgId);
// If multiple messages are being processed, wait for this msgs turn.
while (MsgIds.Count > 1 && MsgIds.IndexOf(msgId) != 0)
await Task.Delay(250);
Title = title;
Body = body;
ModalDisplay = "block;";
ModalClass = "Show";
MType = mType;
ShowBackdrop = true;
StateHasChanged();
while (ShowBackdrop)
await Task.Delay(250);
switch(mType)
{
default:
case ModalType.Alert:
MsgIds.Remove(msgId);
return string.Empty;
case ModalType.Confirm:
bool confirmResponse = ConfirmValue;
MsgIds.Remove(msgId);
return confirmResponse;
case ModalType.Prompt:
string promptResponse = PromptValue;
MsgIds.Remove(msgId);
return promptResponse;
}
}
private void Close(bool isCancel)
{
// Determine returned values.
PromptValue = isCancel ? string.Empty : PromptValue;
ConfirmValue = isCancel ? false : true;
ModalDisplay = "none";
ModalClass = "";
ShowBackdrop = false;
StateHasChanged();
}
}
Markup Usage
<Modal #ref="Modal"></Modal>
<button #onclick='() => Modal.Show(Modal.ModalType.Alert, "Title goes here","Body goes here")'>Open Modal</button>
Code Usage
if (await Modal.Show(Modal.ModalType.Confirm,"Save Settings", "Are you sure you want to save settings?"))
{
string fileName = await Modal.Show(Modal.ModalType.Prompt, "File Name", "Please enter a filename");
if (!string.IsNullOrEmpty(fileName))
await Modal.Show(Modal.ModalType.Alert, "File Saved", $"File Saved as {fileName}");
}
JavaScript Usage
With promise support we can get a response from Prompt and Confirm right from JavaScript. To avoid declaring our Modal as static we need to setup a DotNetReference.
// Defined somewhere globally
var MODAL = {};
MODAL.DotNetReference = null;
MODAL.SetDotnetReference = function (pDotNetReference) {
MODAL.DotNetReference = pDotNetReference;
};
MODAL.MType = {
Alert: 0,
Prompt:1,
Confirm: 2,
};
// Called from wherever
MODAL.DotNetReference.invokeMethodAsync('Show', MODAL.MType.Prompt, `Title goes here`, `Body goes here`)
.then(data => {
console.log(`Prompt Response`, data);
});
JavaScript Note: Polyfil recommended for promise support in older browsers

As an alternative you can use Bootstrap Blazor which is an open-source and very nice implementation of bootstrap integrated with blazor.

To include fade in and slide down effects, it would be needed simply to add some CSS. For example, this could be as I did in my code.
My modal blazor component is the following:
<div class="modal fade show"
id="trxModal"
style="display: #(IsDisplayed ? "block" : "none"); background-color: rgba(10,10,10,.8);"
aria-modal="true"
role="dialog">
<div class="modal-dialog #(IsDisplayed ? "fadeInAnimation" : "")">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">#Title</h4>
<button type="button" class="trx-btn btn-text" #onclick="onCancelClick">×</button>
</div>
<div class="modal-body">
<p>#Text</p>
</div>
<div class="modal-footer">
#if (CancelText != null)
{
<button type="button" class="trx-btn trx-btn-cancel" #onclick="onCancelClick">
#CancelText
</button>
}
#if (OkText != null)
{
<button type="button" class="trx-btn trx-btn-primary" #onclick="onOkClick">
#OkText
</button>
}
</div>
</div>
</div>
</div>
Please note "fadeInAnimation" class. This will be added to the div element when "isDisplayed" is true. In CSS, I wrote the following code to implement fade in and slide down effects:
#keyframes fade-in-slide-down {
from {
opacity: 0.1;
margin-top: 0;
}
to {
opacity: 1;
margin-top: 75px;
}
}
.fadeInAnimation {
animation-name: fade-in-slide-down;
animation-duration: 500ms;
animation-fill-mode: forwards;
}
I hope this will work for you.

Related

How can I change button color when clicked

To be brief, i have two buttons that have the same class, one of them have an attribute data-toggle='modal'. I use this attribute to detect the button.
I need to change the color of the button which does not have an attribute data-toggle, I can't target it by using the className, because they both have the same.
In the js click function, I use a ternary condition to say: if the button has an attribute 'data-toggle' then do nothing, else removeClass btn--ghost and addClass btn--plain. But it doesn't work.
PS: the btn--plain is the class name used to give the button a bgcolor instead of btn--ghost.
This is the js :
if ($('.o-block-global .btn--link').length) {
$('.btn--primary').on("click", function (e) {
console.log("iiiiii", $(this));
$(this).attr("data-toggle" ? "" : $(this).removeClass("btn--ghost").addClass("btn--plain"));
});
}
This is the HTML :
<div class="o-block-global__links link_choice ">
<button type="button" class="btn btn--ghost btn--primary">
OUI
</button>
<button type="button" class="btn btn--ghost btn--primary" data-toggle="modal" data-target="#modalChoice2">
NON
</button>
</div>
You can check if the attr value is undefined or not using (typeof $(this).attr("data-toggle")) !== 'undefined'
Demo Code :
$('.btn--primary').on("click", function(e) {
//check if the attr type is not undefined
(typeof $(this).attr("data-toggle")) !== 'undefined' ? "" : $(this).removeClass("btn--ghost").addClass("btn--plain");
});
.btn--ghost {
color: red;
}
.btn--plain {
color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class="o-block-global__links link_choice ">
<button type="button" class="btn btn--ghost btn--primary">
OUI
</button>
<button type="button" class="btn btn--ghost btn--primary" data-toggle="modal" data-target="#modalChoice2">
NON
</button>
To do that you use the :active selector. HTML:
<button>Click Me!</button>
button{
background:green;
}
button:hover{
background:lightgreen;
}
button:active{
background:red;
}

Adding Modal dialog to the Header file in MVC project does not work

I implemented a modal popup as a part of the header.cshtml file in MVC project. the issue is that it does not show the dialog box (not working)
The only way I was able to make work is when I put the both the button and the modal container in the header file.
The modal body is a long text and it should not be a part of the header file.
I tried several tutorials, read modal documentation, and applied fixes from stackoverflow nothing worked for me.
I must be missing something very simple and trivial I just can't see it.
Below is the code in the Roles.cshtml file where the modal container is created and the text is written
#{
Layout = "~/Views/Shared/SubLayout.cshtml";
}
#{
ViewBag.Title = "Roles";
}
<div class="container">
<div class="styleguide-spacer-modals"></div>
</div>
<div class="modal" id="modal-active" aria-hidden="true">
<div class="modal__overlay bg-modal" tabindex="-1"
data-micromodal-close>
<div class="modal__container" role="dialog" aria-modal="true"
aria-labelledby="modal-title-1">
<header class="modal__header">
<h1 class="modal__title h2" id="modal-title-1">
<h2>Roles</h2>
<br />
</h1>
</header>
<main class="modal__content">
BLABLA......
</main>
<footer class="modal__footer">
<button class="button button-primary"
aria-label="submit">
close
</button><button class="button button-secondary" data-
micromodal-close
aria-label="closing">
close
</button>
</footer>
</div>
</div>
</div>
Then the Header.cshtml file look like this: where I added "btnTrigger" and AJAX script to call and show the modal
#model TR.Service.Web.ViewModels
<header class="header" role="banner">
<!--1A: Portal header -->
<div class="portal-header">
<div class="container portal-header-inner">
<button class="button button-tertiary button-menu-open js-menu-
open ml-auto" aria-haspopup="menu" title="mobil menu">Menu</button>
<a href="#" class="button button-secondary" role="button">
login
</a>
</div>
<button class="button button-primary" id="btnTrigger"
data-micromodal-trigger="modal-passive">
Read me
</button>
<div id="divContainer"></div>
</div>
<div class="solution-header">
blabla........
</div>
</header>
#section scripts
{
<script>
$(function () {
$('#btnTrigger').unbind();
$('#btnTrigger').on('click', function () {
$.ajax({
url: '#Url.Action("Betingelser", "Rolles")',
type: 'POST',
data: { },
success: function (arr) {
$('#divContainer').html(arr); //Load your HTML to DivContainer
$('#exampleModal').modal('show'); //Once loaded, show the modal
},
error: function (err) {
console.log(err);
}
});
});
});
</script>
}
Then in my Home controller I have the actionResult that should return the
partial view
public ActionResult Roles()
{
return PartialView("Roles");
}
I can't see why this is not working. Please help.
Pupop should appear when click on the button with id =btnTrigger
Here is a working code
First since the Header.cshtml is part of the SubLayout.cshtml, move the #section scripts to the Roles.cshtml like below, you cna also put it in your Layout but without the #section scripts tag. I did'nt use the modal you had in your question. But this is a working modal
#{
Layout = "~/Views/Shared/SubLayout.cshtml";
}
#{
ViewBag.Title = "Roles";
}
<div class="container">
<div class="styleguide-spacer-modals"></div>
</div>
<div id="exampleModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Roles</h4>
</div>
<div class="modal-body">
<p>Content</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
#section scripts
{
<script>
$(function () {
$('#btnTrigger').unbind();
$('#btnTrigger').on('click', function () {
$.ajax({
url: '#Url.Action("Betingelser", "Rolles")',
type: 'POST',
data: { },
success: function (arr) {
$('#divContainer').html(arr); //Load your HTML to DivContainer
$('#exampleModal').modal('show'); //Once loaded, show the modal
},
error: function (err) {
console.log(err);
}
});
});
});
</script>
}
Then your Header.cshtml now looks like this
<header class="header" role="banner">
<!--1A: Portal header -->
<div class="portal-header">
<div class="container portal-header-inner">
<button class="button button-tertiary button-menu-open js-menu-
open ml-auto" aria-haspopup="menu" title="mobil menu">
Menu
</button>
<a href="#" class="button button-secondary" role="button">
login
</a>
</div>
<button class="button button-primary" id="btnTrigger"
data-micromodal-trigger="modal-passive">
Read me
</button>
<div id="divContainer"></div>
</div>
<div class="solution-header">
</div>
</header>
Also change the Home controller method to
public ActionResult Roles()
{
return View();
}

Autodesk Forge Extension

So I am following this tutorial to extend the autodesk forge viewer. I have compelted all of the steps and no button is showing, I assume this is due to an error with the loading.
https://forge.autodesk.com/blog/extension-skeleton-toolbar-docking-panel
I have also tried this tutorial, with the same issue:
http://learnforge.autodesk.io/#/viewer/extensions/selection?id=conclusion
My issue is I am not getting an error, the extension just isn't showing... does anyone know why?
I'm assuming theres an error in either the viewer or the index.
Below is my code: (index & forge viewer)
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Autodesk Forge Tutorial</title>
<meta charset="utf-8" />
<!-- Common packages: jQuery, Bootstrap, jsTree -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<script src="/js/MyAwesomeExtension.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" />
<!-- Autodesk Forge Viewer files -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/style.min.css?v=v6.0" type="text/css">
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js?v=v6.0"></script>
<!-- this project files -->
<link href="css/main.css" rel="stylesheet" />
<script src="js/ForgeTree.js"></script>
<script src="js/ForgeViewer.js"></script>
</head>
<body>
<!-- Fixed navbar by Bootstrap: https://getbootstrap.com/examples/navbar-fixed-top/ -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<ul class="nav navbar-nav left">
<li>
<a href="http://developer.autodesk.com" target="_blank">
<img alt="Autodesk Forge" src="//developer.static.autodesk.com/images/logo_forge-2-line.png" height="20">
</a>
</li>
</ul>
</div>
</nav>
<!-- End of navbar -->
<div class="container-fluid fill">
<div class="row fill">
<div class="col-sm-4 fill">
<div class="panel panel-default fill">
<div class="panel-heading" data-toggle="tooltip">
Buckets & Objects
<span id="refreshBuckets" class="glyphicon glyphicon-refresh" style="cursor: pointer"></span>
<button class="btn btn-xs btn-info" style="float: right" id="showFormCreateBucket" data-toggle="modal" data-target="#createBucketModal">
<span class="glyphicon glyphicon-folder-close"></span> New bucket
</button>
</div>
<div id="appBuckets">
tree here
</div>
</div>
</div>
<div class="col-sm-8 fill">
<div id="forgeViewer"></div>
</div>
</div>
</div>
<form id="uploadFile" method='post' enctype="multipart/form-data">
<input id="hiddenUploadField" type="file" name="theFile" style="visibility:hidden" />
</form>
<!-- Modal Create Bucket -->
<div class="modal fade" id="createBucketModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Cancel">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Create new bucket</h4>
</div>
<div class="modal-body">
<input type="text" id="newBucketKey" class="form-control"> For demonstration purposes, objects (files) are
NOT automatically translated. After you upload, right click on
the object and select "Translate".
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createNewBucket">Go ahead, create the bucket</button>
</div>
</div>
</div>
</div>
</body>
</html>
ForgeViewer.js:
var viewerApp;
function launchViewer(urn) {
var options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken
};
var documentId = 'urn:' + urn;
Autodesk.Viewing.Initializer(options, function onInitialized() {
viewerApp = new Autodesk.Viewing.ViewingApplication('forgeViewer');
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.Private.GuiViewer3D, { extensions: ['MyAwesomeExtension'] });
viewerApp.loadDocument(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
});
}
function onDocumentLoadSuccess(doc) {
// We could still make use of Document.getSubItemsWithProperties()
// However, when using a ViewingApplication, we have access to the **bubble** attribute,
// which references the root node of a graph that wraps each object from the Manifest JSON.
var viewables = viewerApp.bubble.search({ 'type': 'geometry' });
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Choose any of the available viewables
viewerApp.selectItem(viewables[0].data, onItemLoadSuccess, onItemLoadFail);
}
function onDocumentLoadFailure(viewerErrorCode) {
console.error('onDocumentLoadFailure() - errorCode:' + viewerErrorCode);
}
function onItemLoadSuccess(viewer, item) {
// item loaded, any custom action?
}
function onItemLoadFail(errorCode) {
console.error('onItemLoadFail() - errorCode:' + errorCode);
}
function getForgeToken(callback) {
jQuery.ajax({
url: '/api/forge/oauth/token',
success: function (res) {
callback(res.access_token, res.expires_in)
}
});
}
MyAwesomeExtension.js:
// *******************************************
// My Awesome Extension
// *******************************************
function MyAwesomeExtension(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
this.panel = null;
}
MyAwesomeExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
MyAwesomeExtension.prototype.constructor = MyAwesomeExtension;
MyAwesomeExtension.prototype.load = function () {
if (this.viewer.toolbar) {
// Toolbar is already available, create the UI
this.createUI();
} else {
// Toolbar hasn't been created yet, wait until we get notification of its creation
this.onToolbarCreatedBinded = this.onToolbarCreated.bind(this);
this.viewer.addEventListener(av.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
}
return true;
};
MyAwesomeExtension.prototype.onToolbarCreated = function () {
this.viewer.removeEventListener(av.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
this.onToolbarCreatedBinded = null;
this.createUI();
};
MyAwesomeExtension.prototype.createUI = function () {
var viewer = this.viewer;
var panel = this.panel;
// button to show the docking panel
var toolbarButtonShowDockingPanel = new Autodesk.Viewing.UI.Button('showMyAwesomePanel');
toolbarButtonShowDockingPanel.onClick = function (e) {
// if null, create it
if (panel == null) {
panel = new MyAwesomePanel(viewer, viewer.container,
'awesomeExtensionPanel', 'My Awesome Extension');
}
// show/hide docking panel
panel.setVisible(!panel.isVisible());
};
// myAwesomeToolbarButton CSS class should be defined on your .css file
// you may include icons, below is a sample class:
/*
.myAwesomeToolbarButton {
background-image: url(/img/myAwesomeIcon.png);
background-size: 24px;
background-repeat: no-repeat;
background-position: center;
}*/
toolbarButtonShowDockingPanel.addClass('myAwesomeToolbarButton');
toolbarButtonShowDockingPanel.setToolTip('My Awesome extension');
// SubToolbar
this.subToolbar = new Autodesk.Viewing.UI.ControlGroup('MyAwesomeAppToolbar');
this.subToolbar.addControl(toolbarButtonShowDockingPanel);
viewer.toolbar.addControl(this.subToolbar);
};
MyAwesomeExtension.prototype.unload = function () {
this.viewer.toolbar.removeControl(this.subToolbar);
return true;
};
Autodesk.Viewing.theExtensionManager.registerExtension('MyAwesomeExtension', MyAwesomeExtension);
MyAwesomePanel:
// *******************************************
// My Awesome (Docking) Panel
// *******************************************
function MyAwesomePanel(viewer, container, id, title, options) {
this.viewer = viewer;
Autodesk.Viewing.UI.DockingPanel.call(this, container, id, title, options);
// the style of the docking panel
// use this built-in style to support Themes on Viewer 4+
this.container.classList.add('docking-panel-container-solid-color-a');
this.container.style.top = "10px";
this.container.style.left = "10px";
this.container.style.width = "auto";
this.container.style.height = "auto";
this.container.style.resize = "auto";
// this is where we should place the content of our panel
var div = document.createElement('div');
div.style.margin = '20px';
div.innerText = "My content here";
this.container.appendChild(div);
// and may also append child elements...
}
MyAwesomePanel.prototype = Object.create(Autodesk.Viewing.UI.DockingPanel.prototype);
MyAwesomePanel.prototype.constructor = MyAwesomePanel;
Yes, you are missing the CSS for the Buttons and also the reference to the JS files pertaining to the extensions in your HTML file.
<script src="your_folder/MyExtensionFileName.js"></script>
http://learnforge.autodesk.io/#/viewer/extensions/selection?id=toolbar-css
Check this for the CSS of your extension buttons.

How can I route components in another component without changing url?

Let's assume that I have Angular 5 project with routings. For instance /home, /landing-page, etc. Moreover, let's assume that in my landing page with url - localhost:4200. I want to create login panel. I have two fields - username and password, one button sign in and two other buttons forgot password? and Don't have an account?. My problem is that when user will click Forgot password? or Don't have an account? he will not be routed to another page with url like localhost:4200/sign-up but he will stay at the same page with url localhost:4200 and fields username, password, sign in, forgot password? and Don't have an account? will disappear and in their place will be displayed fields associated with registration. I am not sure whether you know what I mean. The good example what I wanna to achieve is https://www.instagram.com. No matter whether you click Sign up or Log in you are still on the same url and only one component changes. Do you know how can I achieve this? I am not sure whether I should use routes or maybe another way is more optimal to do this? Thanks in advance.
My code looks in this way. I added only the most important code from selected files.
index.html:
</head>
<body>
<app-root></app-root>
</body>
</html>
app.component.html:
<app-navbar></app-navbar>
<router-outlet></router-outlet>
<app-footer *ngIf="removeFooter()"></app-footer>
At the moment my home.component looks in this way:
home.component.html:
<div *ngIf="isSignIn()">
<app-sign-in></app-sign-in>
</div>
<div *ngIf="isSignUp()">
<app-sign-up></app-sign-up>
</div>
<div *ngIf="isForgotPassword()">
<app-forgot-password></app-forgot-password>
</div>
home.component.ts:
constructor() {
this.signin = true;
this.signup = false;
this.forgot = false;
}
isSignUp() {
if (this.signup === true) {
return true;
}
else {
return false;
}
}
isSignIn() {
if (this.signin === true) {
return true;
}
else {
return false;
}
}
isForgotPassword() {
if (this.forgot === true) {
return true;
}
else {
return false;
}
}
sign-in.component.html:
<div class="content-center">
<div class="container">
<div class="title-brand">
<div align="center">
<input style="background-color: black; border-color: white; color:white; width: 270px" type="text" value="" class="form-control" placeholder="USERNAME">
<br>
<input style="background-color: black; border-color: white; color:white; width: 270px" type="text" value="" class="form-control" placeholder="PASSWORD">
<div class="row">
<div class="col-md-8">
<br>
<button style="background-color: black; border-color: white; color:white; width: 270px" type="button" class="btn btn-danger">Log in</button>
</div>
</div>
<br>
<h6 style = "color: white" [routerLink]="['/main']" skipLocationChange class=pointer>Forgot Password?</h6>
<h6 style="color: white" [routerLink]="['/sign-up']" class=pointer>Don't have an account?</h6>
</div>
</div>
</div>
</div>
UPDATE
I added source code of sign-in.component.html to the question. Can you show me how I can switch the component sign-in.component.html after clicking Forgot Password? or Do not have an account? to the component forgot.component.html or sign-up.component.html
Use skipLocationChange from the route NavigationExtras. E.g.
<h6 style = "color: white" [routerLink]="['/main']" skipLocationChange>Forgot Password?</h6>
<h6 style="color: white" [routerLink]="['/sign-up']" skipLocationChange>Don't have an account?</h6>
Here is an example of using a boolean and buttons to allow you to switch between different components on a single page:
stackblitz example
This example could be improved but I hope it shows you how to easily swap the visible component.
You can view the code at this link
EDIT
You need to remove the responsibility of what component to show to a container component (i.e. a parent component). In the updated stackblitz example I've made the HomeComponent responsible for showing the correct component. This means the SignUp/SignIn/ForgotPassword components have no responsibility for switching between each other - that is the job for the HomeComponent (or whichever component you want to use for that job).
Hope that helps
home.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-home',
template:
`<div *ngIf="signin">
<app-sign-in></app-sign-in>
<button (click)="showSignUp()">Sign up</button>
<button (click)="showForgot()">Forgot password</button>
</div>
<div *ngIf="signup">
<app-sign-up></app-sign-up>
<button (click)="showSignIn()">Sign in</button>
<button (click)="showForgot()">Forgot password</button>
</div>
<div *ngIf="forgot">
<app-forgot-password></app-forgot-password>
<button (click)="showSignUp()">Sign up</button>
<button (click)="showSignIn()">Sign in</button>
</div>`,
styles: [`h1 { font-family: Lato; }`]
})
export class HomeComponent {
public signup: boolean = true;
public signin: boolean = false;
public forgot: boolean = false;
constructor() {}
public showSignIn() {
this.signin = true;
this.signup = this.forgot = false;
}
public showSignUp() {
this.signup = true;
this.signin = this.forgot = false;
}
public showForgot() {
this.forgot = true;
this.signin = this.signup = false;
}
}

Full Calendar loading before my Navigation Bar

Having a weird problem with my C# ASP.net application using FullCalendar. When I load the FullCalendar, my nav bar isn't responding properly. When the calendar is loaded, I can't access the drop down list.
I think it has something to do with my FullCalendar loading in before my nav bar but I don't know. Here's my razor page where I carry out my Calendar.
<div id="calendar"></div>
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"><span id="eventTitle"></span></h4>
</div>
<div class="modal-body">
<button id="btnDelete" class="btn btn-default btn-sm pull-right">
<span class="glyphicon glyphicon-remove"></span> Remove
</button>
<button id="btnEdit" class="btn btn-default btn-sm pull-right" style="margin-right:5px;">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
<p id="pDetails"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<link href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.print.css" rel="stylesheet" media="print" />
#section Scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js"></script>
<script>
$(document).ready(function () {
var events = [];
$.ajax({
type: "GET",
url: "/Appointments/GetEvents",
success: function (data) {
$.each(data, function (i, v) {
events.push({
details: v.DetailsOfAppointment,
date: moment(v.DateOfAppointment),
room: v.RoomType,
confirmed: v.Confirmed,
colour: v.ThemeColour,
church: v.Church.Name,
parishAdminName: v.Admins.AdministratorName,
parishAdminUser: v.Admins.AdminUsername,
parishAdminId: v.Admins.AdministratorId,
fee: v.Fee,
id: v.AppointmentId
});
})
GenerateCalender(events);
},
error: function (error) {
alert("failed");
console.log(error);
}
})
function GenerateCalender(events) {
$('#calendar').fullCalendar('destroy');
$('#calendar').fullCalendar({
contentHeight: 500,
defaultDate: new Date(),
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay'
},
timeFormat: 'HH:mm',
eventLimit: true,
eventColor: events.ThemeColour,
events: events,
eventRender: function (event, element) {
if (event.fee == null) {
if (event.confirmed == false) {
element.css('background-color', '#FF0000');
element.css('border-color', '#FF0000');
}
else {
element.css('background-color', '#008000');
element.css('border-color', '#008000');
}
}
else
{
element.css('background-color', '#0000FF');
element.css('border-color', '#0000FF');
}
},
eventClick: function (calEvent, jsEvent, view) {
$('#myModal #details').text(calEvent.details);
var $details = $('<div/>');
if (calEvent.fee != null) {
$details.append($('<p/>').html('<b>Date of Ceremony : </b>' + calEvent.date.format("DD-MMM-YYYY HH:mm a")));
}
else {
$details.append($('<p/>').html('<b>Date of Appointment : </b>' + calEvent.date.format("DD-MMM-YYYY HH:mm a")));
}
if (calEvent.end != null) {
$details.append($('<p/>').html('<b>End:</b>' + calEvent.end.format("DD-MMM-YYYY HH:mm a")));
}
$details.append($('<p/>').html('<b>Details : </b>' + calEvent.details));
$details.append($('<p/>').html('<b>Church Name : </b>' + calEvent.church));
if (calEvent.fee == null) {
if (calEvent.room != null) {
$details.append($('<p/>').html('<b>Room : </b>' + calEvent.room));
}
else {
$details.append($('<p/>').html('<b>Room Not Confirmed'));
}
}
$details.append($('<p/>').html('<b>Parish Admin : </b>' + calEvent.parishAdminName));
if (calEvent.confirmed == true)
{
$details.append($('<p/>').html('<b>Status : Confirmed </b>'));
}
else
{
$details.append($('<p/>').html('<b>Status : Not Confirmed </b>'));
}
$('#myModal #pDetails').empty().html($details);
$('#myModal').modal();
}
})
}
})
</script>
}
It seems like a z-index issue. Make sure your element is positioned (e.g relative) and add an z-index property like: z-index: 99;
More about it in the MDN documentation: