Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Do I Overlay a Popup View in Mithril.js?

As a practical exercise in learning bare-bones JS programming in depth (on up to date browsers), I am building an SPA to maintain customer records. The only external library I am using is Mithril.js MVC. So far I have got a table view with live data from my database, which includes edit, merge and delete buttons for each record. The editing is done and working well, using an inline "form" and save/cancel for that works.

I am now trying to implement delete and merge, both of which need a popup confirmation before being actioned, which is where I am stuck. I know exactly what I'd do in a desktop GUI environment, so the roadblock may be my lack of understanding of the browser front-end more than of Mithril, per se.

Ideally, I'd like to create a self-contained, reusable "popup" component represent the popup, but I can't see how I should go about doing this in JS using Mithril, in particular, but not solely, how to make Mithril to overlay one view on top of another.

Any assistance would be appreciated, from a broad outline to specific code snippets.

like image 866
Lawrence Dol Avatar asked Sep 18 '14 19:09

Lawrence Dol


2 Answers

You probably want to use a view model flag to control the modal popup's visibility.

//modal module
var modal = {}
modal.visible = m.prop(false)
modal.view = function(body) {
  return modal.visible() ? m(".modal", body()) : ""
}

//in your other view
var myOtherView = function() {
  //this button sets the flag to true
  m("button[type=button]", {onclick: modal.visible.bind(this, true)}, "Show modal"),

  //include the modal anywhere it makes sense to
  //its visibility is taken care by the modal itself
  //positioning is controlled via CSS
  modal.view(function() {
    return m("p, "modal content goes here")
  })
}

To make a modal dialog, you can either use the styles from one of the many CSS frameworks out there (e.g. Bootstrap), or style .modal with your own CSS

/*really contrived example to get you started*/
.modal {
  background:#fff;
  border:1px solid #eee;
  position:fixed;
  top:10px;
  left:100px;
  width:600px;
}
like image 107
LeoHorie Avatar answered Oct 16 '22 08:10

LeoHorie


I don't know if I am just not quite getting MVC, but I simply set a view-model object that contains the detail of the popup, and then when generating the view if that is currently set I populate the div containing the popup. CSS controls the look and positioning.

So basically I am relying of Mithril's top-down re-render approach to conditionally build the view based on current application state -- it works really well and is immanently sensible to me.

I actually used a list of popup confirmation objects, so multiple confirmations can queue up.

In the controller, make a confirmation queue:

function Controller() {
    ...
    this.confirmation                   =[];
    ...
    }

In the view, create a confirmation view div if there's a confirmation queued, or an empty placeholder otherwise (Mithrils differencing works best if container elements don't appear and disappear from render to render):

function crtView(ctl) {
    ...
    return m("div", [
        ...
        crtConfirmationView(ctl),
        ...
        ]);
    }

function crtConfirmationView(ctl) {
    var cfm=ctl.confirmation[0];

    return m("div#popup-confirm",(cfm ? muiConfirm.crtView(ctl,cfm.title,cfm.body,cfm.buttons) : null));
    }

Then, whenever a confirmation is needed, just push a confirmation object into the queue and let Mithril's drawing system run and rebuild the view.

function deleteRecord(ctl,evt,row,idx,rcd) {
    var cfm={
        title   : m("span","Delete Customer: "+rcd.ContactName),
        body    : [
            m("p","Do you really want to delete customer "+rcd.CustomerId+" ("+rcd.ContactName+") and all associated appointments and addresses?"),
            m("p.warning", "This action cannot be undone. If this is a duplicate customer, it should be merged with the other record."),
            ],
        buttons : deleteButtons,
        proceed : "delete",
        index   : idx,
        record  : rcd,
        };

    ctl.confirmation.push(cfm);
    }

The confirmation object contains whatever properties that the confirm helper function crtView needs to create a confirmation view and then take action when the user clicks a button (or presses ENTER or ESCAPE, etc) -- just standard UI stuff that you abstract away into shared reusable components.


Note: Just in case anyone has questions about the array index, I have since moved away from using the array index to identify the record in the data model (when the delete is complete the array element should be removed). Instead I locate the affected record using database ID, which is resilient against intervening changes in the model, like sorting the list.

like image 34
Lawrence Dol Avatar answered Oct 16 '22 09:10

Lawrence Dol