I'm following this angular recipes page for adding a modal dialog to my ui. It suggests the following markup, which I've added to one of my views.
... html for my view is here ...
<button class="btn" ng-click="open()">Open Modal</button>
<div modal="showModal" close="cancel()">
<div class="modal-header">
<h4>Modal Dialog</h4>
... etc, from the recipe doc
</div>
What I want to see is my view, plus an "Open Modal" button on the bottom and nothing else. What I see instead is the button and the content of the modal already visible on the page.
The very next words in the recipe doc are:
Note that even though we don’t specify it explicitly the modal dialog
is hidden initially via the modal attribute. The controller only
handles the button click and the showModal value used by the modal
attribute.
Why is my modal mark up initially visible on the page? I think I have installed angular-ui properly... in my index.html:
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
And in my app JS:
angular.module('MonteAdmin', [
...
'ui.bootstrap',
...
])
That recipes page is likely out of date. At the time of the writing it might have been possible to pass a variable showModal to the modal directive to reveal or hide it. In your controller, you would have been able to show the modal by setting the scope variable showModal to true or false:
$scope.showModal = false;
$scope.open = function() {
$scope.showModal = true;
}
The current version does not work that way. You will have much better experience if you read the official documentation for the library at Angular UI Bootstrap
If you are using the latest version of the library, the directive is no longer modal but uib-modal. In addition, you have a bit more work to do to implement your modal.
Modal markup should be in a script tag, with a type set to text/ng-template as per the official example:
<script type="text/ng-template" id="stackedModal.html">
<div class="modal-header">
<h3 class="modal-title" id="modal-title-{{name}}">The {{name}} modal!</h3>
</div>
<div class="modal-body" id="modal-body-{{name}}">
Having multiple modals open at once is probably bad UX but it's technically possible.
</div>
</script>
To actually open the modal, your button click should trigger the following example function:
var modalInstance = $uibModal.open({
animation: $ctrl.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
controllerAs: '$ctrl',
size: size,
appendTo: parentElem,
resolve: {
items: function () {
return $ctrl.items;
}
}
});
You must also define a controller for the modal, itself:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($uibModalInstance, items) {
var $ctrl = this;
$ctrl.items = items;
$ctrl.selected = {
item: $ctrl.items[0]
};
$ctrl.ok = function () {
$uibModalInstance.close($ctrl.selected.item);
};
$ctrl.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
All of this code is found on the official documentation for Angular UI Bootstrap
Related
I'm trying to figure out how to link a button to open a new HTML component but no matter which method I've tried I cannot get it to work
First I tried a JS Function:
function openNext(){
window.location = '../nextpage.html';
}
on this button code:
<div class="content">
<button type="button" ng-click="openNext()" class="nextBtn mat-raised-button"> Next!</button>
</div>
But that didn't do anything, so tried a simple href link, still nothing.
So I thought it was something perhaps with the routing
Notice that you are only asking to load a component on the click of a button. Nothing simpler:
<div class="content">
<button type="button" ng-click="openNext()" class="nextBtn mat-raised-button"> Next!</button>
<the-html-component-you-want-to-open
ng-if="isMyComponentOpen == true"
></the-html-component-you-want-to-open>
</div>
In your controller:
$scope.isMyComponentOpen = false;
$scope.openNext = function() {
$scope.isMyComponentOpen = true;
}
On the other hand, if you are looking into switching pages in your application, or loading external dialogs/modals containing other components, then you are asking the wrong question.
In my index.html, I have the following code:
<head>
...
<script type="text/javascript"
src="https://mysrc.com/something.js&collectorId=f8n0soi9"
</script>
<script type="text/javascript">window.ATL_JQ_PAGE_PROPS = {
'f8n0soi9': {
"triggerFunction": function (showCollectorDialog) {
document.getElementById("button1").addEventListener("click", function (e) {
e.preventDefault();
showCollectorDialog();
});
}
}
};</script>
</head>
and then in my myComponent.tsx file, I have a button somewhere on the page that looks like this:
function myComponent() {
return (
...
<button id="button1">
Button Text
</button>
...
);
}
export default myComponent;
It's probably also important to note that I'm using react-routing to navigate between various components, and the button above is just in one of those components
So the issue seems to be that if you load in to a site on any other webpage and later navigate to the page with the button on it, the button won't work unless you refresh that specific page, since presumably it wasn't on the first page loaded and perhaps no element with id "button1" was found to bind the event listener to. React-routing doesn't refresh the page by default when navigating through the site.
Putting the code I have in the index.html file into the myComponent.tsx file also does not work, since (I think) the index.html file allows for any raw html but the tsx file isn't truly html? Is there perhaps a way to define this as a function in the index.html file and then assign an onClick event to the button? Thank you all in advance!
Yes, it should be possible to bind the function that shows the dialog for later use and then use the recommended React event on the button.
In this example bound globally to the window object for simplicity:
"triggerFunction": function (showCollectorDialog) {
window.showCollectorDialog = showCollectorDialog;
}
// ...
<button id="button1" onClick={e => {e.preventDefault(); window.showCollectorDialog();}}>
If you run into Type errors with that, try (on the button):
=> {...; (window as any).showCollectorDialog();}
Or declare only the property (possible be more specific than any here if the signature is known):
declare global {
interface Window { showCollectorDialog: any; }
}
It should be fine to have this just somewhere in your TS source, your index.html without TS should just assign it.
I am installing this modal dialog on a client's website, with practically no modifications.
However, I could not find how to make the modal dialog display on page load in the documentation.
Right now it just says:
<!-- Link to open the modal -->
<p>Open Modal</p>
But I am sure there is a way to make it just open on load.
I am using a hosted version of jQuery (jquery.min.js, jquery.modal.min.js) so I'm not trying to add/edit code in the JS file.
http://github.com/kylefox/jquery-modal Method 2: Manually:
$(function() {
$('#login-form').modal();
});
You need to create on your html a div like this:
<div ng-include="'/Somepast/busyModal.html'" ng-show="isLoading"></div>
<div> after load</div>
Then in your javascript you create:
function init() {
$scope.isLoading = true;
SomeFunction().then(function (data) {
$scope.isLoading = false;
}, onError);
};
that's it.
I have a modal and I want a progress bar to be shown while the data for modal display is being fetched by the service call. But in this case, progress bar is being fetched first and then the modal which makes the progress bar to be displayed under the modal. How to fix this ?
this.service.searchMembers(memSearchJson).subscribe((response: any) => {
// some function
}
<modal id="custom-modal-2">
<div class="modal">
</div>
<div id="memberSearchBar" class="class-hide">
<mat-spinner></mat-spinner>
Finding Member IDs..
</div>
</modal>
If document.getElementById("memberSearchBar").className = 'loading-div'; is called before the service call, it throws error as className null. Where should I call this to display progress bar on modal?
I would recommend using *ngIf, as mentioned above. For a better understanding, I add a link to the perfect tutorial.
Loading spinner
For example: in ts:
showSpinner: boolean = true;
ngOnInit() {
this.spinnerShow();
}
if you need show spinner before you get data like from service.
spinnerShow(){
this.workflowService.getData().subscribe(()=>
this.showSpinner = false);
}
And in HTML
<div *ngIf="showSpinner"></div>
Sorry for my terrible english, but i hope this will help you. :)
Instead of doing document.getElementById("memberSearchBar").className = 'loading-div' you can take advantage of Angular's *ngIf and [class.class-name]="expression"
Try something like this instead
displayModalAndSearchMembers(){
// using only isLoading should be sufficient, but I wanted to show how you could use [class.classname]="expression" in the html template as well.
this.isLoading = true;
this.showSpinner = true;
this.displayModal = true;
this.service.searchMembers(memSearchJson).subscribe((response: any) => {
// some function
this.isLoading = false;
this.showSpinner = false;
}
}
<modal id="custom-modal-2">
<div *ngIf="displayModal" class="modal"> <!-- added *ngIf -->
</div>
<div id="memberSearchBar" *ngIf="showSpinner" [class.loading-div]="isLoading">
<mat-spinner></mat-spinner>
Finding Member IDs..
</div>
</modal>
EDIT
You could also use *ngIf="displayModal" on the modal as well, in case you have some display: none on that one as well.
I added some ts code as well. I am not 100% sure what the desired behaviour OP wants, but I am assuming that she wants to show/hide the spinner, and set a class name on it.
This can be simplified by using only *ngIf="isLoading":
<div id="memberSearchBar" class="loading-div" *ngIf="showSpinner">
this is my simple code , i'm trying opening a modal and when opened to put data inside via controller and view, but actually still only printing "{{ i }}" inside modal
html
<body ng-controller="AppController">
<div id="modal">
<div id="modal-content"></div>
</div>
<button class="btn btn-option" onclick="modal({src:'views/layout/modals/modal.html'});">
open
</button>
</body>
modal.html :
<div ng-repeat="i in config.app_genres.genres">
{{ i }}
</div>
app.js:
.controller('AppController', function($scope,$location,$http) {
$scope.config = {};
$scope.config.app_ws = "http://localhost/And/www/";
$scope.config.app_name = "Appname";
$scope.config.app_short_description = $scope.config.app_name+" helps you go out with your rabbit";
$scope.config.app_motto = "hey ohhhhhhhhhh <i class='icon-check'></i>";
$scope.config.app_url = $location.url();
$scope.config.app_path = $location.path();
$http.get($scope.config.app_ws+'jsons/json.json').success(function(response) {
$scope.config.app_genres = response;
});
modal() js function:
function modal(_options){
/*
options = {
html:html to load,
src: false | url
}
*/
var modal = document.getElementById('modal'),
modal_content = document.getElementById('modal-content');
if(_options){
if(_options.html){
modal_content.innerHTML = _options.html;
show(modal);
}
if(_options.src){
_load(_options.src,modal_content) ;
show(modal);
}
}else{
console.log('Please set modal options');
}
}
Instead of calling the modal function directly, create a method called modal on your scope and bind it using ngClick.
Modal should really be a directive because it manipulates the DOM, but in short the reason why you aren't seeing the {{i}} resolve is because the HTML is not compiled. Angular compiles HTML it knows about, but in this example you created a new block of HTML outside of Angular.
From the controller, you could create the method to build it up, and do something like this:
// create a scope from the modal that inherits from the parent scope because it accesses properties there
var modalScope = $scope.new();
// be sure to inject the compiler service: function($scope,$location,$http,$compile)
var element = angular.element(modal_content);
$compile(element.contents())(modalScope);
Although that should work, it would be better if you considered creating a directive for the modal instead.