A am trying to use bootstrap-select - a javascript/css library extending the html-select-tag with nice features and style. At first glance, calling it from elm seems simple. Indeed, the snipped
view : Model -> Html Msg
view model =
select [ class "selectpicker", attribute "data-live-search" "true" ]
[ option [] [ text "foo" ]
, option [] [ text "bar" ]
]
yields a nice (searchable) select box with two items. However, things get complicated in dynamic situations. Suppose our elm model is a boolean deciding wether the select box is shown or not.
type alias Model = Bool
init : Model
init = True
update : Msg -> Model -> Model
update Toggle model = not model
view : Model -> Html Msg
view model =
if model then
div []
[ select [ class "selectpicker", attribute "data-live-search" "true" ]
[ option [] [ text "foo" ]
, option [] [ text "bar" ]
]
, button [ onClick Toggle ] [ text "toggle" ]
]
else
button [ onClick Toggle ] [ text "toggle" ]
When loading the page, we see again a nice select box which disappears when hitting the toggle button. However, when hitting the toogle button again, the select box will not appear again! The reason is that selectpicker nodes are required to be refreshed if content has changed (including enabling/disabling the node). That is, we have to call
$('.selectpicker').selectpicker('refresh');
from the outside Javascript world after our select box has been added to the DOM again.
I tried to solve that problem using ports, but unfortunately I only got elm to fire an event before rendering, so I additionally had to use setTimeout to wait for completion, which is quite hacky. I suppose there must be a neat solution using a custom element, but again, I was not able to figure out how to call the refresh function at the right moment.
Any help is greatly appreciated!
Finally, I managed to wrap bootstrap-select into a (minimal, nonperfect) custom element which automatically refreshes on updates. Here it is:
import { LitElement, html, customElement, property } from 'lit-element';
import * as $ from 'jquery';
import 'bootstrap';
import 'bootstrap-select';
#customElement('lit-select')
export class LitSelect extends LitElement {
#property({ type : Array }) items = []
updated() {
$(this).find(".selectpicker").selectpicker('refresh');
}
createRenderRoot() {
return this;
}
private renderItem(item: string) {
return html`
<option>
${item}
</option>
`;
}
render() {
return html`
<select class="selectpicker" data-live-search = "true">
${this.items.map(item => this.renderItem(item))}
</select>
`;
}
}
This element can be created from HTML as
<lit-select items='["foo", "bar"]'></lit-select>
or from elm as
node "lit-select" [ attribute "items" "[\"foo\",\"bar\"]" ] []
and it also works in dynamic situations as above.
However, an obvious drawback is that the item list has to be given to a lit-select attribute encoded as a json string. So the markup possibilities are rather limited (for example, the user cannot decide wether to give lit-select a bunch of options or a bunch of option groups).
I would be happy to see better solutions but since this is another topic, I will start a followup question soon.
Related
I have below array of objects as sample input. I have this object available on page load of component.
I need to iterate on it and then fetch its Label Name,its input type and text associated to it.
Say suppose for first objet, I need to show label and its input type is dropdown and its text in one row, then go to second object show its lable text, its input type is textbox and then text on second row and so on for all other objects.
It should happen on run time when page is loading. I am not sure how to do it.
I just need to render it on page load. So I know that I need to write that code in componentDidMount() method but how to render is not sure.
While I am looking for sample code but approach to resolve it will also help me to solve it on my own. Thanks in advance.
[
{
"LABEL_NAME": "Purpose",
"LABEL_TYPE": "STANDARD_LABEL",
"WIDGET_TYPE": "NARROW_DROPDOWN",
"TEXT": "Abc"
},
{
"LABEL_NAME": "Sub-purpose",
"LABEL_TYPE": "STANDARD_LABEL",
"WIDGET_TYPE": "Textbox",
"HELP_TEXT": "pqr"
},
{
"LABEL_NAME": "Date",
"LABEL_TYPE": "STANDARD_LABEL",
"WIDGET_TYPE": "DATE_FIELD",
"HELP_TEXT": null
}
]
I've got a VueJS application which filters items based on a number of checkbox items like a category filter for a shop.
When a user clicks a checkbox, we fire off an API request and a list of updated items is returned. The URL is also updated with a query string representing the checkbox that they have selected.
If a user navigates to a query stringed URL we want to have the checkboxes relating to the filters in the query string checked. That way if there is a page refresh, all the same checkboxes are checked.
We've done this so far using an if(window.location.search) and then parsing that query string, adding the parsed query string into an object. Passing that object down into the child component as a prop then setting the model the checkboxes are bound to to the query string object on update.
This works and is fine. The issue is theres stuttering and flashing of the checkboxes. You click the checkbox, it initially unchecks after selecting, the when the API response comes back, it select. Not very good for UX. I'm assuming this is because we're modifying the model the checkboxes are bound to while also trying to update it on checkbox click.
So I'm wondering if there's a better way of doing this please and if someone else has tackled a similar issue.
I've attached some code below, but as its spread across multiple components its quite hard to display here.
Thanks
<template>
<ul>
<li v-for="(filter, index) in filters" v-bind:key="index">
<input type="checkbox" name="filters" v-model="checked" v-on:change="changeItems">{{filter.filterName}}
</li>
{{checked}}
</ul>
</template>
<script>
export default {
data() {
return {
checked: []
}
},
props: [
'filters',
'checkedFilters' //passed object of filters in query string
],
updated: function() {
this.checked = this.checkedFilters
},
methods: {
changeItems: function (){
this.$emit('change-items', this.checked)
}
}
}
</script>
I am currently working on a pretty simple input form with various input elements, such as selects, texts and radiobuttons. The form is validated upon post and if not validated successfully the form is displayed again with erroneous fields highlighted. The highlight is achieved by adding a class has-error and appending a div with an error message. Upon correcting the error - say checking one of the radio-buttons in a group - the onChange event is captured and it removes the has-error class and the div:
if ($(obj).parents('div.has-error')) {
$(obj).parents('div.has-error').removeClass('has-error');
$(obj).parents('div.has-error').find('div.help-block').remove();
}
It works exactly as intended when performed by a "real" user. However when the exact same thing is performed by a Selenium (GEB) test, the radiobutton isn't checked. For a short while it has the "focus" dotted line as if it tabbed through the element.
Hence the test fails since the radiobutton is never checked. If I remove the above mentioned javascript it works just fine... And likewise if I remove just one of the two statements....
The test is written with GEB and uses "form-control-shortcuts" to set the value of the fields by name, like:
class FormModule extends Module {
static base = { $('form#personform') }
static content = {
personskoen { "person.koen"().module(RadioButtons) }
}
}
class OpretPersonPage extends Page {
static url = "person/opret"
static at = { title.contains("person.opret.titel") }
static content = {
form { module FormModule }
}
}
And the value is set in the test like this:
def () {
given:
at OpretPersonPage
when:
page.form.persoenskoen = "KVINDE"
then:
page.form.persoenskoen == "KVINDE"
}
I am completely stuck and have no further ideas to test. I really hope you guys have some!
I am using:
org.seleniumhq.selenium:selenium-firefox-driver:2.52.0
org.seleniumhq.selenium:selenium-support:2.52.0
org.seleniumhq.selenium:selenium-server:2.52.0
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.
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;
});