Change JavaScript object values with auto-generated HTML form - javascript

I have got a 'big' JavaScript object that looks somewhat like this:
var userConfig = {
active: true,
subElement: { someProp: 156, otherProp: 'yaye' },
foo: ['bar', 'see', 'no']
// etc
}
What I'm looking for is some sort of framework that I pass the variable (or a part of the variable) to and that reads all properties and creates a form where these can be configured. So a checkbox would be created for a boolean, a textbox for a string etc...
Anyone knows about such a library?
Update: At the moment settings are changed by opening the JS and editting the variables manually (The JS is a locally stored greasemonkey script). Pretty much anything beats that really.
I'm not interested in writing (alot of) code to do two way binding, creating all the UI widgets and having a clean seperation of concerns (MVVM, MVP, ...) which is what Knockout/Backbone/... does (judging from the tutorials).
Instead:
var userConfigUpdater = {
active: { description: "Activates or deactivates feature X", editType: "boolean"},
subElement: {
description: "subElement",
editType: "tabularItem",
someProp: {description: "foo", editType: "text"},
// more
}
}
createHtmlWidgets(userConfig, userConfigUpdater);
Now the user can edit the form elements and then we have something like:
$("#okButton").click(function() {userConfig = getUpdatedValues();});
Granted, it doesn't look very nice, but it would get the job done quite fast/easily. I'm guessing there is not yet some public framework that does something like this?

The closest thing I know is knockoutjs. This doesn't do exactly what you want but what it does do is allow a mechanism for keeping that object in the knockout world it would be called a viewModel in sync with your form so if you update the form contents it would update that object's data automatically and vice-versa

I ended up writing my own 'framework'.
It is 'pretty' generic but somewhat integrated into the rest of my project, really limited in features and the API is not very clean. Use at your own risk :)
The source code on GitHub. The 'framework' is propui.js and sangu_config.js is the configuration for propui.
Example how to call the API:
backgroundColor: {
label: "Change the background color",
propUI: {
getter: function() { return settings.backgroundColor; },
setter: function(value) { settings.backgroundColor = value; },
editor: "bool"
}
},

Related

Truly protecting data members (of classes/IIFE variables) in JavaScript

Context
I have been tasked with fixing a big bug on the menu-edit page, which was caused by a stale element issue, caused by the HTML elements for it being rendered server-side. In my three-day fight against this bug, I got some inspiration from Angular and decided to try to make a menu state that will power everything on the page (adding/removing categories/items, and later, pagination of the modals for the adding)
Some Code
I came up with this IIFE (to be the "controller" of the MVC. Selector modals hit the add methods of this, and delete buttons hit the remove methods of this. Also, this gets passed to template-render function, which is literally the first thing hit when a modal gets popped):
/* all the categories, items, and modifiers that power this page */
var menuState = (function() {
let _categories = {
attached: [],
available: []
}, _items = {
attached: [],
available: []
}, _modifiers = {
attached: [],
available: []
}
function getExposedMethodsFor(obj) {
return {
all : function() { return obj.attached.concat(obj.available) },
attached : function() { return obj.attached },
available : function() { return obj.available }
// ... other methods that work on obj.attached,obj.available
}
}
let categoryExposedMethods = getExposedMethodsFor(_categories)
// other exposer objects
return {
getAllCategories : categoryExposedMethods.all,
getAttachedCategories : categoryExposedMethods.attached,
getAvailableCategories : categoryExposedMethods.available
// the rest of the exposed methods irrelevant to this question at hand
}
})()
OK, so what's the problem?
The problem is that this is false sense of security, it seems. When I try to XSS-test this structure alone, it fails.
I test it with three entities in _categories, all of which are attached, causing
menuState.getAllCategories().length
to return 3 and
menuState.getAvailableCategories().length
to return 0. Good news is that when I tried
menuState.getAllCategories().push('a')
menuState.getAllCategories().length
I still get three.
However, when I go
menuState.getAvailableCategories().push('b')
menuState.getAvailableCategories().length
I get 1, instead of 0 !!
Is there truly a way to lock down the other getters here?! If not, what are my alternatives?
I fixed it with Object.freeze, which I already used for refactoring the "enums" the dev before me wrote when he was working on this project. What it does is fully protect a state from any type of changes, including:
adding properties
deleting properties
modifying properties
re-assigning the object/array being "frozen"
How I use it
In the helper method, I did the following :
attached : function() { return Object.freeze(obj.attached) },
available : function() { return Object.freeze(obj.available) },
This prevents the arrays being changed from those methods, thus shutting down this type of XSS. Also, menuState was declared with const.

Shopware 5, open BatchProcess window from Own Plugin

I hope its not to harsh to ask not to mince matters.
Here we go:
I have a problem developing a custom Plugin for Shopware 5.
I already have a working plugin which lists orders for certain criteria.
Now I want a Button (which i already have) in the toolbar of this grid-window.
The Button should open the Batch Process Window which is already available in the native "Order" Window of shopware.
Q: How Can I open this app with the selected Ids of my grid?
Heres what I have:
[...]
createToolbarButton: function () {
var me = this;
return Ext.create('Ext.button.Button', {
text: 'Batch Processing Orders',
name: 'customBatchProcessButton',
cls: 'secondary',
handler: function () {
me.onClickCustomBatchProcessButton(me);
}
});
},
onClickCustomBatchProcessButton: function(me){
var thisGrid = me.getTransferGrid();
var records = thisGrid.getSelectionModel().getSelection();
console.log("Grid");
console.log(thisGrid);
console.log("records");
console.log(records);
Shopware.app.Application.addSubApplication({
name: 'Shopware.apps.Order',
action: 'batch',
params: {
mode: 'multi',
records: records
}
});
}
[...]
It always opens the normal view of the order window. (no error in console)
Anybody has a suggestions?
That would be great!
Thanks for your time :)
Greetings
EDIT:
Hey, thank you for your reply so far.
I managed to open the Batch-process-window like this:
me.getView('Shopware.apps.Order.view.batch.Window').create({
orderStatusStore: Ext.create('Shopware.apps.Base.store.OrderStatus').load(),
records: orderRecords,
mode: 'multi'
}).show({});
But now the Problem ist, the Event for the Batch-Process isn't applied on the button on the form...
I am still on try and error.
Many Shopware ExtJS SubApplications can be executed from another app with certain parameters exactly the way you're trying to. Unfortunately I don't see any code in the Order plugin that might lead to the desired result. You can see what actions/params a Shopware SubApplication supports by reading the init function of the main controller -> Shopware.apps.Order.controller.Main
Shopware.apps.Customer.controller.Main from the Customer plugin for example accepts an action like you are using it – it is checking for this:
if (me.subApplication.action && me.subApplication.action.toLowerCase() === 'detail') {
if (me.subApplication.params && me.subApplication.params.customerId) {
//open the customer detail page with the passed customer id
...
In the Order plugin there is similar code, but it just takes an order ID and opens the detail page for the corresponding order. It apparently doesn't feature the batch.Window
You might be able to reuse this class somehow, but that might be a ton of code you need to adapt from the actual Order plugin. If you really need this feature, you can carefully read how the Order plugin is initializing the window and its dependencies and have a try.
I'd rather go for developing a lightweight module in this scenario (It's a frame within a backend window that just uses controllers and template views with PHP/Smarty/HTML)

Bootstrap Dialog Global options?

I have many dialog popups spread across many pages, each utilizing BoostrapDialog
BootstrapDialog.show({...});
or
var x = new BootstrapDialog({...});
I want to add the "closeByBackdrop: true" option to all of them.
Is there a way to define some default options? I'm thinking along the same lines as jQuery's ajaxSettings() where you can set global options (but still override them in each specific instance if necessary)
$.ajaxSetup ({
cache: false,
error: function(x, t, m){
alert("Generic Error");
}
});
I cant't find any information remotely related to a global options setter anywhere...which leads me to think there is no such thing. wondering if someone knows of this... or even better has implemented their own way of achieving this.
The obvious solution is to manually go through all my dialogs and add the option manually, but since this is the preferred way they should ALL work, i'd like to not have to specify it for every instance.
The other solution would be to re code them all to accept my own options object, and just extend that object for each instance:
$(document).ready(function(){
modalOptions = {...};
});
function openDialogX(){
var dialogXOptions = modalOptions;
dialogXOptions.buttons = [...];
dialogXOptions.title = "...";
dialogXOptions.message = "...";
var dialogX = new BootstrapDialog(dialogXOptions);
}
It would be nice to not have to re-code them all to accept a custom options object.
TL;DR:
Is there a BoostrapDialog equivalent to jQuery's ajaxSettup() for redefining default options. If not, what solutions have you used to achieve this (if at all).
You do have a BootstrapDialog.defaultOptions around line 231 in the master repo. Use that to modify the defaults to whatever you need :
$.extend( BootstrapDialog.defaultOptions, {
closable: true,
closeByBackdrop: true,
autodestroy: false
} );
and take benefit of the new settings in all dialogs :
var myDialog = new BootstrapDialog({
title: 'Example',
message: 'Sample text'
}).open()
demo -> http://jsfiddle.net/ffj6r1b6/ (try change closeByBackdrop to false and re-run)

Equivalent of subclassing/interface in Javascript

Preamble: I'm somewhat new to Javascript. I'm currently building a game with CraftyJS (if it matters). I'm making something akin to an RPG.
I want to model a bunch of types of messages which can be specified to show to the user. These can be:
A simple text message (reference)
A text message with an avatar image (reference)
A text message and a bunch of choices (reference)
Possibly more types in the future
Under a language like C# or Java, I would probably choose to model this as a Message class (perhaps with fields for avatar/choices), or with subclasses, or as an interface for my specific message types to implement.
In Javascript, classes are structured differently. What's the correct way to model this? I see a few options right now:
Anonymous objects with just a bunch of fields, eg. { text: "blah", choices: [ ... ] }
Some sort of class, eg. new Message(text, avatar, choices)
Insert option here
Since my game is data-driven, users will be creating JSON (possibly by hand) and specifying message fields. A slight advantage of a class, is that I force users to specify all necessary fields in the constructor (although I can still validate messages without classes if I just use anonymous objects).
What are my options and trade-offs (or what design should I use here?)
If the message "facility" is open ("Possibly more types in the future"), then you may want to investigate a different approach:
Have a class for the user interface that exposes the appropriate methods to manipulate it, e.g.:
function MessageUi() {
....
}
MessageUi.prototype.setText = function(text) { ... }
MessageUi.prototype.setPicture = function(imgUrl) { ... }
MessageUi.prototype.setChoices = function(choicesArray) { ... }
MessageUi.prototype.show = function(callback) { ... }
// and so on
The message classes are required to implement a single method, say render(ui). Example usage would be:
For the simple text case:
var msg = {
render: function(ui) {
ui.setText("My daddy used to hide...");
ui.show(); // no callback; clicking just goes on
}
};
For the choices case:
var msg = {
render: function(ui) {
ui.setText("Maria! So nice to meet you!");
ui.setChoices([
{value: 1, text: "Yes, sir. Very nice to meet you...."},
...
]);
ui.show(function(choice) { // callback gets selected choice
...
});
}
};
As new types of messages are needed, the existing ones need no modification. You will only add methods to MessageUi and the new message types will make use of them.
Just some thoughts, this is not intended to be a "final" solution (the question is quite open ended). Have fun developing the game!

Dojo DataGrid Virtual Scrolling How-To?

I've been digging around for this one quite a bit. I'm using dojox.grid.datagrid and I have an ajax call that brings back 200-300 rows.
The grid renders and scrolls just fine in Chrome but scrolling is excruciatingly slow in IE 7 and 8. I'd like to use virtual scrolling to try and remedy the issue but can't find any sample code.
Here's what my code looks like at present.
function setupAvailableScenes(location) {
var avaiableScenesGridPane = dijit.byId("AvaiableScenesGridPane");
var availableScenesGrid = dijit.byId("AvailableScenesGrid");
if (_isFirstLoad) {
availableScenesGrid = new dojox.grid.DataGrid({
id: 'AvailableScenesGrid',
store: availableScenesStore,
query: { Id: "*" },
sortInfo: "1",
rowsPerPage: 20,
autoHeight:20,
style: "width:315px",
structure: [
{ name: "Available Scenes", field: "Name", width: "85%" },
{ name: " ",
field: "_item",
rowsPerPage: "25",
type: dojox.grid.cells._Widget,
editable: false,
width: "15%",
formatter: function (scene) {
return new dijit.form.Button(
{
label: "+",
onClick: function () {
AddSceneToSelectedScenes(scene);
}
})
}
}
]
});
avaiableScenesGridPane.set('content', availableScenesGrid);
}
var availableScenesStore = new dojo.data.ItemFileWriteStore({
url: _applicationPath + "/Location/" + location.Id + "/Scene.json",
preventUrlCache: true
});
availableScenesGrid.setStore(availableScenesStore);
}
Often one of the biggest things you can do to improve DataGrid performance is to throw away the ItemFileReadStore/WriteStore and use an optimized data store (personally I like QueryReadStore). It would mean needing a server-side servlet of some kind (PHP/JSP/etc) to handle the virtual scrolling/pagination, but I've seen major perf boosts over just using a store backed by a JSON file.
Some other things to consider, which may or may not help:
give your anonymous formatter function a name and try scrolling the table with the Chrome or Firebug profiles turned on to see if it's hogging a lot of cycles (or, like Vijay Agrawal said, you could try replacing the dijit.form.Button with a vanilla html <button> tag)
you shouldn't actually need to specify the dojox.grid.cells._Widget type for that cell; having a custom formatter returning a valid Dijit should be sufficient to make the Grid do the right thing.
Since you specified rowsPerPage=25, it is already doing virtual scrolling (it pulls the new set of rows only when user scrolls down)
Since you mention scrolling is very slow, the performance issue seems to be around rendering the new rows - you may try a couple things to improve performance:
1) remove autoHeight attribute. Instead, specify a fixed height in the style attribute
2) in the formatter function, instead of returning a dijit, try returning a simple html button/anchor styled as button
so remove the type:dojox.grid.cells._Widget attribute and in the format function return the html you want to use

Categories