Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modal): Implement modal component #29

Closed
ryzy opened this issue Nov 6, 2015 · 95 comments · Fixed by #564
Closed

feat(modal): Implement modal component #29

ryzy opened this issue Nov 6, 2015 · 95 comments · Fixed by #564

Comments

@ryzy
Copy link
Contributor

ryzy commented Nov 6, 2015

Hi,

The README says Modal (in progress...). Is it true, is sb working on that? Can we share some progress here (in a separate branch presumably)? I'd consider contributing as I need it for my project...

Thanks for providing more info.
Marcin

@valorkin
Copy link
Member

valorkin commented Nov 6, 2015

Hey, approach to modals implementation was more conceptual
next week I get back to ng2-bootstrap activity
and modal will be component oriented like routing

@jgodi
Copy link

jgodi commented Nov 23, 2015

Any update on the Modal component?

@valorkin
Copy link
Member

in progress, stay tunned

@bertvh
Copy link

bertvh commented Nov 27, 2015

+1 . No pressure though. Great work on the other components!

I'm currently on the verge of writing a modal component myself. If you're planning to release soon, that would be great to know (than I'll postpone my work a bit) ;)

@asafyish
Copy link

+1
That's the only important piece that is missing.

@louisscruz
Copy link
Contributor

+1

1 similar comment
@koodikindral
Copy link

+1

@valorkin
Copy link
Member

Okay! :)

@n-lavrenko
Copy link

+1 :)

@eoodin
Copy link

eoodin commented Dec 22, 2015

+1

@AndreyKotofotoff
Copy link

+2 )

@otabekgb
Copy link

+1

@frankbenoit
Copy link

Perhaps something from here can be used?
https://github.com/shlomiassaf/angular2-modal

@bertvh
Copy link

bertvh commented Dec 29, 2015

I already had a brief look at the code. It's not that easy to understand, especially since the Angular API has changed so much since alpha 26 (and the DomRenderer can't do enough anymore).

I settled for a straightforward component that encapsulates a Modal, which I can show/hide at will. Disadvantage is that I need to insert this component in each template I need it (meaning there are multiple - hidden - dialogs all the time) and I'll probably have issues with the backdrop in some cases. But for now it's very uncomplicated and it works.

I can share the code If you like.

@frankbenoit
Copy link

yes, please :-)

@bertvh
Copy link

bertvh commented Dec 29, 2015

Here you go (very unpolished code):

The component:

import {Component, View, Input, Output, EventEmitter, OnInit} from 'angular2/angular2';

/**
 * Shows a bootstrap modal dialog.
 * Set the body of the dialog by adding content to the modal tag: <modal>content here</modal>.
 */
@Component({
  selector: 'modal'
})
@View({
  templateUrl: './components/modal/modal.html'
})
export class Modal implements OnInit {

  @Input('title') title: string;
  @Input('cancel-label') cancelLabel: string = 'Cancel';
  @Input('positive-label') positiveLabel: string = 'OK';

  /**
   * Fires an event when the modal is closed. The argument indicated how it was closed.
   * @type {EventEmitter<ModalResult>}
   */
  @Output('closed') closeEmitter: EventEmitter<ModalResult> = new EventEmitter<ModalResult>();
  /**
   * Fires an event when the modal is ready with a pointer to the modal.
   * @type {EventEmitter<Modal>}
   */
  @Output('loaded') loadedEmitter: EventEmitter<Modal> = new EventEmitter<Modal>();

  showModal: boolean = false;

  constructor() {
    console.log('showModal = ' + this.showModal);
  }

  onInit() {
    this.loadedEmitter.next(this);
    console.log('modal inited');
  }

  /**
   * Shows the modal. There is no method for hiding. This is done using actions of the modal itself.
   */
  show() {
    this.showModal = true;
  }

  positiveAction() {
    this.showModal = false;
    this.closeEmitter.next({
      action: ModalAction.POSITIVE
    });
    return false;
  }

  cancelAction() {
    console.log('sending close event');
    this.showModal = false;
    this.closeEmitter.next({
      action: ModalAction.CANCEL
    });
    return false;
  }
}

/**
 * The possible reasons a modal has been closed.
 */
export enum ModalAction { POSITIVE, CANCEL }
/**
 * Models the result of closing a modal dialog.
 */
export interface ModalResult {
  action: ModalAction;
}

The template:

<div class="modal-backdrop fade in"
     [style.display]="showModal ? 'block' : 'none'"></div>
<div class="modal"
     tabindex="-1"
     role="dialog"
     style="display: block"
     [style.display]="showModal ? 'block' : 'none'">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <!--
        <button type="button"
                class="close"
                aria-label="Close"
                (click)="cancelAction()">
          <span aria-hidden="true">&times;</span>
        </button>
        -->
        <h4 class="modal-title">
          {{title}}
        </h4>
      </div>
      <div class="modal-body">
        <ng-content></ng-content>
      </div>
      <div class="modal-footer">
        <button type="button"
                class="btn btn-default btn-sm"
                (click)="cancelAction()">
          {{cancelLabel}}
        </button>
        <button type="button"
                class="btn btn-primary btn-sm"
                (click)="positiveAction()">
          {{positiveLabel}}
        </button>
      </div>
    </div>
  </div>
</div>

Use in your own templates:

The trick here is the (loaded) event. This allows the parent component to get a direct reference to the modal.

<modal id="cancelConfirmation"
       [title]="'Are you sure?'"
       [cancel-label]="'cancel'"
       [positive-label]="'OK'"
       (loaded)="cancelConfirmationModalLoaded($event)"
       (closed)="onCancelConfirmation($event)">
  The body of your modal
</modal>
// inside your component:
cancelConfirmationModalLoaded(modal: Modal) {
    this.cancelConfirmationModal = modal; // Here you get a reference to the modal so you can control it programmatically
  }

@frankbenoit
Copy link

This seems to work for me.
@bertvh thank you!

@koodikindral
Copy link

Merge it with main branch 👍

@bertvh
Copy link

bertvh commented Dec 30, 2015

@koodikindral Not ready for that. I see a few problems:

  • You need to include the modal in your templates, resulting in multiple (hidden) modals + you need to subscribe to the loaded even in order to control the modal from your own component, which is OK but not super great.
  • Many things you can't do such as: close with (x) button, only supports two buttons at the bottom, can't control size, animation, (much to do here).
  • Code is not super clean.
  • Not unit tested.

But hey, if everyone likes it, I can clean it up and do a pull request. Just need some more supporters ;)

@jhiemer
Copy link

jhiemer commented Jan 6, 2016

+1

@valorkin
Copy link
Member

valorkin commented Jan 6, 2016

status update: have a working prototype, to represent bs4 logic and classes
debugging, planning to land it this week

@valorkin valorkin changed the title Implement modal component feat(modal): Implement modal component Jan 6, 2016
@NathanWalker
Copy link
Contributor

+1 Very much looking forward to this landing. I'll be able to use ng2-bootstrap in my company's app once it does! :)

@zarkosusnjar
Copy link

+1, also, when this lands with a little bit of tweaking I'll stop copy pasting components and actually install a package into a project.

@valorkin
Copy link
Member

Released :)

@jhiemer

Is it possible to reference the modal via template url?

it will be added, but not as templateUrl in html, but as component to inherit and replace html

Is it possible to put a separate ts file behind a modal window?

please elaborate

Is it possible to exchange information between the modal window and its parent?

they share context, so yes

@jhiemer
Copy link

jhiemer commented May 31, 2016

it will be added, but not as templateUrl in html, but as component to inherit and replace html

Ok, waiting for it. :-)

Is it possible to put a separate ts file behind a modal window?

please elaborate

I think the separate component/html one, would fit into this.

Is it possible to exchange information between the modal window and its parent?

they share context, so yes

I will try it.

Question after trying the modal: Let's say I have two buttons, one to save, and one to cancel. Both need to issue the onHide(), default value is always $event. What is the cleanest way to distinct those two states in my component?

@jhiemer
Copy link

jhiemer commented Jun 1, 2016

@valorkin to be more precise, here is a sample from Angular 1.

$scope.open = function (size) {

    var modalInstance = $uibModal.open({
      animation: $scope.animationsEnabled,
      templateUrl: 'myModalContent.html',
      controller: 'ModalInstanceCtrl',
      size: size,
      resolve: {
        items: function () {
          return $scope.items;
        }
      }
    });

    modalInstance.result.then(function (selectedItem) {
      $scope.selected = selectedItem;
    }, function () {
      $log.info('Modal dismissed at: ' + new Date());
    });
  };

@valorkin
Copy link
Member

valorkin commented Jun 1, 2016

@jhiemer modal is just a UI tool, it doesn't create separate context (at least for now or should not)
so it doesn't have result in a separate way
it just allows you to show data in different way
but handling of data streams is up to you

if doesn't make sense I can elaborate a bit more

@jhiemer
Copy link

jhiemer commented Jun 1, 2016

@valorkin ok, but having just this popup without being able to transfer any data into it and getting back some results from e.g. Save() or Cancel() does not help in many cases.

Which use cases do you see for a modal, which just opens and displays "data in a different way"?

Taking an example: you want to have modal, which opens up, if you want to delete a resource. Then it would be very nice to have at least way to get a reference of the entity to delete into the modal and vice versa.

@mohammedzamakhan
Copy link

This also has another limitation, you cannot open one modal from different location in the application.

And also how should we be able to open multiple modals?!

How can we integrate modal with the router?!

@valorkin
Copy link
Member

valorkin commented Jun 2, 2016

And also how should we be able to open multiple modals?!

http://v4-alpha.getbootstrap.com/components/modal/

Multiple open modals not supported
Be sure not to open a modal while another is still visible. Showing more than one modal at a time
requires custom code.

@valorkin
Copy link
Member

valorkin commented Jun 2, 2016

This also has another limitation, you cannot open one modal from different location in the application.

actually you can but the way it is in ng2 is a bit different from ng1

Other question that you can not create components dynamically via just ModalsService.open
This is planned but will be later

@valorkin
Copy link
Member

valorkin commented Jun 2, 2016

How can we integrate modal with the router?!

How can you integrate opening dropdown with router?
Same way works for modal :)

@valorkin
Copy link
Member

valorkin commented Jun 2, 2016

PS do not hesitate to ask questions, I am listing them and will update documentation with answers

right now top prio datepicker popup, than update docs ( more samples, may be even FAQ per component)

@jhiemer
Copy link

jhiemer commented Jun 2, 2016

@valorkin my questions are still open. :-)

thanks for the great library btw.

@mohammedzamakhan
Copy link

@valorkin

Multiple open modals not supported

Even thought Jquery Bootstrap didn't support it, we had Angular UI (for 1.x) which is a custom code as is ng2-bootstrap, which supported it.

@valorkin
Copy link
Member

valorkin commented Jun 6, 2016

@mohammedzamakhan agreed, should be added

@ShurikAg
Copy link

ShurikAg commented Jun 6, 2016

How can I close a modal from within its own controller? For example, I have a modal that shows a form. The save button is executing a method in modal's controller. Once everything is saved, I want to close it.
Is there any reference to lgModal?

@valorkin
Copy link
Member

valorkin commented Jun 6, 2016

http://valor-software.com/ng2-bootstrap/#modals
like in demo

In general you have two options how to get instance of modal component:

  • via View\Content child
  • via html like this #lgModal="bs-modal"
    than you have an access to hide method

@ShurikAg
Copy link

ShurikAg commented Jun 6, 2016

Right, so I have this as a template:

<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="goalFormModal" aria-hidden="true">
....
      <div class="modal-footer">
        <button type="submit" class="btn btn-primary">Create</button>
        <button type="reset" class="btn btn-danger" (click)="lgModal.hide()">Cancel</button>
      </div>
    </div>
  </div>
</div>

And the controller attached to it is something like this

export class GoalFormComponent {

  newGoalModel: NewGoal = new NewGoal('');

  constructor(
    private goalsService: GoalsService
  ) {}

  newGoalSubmit() {
    this.goalsService.createNewGoal(this.newGoalModel)
      .subscribe(
        _ => this.loadGoals(),
        err => console.error(err),
        () => lgModal.hide()
      );
  }
}

So basically, I am within the modal's component. See the line () => lgModal.hide(), that is the one I am refering to.

@valorkin
Copy link
Member

valorkin commented Jun 6, 2016

@ShurikAg no no, this should throw an exception
please refer to this post:
http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/
and inject modal into your component via View or Content child

@valorkin
Copy link
Member

valorkin commented Jun 6, 2016

and let us move to https://gitter.im/valor-software/ng2-bootstrap
to not spam to all here :)

@karthikkavin
Copy link

How to use bootstrap nested modal in simple way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.