How to close an Angular2 modal with a boolean? - html

I'm new to html and Angular2. Any help with this problem would be much appreciated.
I am placing a form component inside a ng2-modal and I would like the modal to close when the form returns a boolean from an event.
The form is for adding a new class to a database. Its first usage is on another page where it is not inside a modal.
There, upon clicking cancel/submit in the component, I have it return true and then redirect to another url.
For the new implementation I want the modal to close when the form returns true. The problem is that the save/cancel buttons are in the component containing the form. So I can't just use the modal's click event to close it.
Here is my current code:
<modal #addNewClassModal>
<modal-header>
<h4 class="modal-title"><i class="fa fa-cube"></i> Add a new class</h4>
</modal-header>
<modal-content>
<div>
<add-new-class (closeModal)="finishAddingNewClass($event)">
</add-new-class>
</div>
</modal-content>
</modal>
My problem is that I can't figure out how to get the modals close() method to rely on the boolean.
I've tried putting closeModal="addNewClassModal.close()" in different places and switching around the syntax but its not working and I can't find anything online.

You can pass a reference to addNewClassModal to your AddNewClass component:
<add-new-class [modal]="addNewClassModal">
</add-new-class>
And in your AddNewClass component add Input() for modal:
import { Input } from '#angular/core';
import { ModalDirective } from 'ng2-bootstrap/components/modal';
#Input()
modal: ModalDirective; // you can also set type to any instead of ModalDirective
Then in your component you can close modal with hide() function:
this.modal.hide();

Related

Angular. Getting wrong data from ngb-panel

I use ngb-accordion in my app. I am trying to get data from every panel but when the first panel is opened click from the second panel returns me wrong data.
Result
I think the problem is the event which raises when input file changes.
Stackblitz Link
I will be glad if someone give me a hint for solving this problem.
There are few things to note in your code.
Your *ngFor is at ngb-accordion which is creating a new accordion for every loop, instead of creating multiple panel within one accordion.
Fix: <ngb-panel *ngFor="let data of datalist; let i = index">
You are using the same label for all three panels, because of which your first panel is opening every time, regardless of which panel you are clicking.
Fix: <label [for]="'image-input-' + i"> and <input ... [id]="'image-input-' + i"
The modal that opens after image selection has no knowledge of which panel it's getting triggered from. So, you have to use your (change)="onFileChange($event, data)" event/function to keep track of selected panel/corresponding data.
Then you can pass that selection from your modal to your processFile(...)
Fix:
export class AppComponent {
...
selectedData: Data;
...
...
onFileChange(event: any, data): void {
...
this.selectedData = data;
}
}
html:
...
<input ... (change)="onFileChange($event, data)>
...
...
<button
...
(click)="processFile(imageInput, selectedData)"
> Done
</button>
Stackblitz Demo

Why databinding is not working in Angular 13?

I have created an event in my header component and trying to listen it in app component, but it is not working as expected.
In header.component.html, on clicking on "Recipes" it is sending 'recipe' string to "onSelect()" method and on clicking on "Shopping List", it is sending string 'shopping-list' to "onSelect()" method.
<li> Recipes</li>
<li> Shopping List</li>
In header.component.ts , I have created event "featureSelected" which is emitting data, whatever we receive in "onSelect()" method.
#Output() featureSelected = new EventEmitter<string>();
onSelect(feature:string){
this.featureSelected.emit(feature);
}
Now, we are listing event "featureSelected" in app.component.html, if it emits string "recipe" we load, recipe component else we load "shopping-list" component.
<app-header> (featureSelected)="onNavigate($event)"</app-header>
<div class="container">
<div class="row">
<div class="col-md-12">
<app-recipes *ngIf="loadedFeature === 'recipe'"></app-recipes>
<app-shopping-list *ngIf="loadedFeature !== 'recipe'"></app-shopping-list>
</div>
</div>
</div>
app.component.ts
loadedFeature='recipe';
onNavigate(feature:string){
this.loadedFeature=feature;
}
It is not loading shopping-list component on clicking on "Shopping List". I think listener is not working properly even though code looks fine. Please help me in finding the issue and please let me know, if any additional information required.
<app-header> (featureSelected)="onNavigate($event)"</app-header>
First of all this is not correct. You need to have output method inside of app-header like this.
<app-header (featureSelected)="onNavigate($event)"> </app-header>
And with that you can access in the onNavigate($event) and store it to some property.
There is stackblitz with working example StackBlitzDemo
If this helps mark it as answer pls.

ng-bootstrap modal: open it via typescript

I'm having problems, I'd like help opening a mode from the component.
I would like to open the modal when the user has been verified in a form.
.component.html
<ng-template #modalExample let-c="close" let-d="dismiss">
/** Modal body **/
</ng-template>
<form (ngSubmit)="f.form.valid && signin()" #f="ngForm" class="">
/** Buttons, labels, buttons **/
</form>
.component.ts
signin() {
/**Calling backend**/
this.modalService( *REFERENCE TO MODAL HERE* )
}
I've tried using
#ViewChild('modalExample') modalExample: ElementRef
this.modalService.open(this.modalExample);
The open method expects either a component or a template ref to function correctly, and ViewChild gives you a TemplateRef by default. To resolve this change your ViewChild declaration to:
#ViewChild('modalExample', {read: TemplateRef}) modalExample: TemplateRef<any>;
and pass that through to this.modalService.open(this.modalExample) when you want to open the modal and that should work.
The Bootstrap site has various examples you can look to see how to open the modal.

How can I change component without changing URL in Angular?

I would like to change component without changing URL. Let's assume that I have a component register. When I open my website I have url www.myweb.com. Then I would like to register by clicking sign up. I would like to display my component register without changing URL. Should I use ngIf or something else? Can you show me example how it should be done?
UPDATE I am sorry, but it seems to me that I was misunderstood. I tried
this solution:
login.component.ts:
showSignUp: boolean = false;
login.component.html:
<button (click)="showSignUp = true">Sign Up</button>
<register *ngIf="showSignUp"></register>
However when I clicking the button Log in I get this:
before:
after clicking:
After clicking the button Log in I would like to get a new website but with the same URL like this:
UPDATE
What do you think about solution shown below? In html file I will be checking whether variable authenticated is equal true. If so then I will display home component.
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
this.authenticated = true;
// this.router.navigate([this.returnUrl]);
},
error => {
this.authenticated = false;
this.alertService.error(error);
this.loading = false;
});
}
UPDATE
Unfortunately it doesn't work. Any ideas how can I use it with this button?
<button [disabled]="loading" class="btn btn-primary">Log in</button>
You can use *ngIf and show the component in condition!
examle
In your sign up component, set a variable and change its value on click of sign up button. And display your register component on click of the login by pitting the condition in display
// sign up component
showRegister = false;
in your sign up component html
<register *ngIf="showRegister"></register>
Yes, this is a perfect use case for ngIf. Try not to over engineer it.
ngIf is the way to go on this kind of thing.
Just put in your component code something like
showSignUp: boolean = false;
then in template:
<button (click)="showSignUp = true">Sign Up</button>
<register *ngIf="showSignUp"></register>
And since you seem new to Angular, I'll mention that in order to use ngIf in template, your module needs to import the CommonModule like
import { CommonModule } from '#angular/common';
imports: [
CommonModule,
]

Parent/child click relationships in AngularJS directives

I have a custom directive placed on a Kendo UI treeview widget.
It seems to be working fine side-by-side, except that I'm trying to simply display the custom icons next to the tree node which is clicked on (see sample image below).
So my directive is data-toggle-me, placed next to the Kendo k-template directive as follows :
<div class="reports-tree" kendo-tree-view="nav.treeview"
k-options="nav.treeOptions"
k-data-source="nav.reportsTreeDataSource"
k-on-change="nav.onTreeSelect(dataItem)" >
<span class="tree-node" k-template data-toggle-tree-icons>{{dataItem.text}}</span>
</div>
and the directive code here inserts some custom icons next to the tree node when a user clicks on that tree node :
.directive('toggleMe', function ($compile) {
// Kendo treeview, use the k-template directive to embed a span.
// Icons appear on Click event.
return {
restrict: 'AE',
transclude: true,
template: '<span ng-show="nav.displayIcons" id="myIcons" class="reptIcons" style="display:none;width:50px;align:right;">' +
' <a title="add new folder" ng-click="nav.addAfter(nav.selectedItem)"><i class="fa fa-folder-open"></i></a> ' +
'<a title="add report here" ng-click="nav.addBelow(nav.selectedItem)"><i class="fa fa-plus"></i></a> ' +
'<a title="remove" ng-click="nav.remove(nav.selectedItem)"><i class="fa fa-remove"></i></a> ' +
'<a title="rename" onclick="showRename(this);"><i class="fa fa-pencil"></i></a>' +
'</span>',
link: function (scope, elem, attrs) {
var icons = elem.find("#myIcons");
elem.on('click', function (e) {
$('.reptIcons').css('display', 'none');
icons.css("display", "inline");
icons.css("margin-left", "5px");
});
}
}
})
My biggest problem at this point is getting the icons to appear on the treenode which is clicked on. Then once the user clicks on a different node, the icons will only render again on the newly-clicked node.
This fiddle represents a partially-working example but the icons are appearing on every single treenode - click tree item to show icons
**** UPDATED TREE IMAGE - All child nodes now show icons (not what I want) ****
I'm not sure to understand your issue, you should try to reduce the code to the minimum and have a snippet/jsfiddle that works.
If all you want is not trigger click events when $scope.disableParentClick is set to true, simply add
elem.on('click', function (e) {
// Do not execute click event if disabled
if (!$scope.disableParentClick) { return; }
...
});
Now that seems all not very angular friendly to me. You should externalize your HTML in either the template or templateUrl of your directive, potentially adding to it a ng-if="displayTemplate" which would only display the node when a click would set $scope.displayTemplate = true;
Also, instead of listening for click events this way, you should use the ng-click directive. Everything is doable with directives. I can give more information when you better understand your problem: I suspect you are not approaching it the right way.
UPDATE: if all you want is display the icons list of the clicked element, you could do it way easier. You actually don't need the toggle-me directive, but even if you keep it you can solve all your troubles the angular-way, which is by using ng-click, ng-repeat, etc. Please have a look at the following jsFiffle to see one way of doing that. There are many other ways, but really try using ng-click to avoid troubles:
http://jsfiddle.net/kau9jnoe/
Events in the DOM are always bubbling up. That is a click on a link would trigger an onclick handler on every element up the hierarchy, e.g. also the body element. After all the click happened within body.
The same is true for your directive. Any click within your element triggers its event handler. To circumvent this either attach the event handler somewhere else or ignore clicks from the links.
The event object has a target property that tells you what element initiated the event. So you could do something like this:
elem.on('click', function (e) {
if (e.target.nodeName.toLowerCase() == 'a') return; //ignore click on links