Preserving state of checkboxes generated by Rxjs - rxjs5

Based on selecting different items in a dropdown, I am generating html of items with check boxes. How can I preserve the state of checkbox i.e. check/uncheck whenever value in dropdown is changing. See the plunkr here https://plnkr.co/edit/PUG3g7dfTbQjPyIgGLzh?p=preview
Steps:
- uncheck 'abc2'
- change dd value to 'international'
- again change the dd value to 'local'
- here 'abc2' must be unchecked...
here is my code:
var data = [
{type: 'local', name: 'abc1', location: 'xyz1', checked: true},
{type: 'local', name: 'abc2', location: 'xyz2', checked: true},
{type: 'international', name: 'abc3', location: 'xyz3', checked: true},
{type: 'local', name: 'abc4', location: 'xyz4', checked: true},
{type: 'local', name: 'abc5', location: 'xyz5', checked: true},
{type: 'international', name: 'abc6', location: 'xyz6', checked: true},
{type: 'international', name: 'abc7', location: 'xyz7', checked: true}
];
var $container = $('#container');
var $splitDD = $('#testDD');
var splitDD$ = Rx.Observable.fromEvent($splitDD, 'change')
.startWith($splitDD.val())
.map(function(e) { $container.html(''); return e.target ? e.target.value : e;})
.switchMap(function(v) {return data;})
.filter(function(v) {
return v.type == $splitDD.val()
})
.map(getHtml)
.subscribe(function(html) {
$container.append(html);
});
function getHtml(v) {
var checked = v.checked ? 'checked="checked"' : '';
var html = '<label><input class="point" name="' + v.name + '" type="checkbox" ' + checked + '>' +
'<span>' + v.name + ' </span>' +
'</label>';
return html;
}

Here is solved: https://plnkr.co/edit/72ciT20saCpLCLFNBbGv?p=preview
Steps:
added onclick listener to checkbox and while checkbox is changed, array is being updated.
1) in getHTML()
var html = '<label><input onclick="CheckboxPreserve(this);" ......
2) added in bottom of scripts.js :
function CheckboxPreserve(el){
var $this = $(el);
for (var key in data) {
if (data[key]['name']==$this.attr("name")) {
data[key]['checked']=$this.is(':checked');
}
}
}

Here's what seemed to work for me:
var splitDD$ = Rx.Observable.fromEvent($splitDD, 'change')
.startWith($splitDD.val())
.map(function(e) { return e.target ? e.target.value : e;})
// See below what this is about
.switchMap(function(v) { return getData$(data); })
// In functional world, you only cause effects to the world in subscribe
// It's not 100% true because we change data above, but it's close
.subscribe(function(items) {
var html = items.map(getHtml);
$container.html(html);
});
function getData$(itemsArray) {
// We can use Rx.Observable.of() and get the entire array, and do some
// and do some optimxations based on that (for example, toArray won't be needed)
// but I'm avoidng changing the symantics of the question
// just in case if they have a reaosn in the real case
return Rx.Observable.from(itemsArray)
// Update checkboxes before filtering because
// the checkbox you are about to update is one you are about to hide
.map(function(v) {
var existing = $container.find("[name='" + v.name + "']");
if(existing.length) {
v.checked = !!existing.prop("checked");
}
return v;
})
.filter(function(v) {return v.type == $splitDD.val();})
// `toArray` needs an observable that completes
// That's why we separate this from the event, which might emit forever
// as the event might fire any time.
.toArray();
}
function getHtml(v) {
var checked = v.checked ? 'checked="checked"' : '';
var html = '<label><input class="point" name="' + v.name + '" type="checkbox" ' + checked + '>' +
'<span>' + v.name + ' </span>' +
'</label>';
return html;
}
Plunker
Another obvious change, and much easier, is to set another event handler, like:
var containerCHK$ = Rx.Observable.fromEvent($container, 'change')
.subscribe(function(e) {
var input = e.target;
var dataPart = data.filter(function(v) {
return v.name == input.name;
});
if(dataPart.length) {
dataPart[0].checked = !! input.checked;
}
});
Plunker

Related

Summernote custom button with dialog

I want to add a custom button to the Summernote toolbar that opens up a dialog that has a textbox for a URL and several checkboxes for settings. I then want to use the info from the dialog to scrape web pages and do processing on the content. The ultimate goal is to place the scraped content into the editor starting where the cursor is. I've searched and found some code on creating a custom button, but not any solid examples of implementing a dialog. I went through the summernote.js code to see how the Insert Image dialog works and that left me really confused. The test code I've got so far is in the code block, below. Thanks in advance to anyone who can help get me sorted out.
var showModalDialog = function(){
alert("Not Implemented");
};
var AddWiki = function(context) {
var ui = $.summernote.ui;
var button = ui.button({
contents: '<i class="fa fa-plus"/> Add Wiki',
tooltip: "Set a New Wiki",
class: "btn-primary",
click: function() {
showModalDialog();
}
});
return button.render();
};
$(".tw-summernote-instance textarea").summernote({
airMode: false,
dialogsInBody: false,
toolbar: [["mybutton", ["customButton"]]],
buttons: {
customButton: AddWiki
},
callbacks: {
onInit: function(e) {
var o = e.toolbar[0];
jQuery(o)
.find("button:first")
.addClass("btn-primary");
}
}
});
I found a good, simple example of what I wanted to do. Here's the code:
(function(factory) {
/* global define */
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(window.jQuery);
}
}(function($) {
$.extend($.summernote.plugins, {
'synonym': function(context) {
var self = this;
var ui = $.summernote.ui;
var $editor = context.layoutInfo.editor;
var options = context.options;
context.memo('button.synonym', function() {
return ui.button({
contents: '<i class="fa fa-snowflake-o">',
tooltip: 'Create Synonym',
click: context.createInvokeHandler('synonym.showDialog')
}).render();
});
self.initialize = function() {
var $container = options.dialogsInBody ? $(document.body) : $editor;
var body = '<div class="form-group">' +
'<label>Add Synonyms (comma - , - seperated</label>' +
'<input id="input-synonym" class="form-control" type="text" placeholder="Insert your synonym" />'
'</div>'
var footer = '<button href="#" class="btn btn-primary ext-synonym-btn">OK</button>';
self.$dialog = ui.dialog({
title: 'Create Synonym',
fade: options.dialogsFade,
body: body,
footer: footer
}).render().appendTo($container);
};
// You should remove elements on `initialize`.
self.destroy = function() {
self.$dialog.remove();
self.$dialog = null;
};
self.showDialog = function() {
self
.openDialog()
.then(function(data) {
ui.hideDialog(self.$dialog);
context.invoke('editor.restoreRange');
self.insertToEditor(data);
console.log("dialog returned: ", data)
})
.fail(function() {
context.invoke('editor.restoreRange');
});
};
self.openDialog = function() {
return $.Deferred(function(deferred) {
var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
var $synonymInput = self.$dialog.find('#input-synonym')[0];
ui.onDialogShown(self.$dialog, function() {
context.triggerEvent('dialog.shown');
$dialogBtn
.click(function(event) {
event.preventDefault();
deferred.resolve({
synonym: $synonymInput.value
});
});
});
ui.onDialogHidden(self.$dialog, function() {
$dialogBtn.off('click');
if (deferred.state() === 'pending') {
deferred.reject();
}
});
ui.showDialog(self.$dialog);
});
};
this.insertToEditor = function(data) {
console.log("synonym: " + data.synonym)
var dataArr = data.synonym.split(',');
var restArr = dataArr.slice(1);
var $elem = $('<span>', {
'data-function': "addSynonym",
'data-options': '[' + restArr.join(',').trim() + ']',
'html': $('<span>', {
'text': dataArr[0],
'css': {
backgroundColor: 'yellow'
}
})
});
context.invoke('editor.insertNode', $elem[0]);
};
}
});
}));

Jest only - addEventListener ~ click assertion

I want to mock a normal dom click as indicated in the Jest docs:
test('displays a user after a click', () => {
document.body.innerHTML =
'<div>' +
' <span id="username" />' +
' <button id="button" />' +
'</div>';
// Use jquery to emulate a click on our button
$('#button').click();
expect($('#username').text()).toEqual('Johnny Cash - Logged In');
});
My function is as follows:
function clickTrack() {
const data = {};
document.addEventListener('click', function clicked(e) {
if (e.target.matches('a')) {
e.preventDefault();
data['click.Classes'] = e.target.classList;
data['click.ID'] = e.target.id;
data['click.Destination'] = e.target.href;
data['click.ElementText'] = e.target.innerText;
}
}, false);
return data;
}
And the test:
describe('Click Tracking', () => {
test('Clicking on an a tag will collect its information', () => {
clickTrack();
document.body.innerHTML = '<a class="j1 j2 j3" id="j" href="http://www.google.com/">Jest</a>';
document.getElementById('j').click();
expect(clickTrack()).toEqual({a:'b'});
});
});
I expect the output to be vaguely:
{
click.Classes: ["j1 j2 j3"]
click.Destination: "https://www.google.com/"
click.ElementText: "Jest"
click.ID: "j"
}
But an empty object is returned instead.
clickTrack hooks up a click event listener and returns a data object that will be updated whenever a click event happens, so you only have to call clickTrack once.
Right now you are calling it again after your click event so it is creating an additional click listener and returning a new, empty data object.
You'll also want to use e.target.text for the text and get the class names by calling split(' ') on e.target.className:
function clickTrack() {
const data = {};
document.addEventListener('click', function clicked(e) {
if (e.target.matches('a')) {
e.preventDefault();
data['click.Classes'] = e.target.className.split(' ');
data['click.ID'] = e.target.id;
data['click.Destination'] = e.target.href;
data['click.ElementText'] = e.target.text;
}
}, false);
return data;
}
describe('Click Tracking', () => {
test('Clicking on an a tag will collect its information', () => {
const data = clickTrack();
document.body.innerHTML = '<a class="j1 j2 j3" id="j" href="http://www.google.com/">Jest</a>';
document.getElementById('j').click();
expect(data).toEqual({
'click.Classes': ['j1', 'j2', 'j3'],
'click.Destination': 'http://www.google.com/',
'click.ElementText': 'Jest',
'click.ID': 'j'
}); // Success!
});
});

Kendo Treeview expand and checkbox not working after new value was passed in from a dropdown list

new to stackoverflow and implementing treeview. I'm having an issue where whenever I select a different value from my dropdown list that would populate my treeview the expand and checkbox is not working anymore. But the first time works.
So I have ajax call that would grab a parameter from a dropdown list to return JSON data to populate my tree. Here's my code. I've been pounding my head for this error and I've also tried some solution in the kendo ui documentation but it just doesn't work. Really need some help. Thank you!
Here's my code:
dataB1 = #Html.Raw(Json.Encode(ViewData["branchList"]));
reloadNewBranch = #Html.Raw(Json.Encode(ViewData["reloadNewBranch"]));
$('#comboNewBranch').kendoAutoComplete({
dataSource: dataB2,
placeholder: "Enter branch name...",
value: reloadNewBranch
});
//Create TreeView
function onChange(e) {
$("body").css("cursor", "progress");
$('#hiddenPackageArray').val('');
document.getElementById("textTestpassId").innerHTML = 'At least one(1) template must be selected.';
$('#treeview').empty();
$.ajax({
url: '#Url.Action("LoadOldBranchTemplates", "Home")',
data: { oldBranchName: $("#comboOldBranch").data("kendoAutoComplete").value() },
dataType: "json"
}).done(function (result, textStatus, jqXHR) {
var viewModel = new kendo.data.HierarchicalDataSource({
data: result,
schema: {
model: {
id: "Id",
hasChildren: true,
children: "Templates"
}
}
});
$("#treeview").kendoTreeView({
loadOnDemand: false,
checkboxes: {
checkChildren: true
},
check: onCheck,
dataSource: viewModel,
dataTextField: ["TestPassName", "DisplayName"]
})
// gathers IDs of checked nodes
function checkedNodeIds(nodes, checkedNodes) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].checked) {
checkedNodes.push(nodes[i].Id);
let filtered = checkedNodes.filter(function (listOfId) {
return listOfId != null;
});
$('#hiddenPackageArray').val(filtered);
}
if (nodes[i].hasChildren) {
checkedNodeIds(nodes[i].children.view(), checkedNodes);
}
}
}
// show checked node IDs on datasource change
function onCheck(e) {
console.log("I'm in onSelect function");
var checkedNodes = Array();
var treeView = $("#treeview").data("kendoTreeView");
var message = String();
checkedNodeIds(treeView.dataSource.view(), checkedNodes);
console.log("Checkbox changed :: " + this.text(e.node));
if (checkedNodes.length > 0) {
message = checkedNodes.join(" ");
} else {
message = "No package(s) are selected.";
$('#hiddenPackageArray').val('');
}
$("#result").html(message);
}
$("body").css("cursor", "default");
})
.fail(function (xmlHttpRequest, textStatus, errorThrown) {
$('#mainDiv').append('<p>Status: ' + textStatus + '</p>');
$('#mainDiv').append('<p>Error: ' + errorThrown + '</p>');
$('#mainDiv').append('<p>' + xmlHttpRequest + '</p>');
});
};

Ckeditor custom plugin- populate dialog data from ajax

I have created a custom plugin to tag a list of usernames in ckeditor. The methodology for implementing is to get the data from the ajax controller. The following is the ajax function to get the list of user names.
$org = new VA_Logic_Organization($orgid);
if ($groupid) {
$group = new VA_Logic_Group($groupid);
if ($group->assigntomanager == 1) {
$manager = new VA_Logic_Avatar($group->managerid);
$avatarlist[$group->managerid] = $manager->firstname . ' ' . $manager->lastname;
} else {
$avatarlist = $group->getAvatarListByName($name, $form->hideleveldifference);
}
} else if ($form->defaultassigngroup) {
$group = new VA_Logic_Group($form->defaultassigngroup);
if ($group->assigntomanager == 1) {
$manager = new VA_Logic_Avatar($group->managerid);
$avatarlist[$group->managerid] = $manager->firstname . ' ' . $manager->lastname;
} else {
$avatarlist = $group->getAvatarListByName($name, $form->hideleveldifference);
}
} else {
$avatarlist = $org->getAvatarListByName($name, $form->hideleveldifference);
}
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender(TRUE);
echo json_encode($avatarlist);
The following is the ckedior pluginname.js.
elements: [
{
type: 'select',
id: 'exam_ID',
label: 'Select Exam',
items :function(element)
{
$.ajax({
type: 'POST',
url: baseurl +"employee/ajax/assignedtoselect/groupid/" ,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: false,
});
}
}
]
I need to populate the json data on click of the dialog text box.
Finally found the solution.
The onShow function was very helpful,
The working code for pluginname.js is here
CKEDITOR.dialog.add( 'abbrDialog', function( editor ) {
return {
// Basic properties of the dialog window: title, minimum size.
title: 'Tag Avatars',
minWidth: 250,
minHeight: 100,
// Dialog window contents definition.
contents: [
{
// Definition of the Basic Settings dialog tab (page).
id: 'tab-basic',
label: 'Avatars',
// The tab contents.
elements: [
{
// Text input field for the abbreviation text.
type: 'text',
id: 'avatarid',
label: 'Type an avatar name to tag'
},
]
},
{
// Definition of the Basic Settings dialog tab (page).
id: 'tab-advanced',
label: 'Items/Modules',
// The tab contents.
elements: [
{
// Text input field for the abbreviation text.
type: 'text',
id: 'instanceid',
label: 'Type an item to tag'
},
{
type: 'checkbox',
id: 'checkid',
label: 'check to add link'
}
]
}
// Definition of the Advanced Settings dialog tab (page).
],
onShow: function() {
var assigned_config = {
source: baseurl+"employee/ajax/assignedtoselect/",
select: function(event, ui){
$("#cke_79_textInput").val(ui.item.value);
$("#cke_79_textInput").val(ui.item.id);
window.abbr1 = ui.item.id;
},
change: function (ev, ui) {
if (!ui.item){
$("#cke_79_textInput").val("");
}
}
};
$("#cke_79_textInput").autocomplete(assigned_config);
var ac_config = {
source: baseurl+"employee/module/parentlist/",
select: function(event, ui){
$("#cke_84_textInput").val(ui.item.value);
$("#cke_84_textInput").val(ui.item.id);
window.instanceid = ui.item.id;
},
change: function (ev, ui) {
if (!ui.item){
$("#cke_84_textInput").val("");
}
}
};
$("#cke_84_textInput").autocomplete(ac_config);
},
// This method is invoked once a user clicks the OK button, confirming the dialog.
onOk: function() {
var dialog = this;
if(window.abbr1 === undefined){
var checkid = document.getElementById('cke_88_uiElement');
if (checkid.checked) {
var url = baseurl+ '/employee/module/view/instanceformid/'+instanceid;
abbr = dialog.getValueOf( 'tab-advanced', 'instanceid' );
var inst = '<a target="_blank" href='+url+'>'+abbr+'</a>';
editor.insertHtml(inst);
}
else{
var inst = '\$\[' +instanceid+ '\!\description]';
editor.insertHtml( inst );
}
}
else{
abbr = dialog.getValueOf( 'tab-basic', 'avatarid' );
var url = baseurl + '/employee/avatar/friendsprofilepopup/avatarid/' +abbr1;
var htmlavatar = '<a class="infopopup20" href= '+url+' >'+abbr+ '</a>';
editor.insertHtml( htmlavatar );
}
}
};
});

How to add toggle button from jqgrid toolbar to autogenerated context menu

Toggle button in jqgrid top toolbar is defined using Oleg answer as
var autoedit;
$("#grid_toppager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all" >' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" '+(autoedit ? 'checked ' : '')+'/>' +
'<label title="Toggle autoedit" for="AutoEdit">this text is ignored in toolbar</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-star"}
}).click(function () {
autoedit = $(this).is(':checked');
});
Answer from how to add standard textbox command to jqgrid context menu is used to autogenerate context menu for grid from toolbar.
In generated context menu for this item only text "this text is ignored in toolbar" appears and selecting it does nothing.
How to make it work or remove this item from context menu?
Look at the demo or the same demo with another themes: this and this.
First of all I modified the code of the jquery.contextmenu.js to support jQuery UI Themes. Then I modified the code more, to be able to create context menu more dynamically. In the modified version of jquery.contextmenu.js one can crate menu and the bindings not only in the onContextMenu, but also in onShowMenu. Inside of onContextMenu one can create just the empty menu
<div id="myMenu"><ul></ul></div>
It is important only if one use dynamically switching of the icons of the text of buttons from the navigator bar.
You can download the modified version of the file here.
In the demo I used the last modification of the code from the answer, so the standard context menu can be still used in the grid on selected text or in the enabled input/textarea fields. The context menu of the browser will be displayed in the case:
I modified the code of createContexMenuFromNavigatorButtons function from the answer to the following:
var getSelectedText = function () {
var text = '';
if (window.getSelection) {
text = window.getSelection();
} else if (document.getSelection) {
text = document.getSelection();
} else if (document.selection) {
text = document.selection.createRange().text;
}
return typeof (text) === 'string' ? text : text.toString();
},
createContexMenuFromNavigatorButtons = function (grid, pager) {
var menuId = 'menu_' + grid[0].id, menuUl = $('<ul>'),
menuDiv = $('<div>').attr('id', menuId);
menuUl.appendTo(menuDiv);
menuDiv.appendTo('body');
grid.contextMenu(menuId, {
bindings: {}, // the bindings will be created in the onShowMenu
onContextMenu: function (e) {
var p = grid[0].p, i, lastSelId, $target = $(e.target),
rowId = $target.closest("tr.jqgrow").attr("id"),
isInput = $target.is(':text:enabled') ||
$target.is('input[type=textarea]:enabled') ||
$target.is('textarea:enabled');
if (rowId && !isInput && getSelectedText() === '') {
i = $.inArray(rowId, p.selarrrow);
if (p.selrow !== rowId && i < 0) {
// prevent the row from be unselected
// the implementation is for "multiselect:false" which we use,
// but one can easy modify the code for "multiselect:true"
grid.jqGrid('setSelection', rowId);
} else if (p.multiselect) {
// Edit will edit FIRST selected row.
// rowId is allready selected, but can be not the last selected.
// Se we swap rowId with the first element of the array p.selarrrow
lastSelId = p.selarrrow[p.selarrrow.length - 1];
if (i !== p.selarrrow.length - 1) {
p.selarrrow[p.selarrrow.length - 1] = rowId;
p.selarrrow[i] = lastSelId;
p.selrow = rowId;
}
}
return true;
} else {
return false; // no contex menu
}
},
onShowMenu: function (e, $menu) {
var options = this, $menuUl = $menu.find('ul:first').empty();
$('table.navtable .ui-pg-button', pager).each(function () {
var $spanIcon, text, $td, id, $li, $a, button,
$div = $(this).children('div.ui-pg-div:first'),
gridId = grid[0].id;
if ($div.length === 1) {
text = $div.text();
$td = $div.parent();
if (text === '') {
text = $td.attr('title');
}
if (this.id !== '' && text !== '') {
id = 'menuitem_' + this.id;
if (id.length > gridId.length + 2) {
id = id.substr(0, id.length - gridId.length - 1);
}
} else {
// for custom buttons
id = $.jgrid.randId();
}
$li = $('<li>').attr('id', id);
$spanIcon = $div.children('span.ui-icon');
if ($spanIcon.length > 0) {
// standard navGrid button or button added by navButtonAdd
$li.append($('<a>')
.text(text)
.prepend($spanIcon.clone().css({
float: 'left',
marginRight: '0.5em'
})));
$menuUl.append($li);
options.bindings[id] = (function ($button) {
return function () { $button.click(); };
}($div));
} else {
button = $div.children("input").data("button");
if (button !== undefined) {
$a = $('<a>')
.text(button.options.label)
.prepend(
$('<label>').addClass("ui-corner-all").css({
float: 'left',
width: '16px',
borderWidth: '0px',
marginRight: '0.5em'//'4px'
}).append(
$('<span>').addClass("ui-button-icon-primary ui-icon " +
button.options.icons.primary)
.css({
float: 'left',
marginRight: '0.5em'
})
)
);
$li.append($a);
if (button.type === "checkbox" && button.element.is(':checked')) {
$a.find('label:first').addClass("ui-state-active");
}
$menuUl.append($li);
options.bindings[id] = (function ($button, isCheckbox) {
if (isCheckbox) {
return function () {
if ($button.is(':checked')) {
$button.siblings('label').removeClass("ui-state-active");
} else {
$button.siblings('label').addClass("ui-state-active");
}
$button.click();
$button.button("refresh"); // needed for IE7-IE8
};
} else {
return function () { $button.click(); };
}
}(button.element, button.type === "checkbox"));
}
}
}
});
return $menu;
}
});
},
autoedit = false;
and fill the check-button in the navigator bar with the code which is changed only a little:
$("#pager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all">' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" />' +
'<label title="Checkx caption which should appear as button tooltip"' +
' for="AutoEdit">Autoedit</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-mail-closed"}
}).click(function () {
var iconClass, $this = $(this);
if (!autoedit) { // $this.is(':checked')) {
autoedit = true;
iconClass = "ui-icon-mail-open";
} else {
autoedit = false;
iconClass = "ui-icon-mail-closed";
}
$this.button("option", {icons: {primary: iconClass}});
});
createContexMenuFromNavigatorButtons($grid, '#pager');
UPDATED: One more demo which support buttons added by new inlineNav method you can find here. Additionally I included in the demo the function normalizePagers which I use to improve the look of the pager:
How you can see the contextmenu includes only enabled buttons from the navigator bar.

Resources