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

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.

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;
}

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.

Related

Dynamically loaded JS needs to be clickable just like it's in the html

My page fires off an ajax query, where the MySQL Db is queried and the results are returned. (all successful).
Those results are formatted for output as a shopping gallery/catalogue and also as an accordion filter menu. So I can filter the shopping catalogue display. eg say I want to see only items that are red.
All is working so far.
My problem is with the filter accordion menu - dynamically created in js.
When I click on any selectable item in the tab-content, nothing happens. This means the parameter that should be sent, isn't being sent.
If I hard code the accordion filter or even load it with my server-side language, into the html directly, the filtering does send off the parameter and so the shopping catalogue is adjusted accordingly but, in that scenario, I am unable to dynamically change the filter menu.
I think the code I shall post below is the relevant code that recognises changes in the originally loaded content and fires off the ajax but (I think) it doesn't understand any changes to textboxes in the dynamically loaded content.
Please help me to understand what I need to add that will make dynamically loaded content fire-off to the ajax calls.
var $checkboxes = $("input:checkbox");
function update_nav_filter(opts) {
$.ajax({
type: "POST",
url: "/php-queries/product-filter-query.php",
dataType: 'json',
cache: false,
data: {
filterOpts: opts
},
success: function(records) {
//console.log(records);
//alert('SUCCESS!');
// alert(records);
$('#filters_div').html(makeFilter(records));
}
});
}
$checkboxes.on("change", function() {
//alert('there is a change is checkbox status'); // working on page load but not when any checkbox is clicked-on
var opts = getCatalogueFilterOptions();
updateCatalogue(opts);
update_nav_filter(opts);
});
$checkboxes.trigger("change");
Any help greatly appreciated.
I have created an event listener.
Following page-load, I select an item in the JS generated nav filter. eg pedal_bins in the sub_category section. I am then shown a display of pedal_bins. :)
Then I select 'kettles', another sub_category but I can only see the last sub_category that I click on. The pedal_bins disappear.
How best can I build and remove items with a single click? Store in a session parameter and then
a. remove the latest click if it matches whats in the session
b. add the latest click if its not already in the session
Then submit whatever the array is at that stage?
Or, is there a better way to run this?
Here's the listeneer
enter code here
document.getElementById("filtering_div").addEventListener("click",function(e) {
// e.target was the clicked element
if (e.target && e.target.matches("input")) {
var parameter = e.target.id;
//console.log("Anchor element", parameter , " was clicked" );
var opts = getCatalogueFilterOptions(parameter);
console.log(opts);
// update_nav_filter(opts);
updateCatalogue(opts);
}
});
You have a "delegation" problem. When you create a dynamic element, in order to be able to act on the newly created element, you have to reference it as a child element that was originally loaded with the DOM.
For example, if you have an element called <div id="top"></div> and you create a dynamic element, let's say <button id="test">Click</button> in there, you'll have to refer to that div when adding an event listener.
$("#top").on('click', '#test', function(){
//event related code goes here.
});
Here is a fiddle I created that explains the whole thing with some examples.
If you have any questions about it, please let me know.

How to make all the fields visible in the new Task form of Tasks List in SharePoint?

I have created a Tasks List in SharePoint. When I try to add a new Task, it opens a form with few visible fields like TaskName, StartDate, DueDate, Description, AssignedTo. When I click on 'ShowMore', then it is showing all the remaining fields like %complete, TaskStatus, Priority, Comments, ExpectedDueDate.
Issue: I want all the fields to be visible from the start without clicking on 'ShowMore', because some people might get confused with this option and may skip filling these fields. Can someone please kindly suggest how to achieve this. Any help is greatly appreciated. Thank you!
Unfortunately there is no such setting that allows to configure fields visibility in tasks form AFIK.
But task form could be customized in order to display all the fields as demonstrated below.
Since it is a SharePoint 2013 environment, the following approach is suggested:
Create rendering template to display all the fields in New & Edit forms
Update Task web part in New & Edit forms pages
Template file
The following example demonstrates how to display all the fields of Task form:
(function () {
function preTaskFormRenderer(renderCtx) {
rlfiShowMore();
}
function registerRenderer()
{
var ctxForm = {};
ctxForm.Templates = {};
ctxForm.OnPreRender = preTaskFormRenderer;
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(ctxForm);
}
ExecuteOrDelayUntilScriptLoaded(registerRenderer, 'clienttemplates.js');
})();
How to apply changes
Upload the specified script (lets name it TaskForm.js) into SharePoint Site Assets library
Open New Form page in edit mode and go to Tasks web part properties
Specify JS Link property located under Miscellaneous group: ~sitecollection/SiteAssets/TaskForm.js (see pic. 1)
Save changes and repeat steps 2-4 for Edit form
I prefer other methods to the JavaScript workaround to really solve the problem:
1. You can change the list form assigned to the local Task content type, for example via PowerShell:
$web = Get-SPWeb http://YourSharePointSite
$list = $web.Lists["Tasks"]
$ct = $list.ContentTypes[0]
$ct.DisplayFormTemplateName = "ListForm"
$ct.NewFormTemplateName = "ListForm"
$ct.EditFormTemplateName = "ListForm"
$ct.Update()
You can set the list form assigned to the ListFormWebPart via SharePoint Designer
Create your own control template and add the ShowExpanded="true" attribute to the TaskListFieldIterator control
Pass the Expanded=1 in the request query string like NewForm.aspx?Expanded=1
Change the default column order of the local Task content type
All of these have the effect of displaying all the fields without the "Show More" button. You can read more about these methods here:
http://pholpar.wordpress.com/2014/11/01/no-more-show-more-in-tasks-lists/

How to rebind a Kendo ListView after changing template

I'm attempting to rebind the listview data after changing the template, based on a DropDownList value. I've included a JSFiddle for reference. When I rebind currently the values in the template are undefined.
Thanks!
JSFiddle link
I was thinking the best way to handle it would be in the 'select' or 'change' function:
var cboDetailsCategory = $("#detail").kendoDropDownList({
data: [
"All",
"Customer",
"Location",
"Meter",
"Other"],
select: function (e) {
var template = $("#" + e.item.text()).html();
console.log("template", template);
$("#details").html(template);
},
change: function (e) {
},
please refer to the JSFiddle link and this graphic as a visual
Here is a lengthier workflow:
User completes a name search and clicks a search button.
Name results are populated in a listview, rendered individually as button controls using a template.
User then clicks one of the name results (shown as the button text).
A dropdownlist of categories ('All' <--default , 'Location', 'Customer'...) gives the user the ability to target what subject of data they want to see. 'All' is the default, showing all details about the selected name.
So by default the 'All' template is populated.
If user wants to see the 'Location' details (template) they select it from the dropdownlist.
The template shows but the values are all blank. The only way to populate it is to click the name (button) again.
I want to remove the need for having to re-click the button (name) to populate the template ('Location', etc...).
I have put together a JSFiddle showing the structure. Though due to the data being private and served over secure network I cannot access it.
Refer to JSFiddle:
I believe the issue is that the onclick event grabs the data-uid and passes it to the initial default template (named 'All' but it's not included in code as it's lengthy). When the user changes the dropdownlist (cboDetailsCategory) and selects a new template I lose the data.
Thanks for your help. I'm really stuck on this and it's a current show stopper.
There isn't an officially supported way to change templates, without destroying the listview and rebuilding it. However, if you don't mind poking into into some private api stuff (be warned I can't guarantee that kendo won't break it without telling you) you can do this
var listview = $("#MyListview").getKendoListView();
listview.options.template = templateString;
listview.template = kendo.template(listview.options.template);
//you can change the listview.altTemplate the same way
listview.refresh(); //redraws the elements
if you want to protect against unknown API changes you can do this, which has A LOT more overhead, but no risk of uninformed change (untested!)
var listview = $("#MyListview").getKendoListView(),
options = listview.options;
options.dataSource = listview.dataSource;
listview.destroy();
$("#MyListview").kendoListView(options);
Here's the solution, thanks for everyone's help!
JSFiddle Link
The issue was where I was setting the bind:
$("#list").on("click", ".k-button", function (e) {
var uid = $(e.target).data("uid");
var item = dataSource.getByUid(uid);
var details = dropdown.value();
var template = $("#" + details).html();
$("#details").html(template);
kendo.bind($("#details"), item);
currentData = item;
});

jqGrid with navbar/pager having a custom function bound to the edit button

I'm using the jQuery plugin that generates interactive tables called jqGrid.
I want to use this "editfunc" (2/3rds or 3/4ths down the page) but I can't find a clear example of how to implement it anywhere. I've attempted several differnt things and all of them leave me with total failure.
To be clear, the table generated looks something like this:
That lower bar is called the "navpbar" or "pager", you implement it as a separate DIV, the API and documentation is fairly unclear (to myself anyway) on how exactly I put a custom function onto those buttons such as "add", "edit", "delete", etc... I can get default functionality working, but I can't find anything through websearches, this site, or the API docs on what the actual implementation looks like.
jqGrid has opened source. It helps to clear all questions directly in the code. Look at the lines for example. You will see what navGrid do on click on the 'Edit' button of the navigator:
var sr = $t.p.selrow;
if (sr) {
if($.isFunction( o.editfunc ) ) {
o.editfunc(sr);
} else {
$($t).jqGrid("editGridRow",sr,pEdit);
}
} else {
$.jgrid.viewModal("#"+alertIDs.themodal,{gbox:"#gbox_"+$t.p.id,jqm:true});
$("#jqg_alrt").focus();
}
So if you define editfunc callback function the function will be called with id of selected row as the parameter instead of creating editing dialog by editGridRow.
The method editGridRow have many customization functionality. The prmEdit parameter of the navGrid allow to specify any option used by editGridRow.
If you don't want do display editing form and display any other GUI instead you can use editfunc callback function. For example:
$("#list").jqGrid('navGrid', '#pager', {
editfunc: function (rowid) {
alert('The "Edit" button was clicked with rowid=' + rowid);
}
});
See the demo. Select a row and click on the "Edit" button and you will see the alert instead of the standard editing form.

ExtJS List Data View

Left i have a list with all names. I select one name and click on a button 'add' (to the right list). Also i want to 'remove' items form the right list, back to the left list.
Someone an example?
$('#buttonid').click(function(){
var selecteditem = $('#listID option:selected').html();
$('#targetListId').append(selecteditem );
}
vice-vers for second question.
http://dev.sencha.com/deploy/dev/examples/dd/dnd_grid_to_grid.html
is an example to study
Is this what you're after?
I dont have example handy, however if you look at http://dev.sencha.com/deploy/dev/examples/grid/array-grid.html you can see grid example, focus on tpl to generate action (sell, buy) images/buttons, in listView you can also use tpl, change actions to remove (maybe only gray out if want to restore)...
But add action should be easier in EditGridPanel, If you want to still use listView I will suggest to have ad button on top or bottom toolbar, execute formPanel for data entry, when submit just add to listView.store
Dont forget to build communication to DB if needed in add and remove actions
That's really-really simple to do.
Just grab the selected record(s), remove from the store of left list, and add to the store of right list:
var left = // define your GridPanel or ListView
var right = // define your GridPanel or ListView
new Ext.Button({
text: "Move right ->",
handler: function() {
// when using GridPanel
var records = left.getSelectionModel().getSelections();
// when using ListView
var records = left.getSelectedRecords();
left.getStore().remove(records);
right.getStore().add(records);
}
});
I'm sure you can figure out how to implement the "Move left" button.
Note: Always first remove record from one store before adding to another as ExtJS currently doesn't support having one record in multiple stores. If you do it the other way around, strange things will happen.

Categories