I'm customising mxGraph and am looking into custom properties. I'm using the mxDraw template. Creating custom properties for a shape is easy enough such as the below (modifying diagrameditor.xml):
<add as="rectangle">
<Rect label="Rectangle" href="" new_property="hi there">
<mxCell vertex="1">
<mxGeometry as="geometry" width="80" height="40"/>
</mxCell>
</Rect>
</add>
When using mxDraw these properties are immediately visible and all is well, except I want to be able to choose a value for this property using a drop-down menu.
I'm having trouble finding an example of this elsewhere on the internet, and I'm not really sure what my options are here, or where to start.
In my mind there are a few options, but am hoping I can get some guidance on where to even start here...
Disregard custom properties like the above and instead use a separate DIV, an event listener for selected shape and use HTML forms to capture properties/values
Modify diagrameditor.xml like the above code snippet and somehow modify existing javascript library
Some sort of hybrid of the above 2?
Eventually the resulting graph XML/data will be POSTed to the web server for storage, which can then merge/correlate data accordingly.
I know SO has some experienced mxGraph programmers so hoping to get some thoughts from those more experienced than I, so I can go and research. I can't seem to find what I'm looking for so far (in terms of guidance, examples or others attempting a similar thing).
Any help appreciated, thanks.
I kept researching and found a solution that suits me.
Update diagrameditor.xml and create custom data fields. Example shown in my original post
Create a new DIV on the page
Setup a listener for cell selection, add some HTML forms then use mxGraph calls to update the property values
To be done: disable the right-click menu so it cannot be updated manually using the text field, to ensure inputs are valid
Below is the starting code for the HTML page.
<div id="customdata" style="position:relative;padding-left:100px;padding-top:10px;">
<script>
var selectedCell;
mxGraph.prototype.addListener(mxEvent.CLICK, function(sender, event){
selectedCell = event.getProperty('cell');
//update DIV content example. Show cell ID and a drop-down to update a custom data field called 'new_property' (as created in diagrameditor.xml)
var newhtml = "Cell ID is: ";
newhtml += selectedCell.getId();
newhtml += `<select><option id="selectmenu" value="one">one</option><option value="two">two</option></select>`
newhtml += `<button onclick="updateCustomData();">save</button>`
document.getElementById("customdata").innerHTML = newhtml;
//Can also iterate through a multiple-selection and do more things, if need be
/*
for (var i = 0; i < sender.getSelectionCount(); i++) {
//example, alert showing the label of the cell at this array index i
alert(sender.getSelectionCells()[i].getAttribute('label', ''));
}
*/
});
function updateCustomData() {
selectedCell.setAttribute("new_property", document.getElementById("selectmenu").value);
}
</script>
div content
</div>
I hope this question can be left here to serve as potential help for others. I'm still a new SO user, great community. Hope this can be my little contribution.
Related
Good day,
I have been trying for over a day now to figure out a way to dynamically add elements to a web page using visual studio and access their values. I am either over-complicating the whole thing, I'm being dumb, or there is genuinely no way in which to do what I'm trying to do haha.
I am trying to create a page that should be able to generate possibly over 100 of the following html item sets:
It is basically a question and answer form for creating a multiple choice test. There is an area to type a question and then additional text boxes just to add in the multiple choice. There is also a button that can add an extra 2 textboxes to the answer area (whether they are created and added to a div in the table row or hidden and then revealed when the button is pressed.)
[Textbox for question]
[Textbox][Textbox][Textbox][Textbox] [Button that adds additional textbox to this row (only an extra 2 at maximum)]
[Confirm button to submit the text in the textboxes]
[Button that adds another copy of the elements above (up to 100)]
I would love to create just a normal form (as that's probably easiest), but this needs to work on the web. I know keeping track of all of this content can be a nightmare, but I can't think of an alternative (well, I have thought of an alternative but it seems to be more complicated). I have tried the following:
I tried adding a div into a table cell to add the textboxes, but I couldn't access the div afterwards. Is there a way to do this? If I could access the elements I create using the innerHtml method, I would be more or less fine, but I cannot find anything online.
QuestionContent.InnerHtml += "<table runat=\"server\" style=\"width: 100 %; \"><tr><td> Question " + _QuestionNum + " </td></tr><tr><td class=\"auto - style1\"><div id=\"divQ"+_QuestioNum+"\"></div></td></tr></table>";
//Might have an issue adding an element with runat=server, but this was the only way I could think of extracting a value that is created dynamically in C#.
((System.Web.UI.HtmlControls.HtmlGenericControl)mainPanel.FindControl("divQ1")).InnerHtml = "<asp:TextBox ID=\"txtChoice1\" runat=\"server\">Choice1</asp:TextBox>";
//Here I try to access the div created above. Unrelated: Also tried with a normal textbox, but I realise that creating an asp textbox here might cause issues.
I just cannot seem to access anything I create in C#. Is there a better way? Upon giving up on this idea, I tried creating elements using javascript. I still need to find out if I can pass values through to C# using javascript, but I am stuck with another issue now. Whenever the below code runs, the elements appear but then immediately disappear. Is this an issue with asp.net? The google results weren't very helpful.
function addQuestion() {
if (numQuestions == limit) {
alert('Maximum question limit reached. Consider breaking this test up into multiple tests.');
}
else {
var newdiv = document.createElement('div');
newdiv.setAttribute('id', 'divQ' + numQuestions);
newdiv.innerHTML = '<table><tr><td> Question' + numQuestions + '</td></tr><tr><td>';
for(var i=0;i<6;i++){
newdiv.innerHTML += "<input type=\"text\" name=\"textBox"+i+"\">";
}
newdiv.innerHTML += '</td></tr></table>';
numQuestions++;
document.getElementById('divQuestionContent').appendChild(newdiv);
}
}
However, I would like to avoid using javascript if I can. Is there a better way to create this page? Have I simply done something wrong? Any help will be greatly appreciated.
I think you are mixin server and client side code.
1st) Create html form with your first known necesary fields
2nd) Javascript part. User add fields to the form (need to be done in Javascript, client side ), check this answer:
How add new hidden input fields to the form on submit
3rd) C# part . On form request, you need to loop over all request.form items, as you can't know how many they are. This can be done with something like this:
StringBuilder s = new StringBuilder();
foreach (string key in Request.Form.Keys)
{
s.AppendLine(key + ": " + Request.Form[key]);
}
string formData = s.ToString();
I've created a graph and am setting cells onto it via drag and drop. However when a drop or click happens I cannot find the clicked cell's html for use.
For instance I'm trying to get the ID of the dragged element within the mxEvent.CLICK.
graph.addListener(mxEvent.CLICK, function(sender, evt){
var cell = evt.properties.cell;
// cell.id is the cell id, not an id of html that's inside of it.
if(cell) {
var outer = cell.value.outerHTML; // I can see the HTML here encoded
}
})
UPDATES:
So I add the html to the cell with value.setAttribute('htmlLabel', label); when my draggable items are created. However htmlLabel does not seem to be a key anywhere in the evt object.
I've been working with MxGraph for a few months now on a large project, and while it's a great library, it's a bit cumbersome to use in some areas.
Getting at the HTML is one of those areas.
First off, you need to grab the objects of the event using the "MxGraph" tool set, as follows:
graph.addListener(mxEvent.CLICK, function(sender, event){
var mouseEvent = event.getProperty('event');
var selectedCell = event.getProperty('cell');
});
As you've already worked out, you can get the cell ID from the selected cell, via it's ID property, but other than that, there's not a great deal of other things on that object.
To go deeper, you need to drill down in the mouse event that you also get.
It's just a normal HTML event and as such has the regular properties on it you would expect to find.
In your case
mouseEvent.currentTarget.innerHTML
Will get you the HTML of the element you clicked on (You may need to explore different paths in your debugger, but that certainly works for me in the latest version of chrome)
One thing you need to be aware of though, MxGraph renders ALL of it's output using SVG and as far as I can see there is NO standard HTML ID attribute on the rendered tag, in fact none of the rendered graph elements appear to have ID's on them.
I hit this problem a week ago when I was trying to grab the output generated by MxGraph to do some image manipulation, and because I couldn't reliably grab a single image, I had to resort to using Base64 encoded strings and manipulating the cell stylesheets using MxGraph's API.
I used xml to create the diagram:
...
<mxCell id="start" value="Start" style="start" vertex="1" parent="1"><mxGeometry x="0" y="0" width="30" height="30" as="geometry"/></mxCell>
...
So I get the ID with this code:
graph.addListener(mxEvent.CLICK, function (sender, evt) {
var cell = evt.getProperty("cell");
var id = cell.id;
}
Version 3.9.11
in version 3.9.8
I tried to use the above code, but found error with (graph), with more search and try, I found the suitable is (mxGraph.prototype), so this code should be:
mxGraph.prototype.addListener(mxEvent.CLICK, function(sender, event){
var mouseEvent = event.getProperty('event');
var selectedCell = event.getProperty('cell');
});
I've got a pretty simple problem whose solution turns out not to be that simple at all.
I want to add images in front of each option of a selectfield. To be more accurate, I want to add images to the picker it triggers, and also to the selectfield's current value.
For the sake of simplicity, I'll create a little example:
Let's say, you want a user to choose between one of the four playing card suits Diamonds, Hearts, Spades and Clubs. To support visual recognition, you want to prepend the corresponding symbol to each suit name, so it could look something like this:
My first choice of a sencha touch component, that enables selecting from a given set of options naturally was selectfield. Unfortunately, it only seems to be able to display pure text, and nothing more. After digging into the sencha touch sources, I finally came up with half a solution. Basically, I pass the selectfield a custom defaultPhonePickerConfig, in which the corresponding picker(that is used by the selectfield to display the options) gets assigned a custom itemTpl. The itemTpl does the rest, namely adding some html to display the image:
defaultPhonePickerConfig: {
listeners: {
initialize: function() {
var slots = this.query('pickerslot');
Ext.each(slots, function(slot) {
slot.setItemTpl('<img src="someImage.jpg"> {text}');
});
},
change: function() {
// reconstruct the selectfield's change handler,
// since it gets overwritten
var iconSelect = Ext.Viewport.query('#IconSelect')[0];
iconSelect.onPickerChange.apply(iconSelect, arguments);
}
}
}
A working fiddle for this solution can be found here.
My solution isn't that bad, but there's a slight cosmetical problem, that's just not acceptable to me: The icons are only displayed in the picker (lower part of the screenshot above), but not the selectfield itself (upper, grayed out part) when the option was selected. And there seems to be no good way to add an icon to the selectfield's current value aswell.
And that's the main concern of my question: What good way is there to add an icon to both the picker's options and also to the selecfield's current value? Do I maybe just have to add relatively little code to my existing solution or should I take a whole nother approach?
Every contribution is appreciated. Thank you!
Update:
After some hacking around, I found a way (an ugly one) to prepend an icon to the selectfield itself. It is mostly based on brutal HTML DOM manipulation: I now also define event handlers for the selectfield itself (change and painted). On initialization and every time the value is changed, my handlers search the generated DOM for the underlying <input> and mess around with it. (That bad boy is probably the reason why we can't assign HTML in the first place, since the framework changes its value attribute. And value on the other hand can only contain plain text.)
This is how I define the selectfield's listeners:
listeners: {
change: function () {
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(arguments[1], pickerDOM);
},
painted: function () {
// Initialize an icon on creation
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(this.getValue(), pickerDOM);
}
}
The corresponding function prependIconToSelectfield() just defines some CSS:
prependIconToSelectfield: function (optValue, domElement) {
var iconUrl = this.getIconUrl(optValue);
domElement.style.backgroundImage = 'url(' + iconUrl + ')';
domElement.style.backgroundSize = '20px 20px';
domElement.style.backgroundRepeat = 'no-repeat';
domElement.style.backgroundPosition = 'left center';
domElement.style.paddingLeft = '30px';
}
Check out this fiddle for a working example.
This is still no good solution to me, since doing this kind of hackish DOM manipulation is way too rogue for my taste. I don't know what the side effects of actively messing around in the DOM could be in bigger projects, and don't want to learn it the hard way. So, I'm still looking for a cleaner solution.
First kudos on working so hard sencha touch is extremely hard to manipulate when you try to do something out of the box. Having said that let me try & propose a solution for what you want.
A selectfield in sencha has the following DOM tag structure.
div.x-field-select
div.x-field-input
input.x-input-el
div.x-clear-icon
div.x-field-mask
Now concentrate on the x-clear-icon it is normally hidden since a selectfield does not need a clear button. First write a css class for it to show it(display: block). This would display it with an X button similar to text field & it will be positioned towards the right corner. You can through css position it to the left and on change of the select field you can change its background to what you want. It is not a very straight forward solution but i have tried it for a similar problem & it works. Judging from what you have done above i think you can do it. All the best.
Hope my solution helps.
I'm working on a website for my custom themes on a blogging site. I want to have a review section, but I only want to have one review per page refresh. I was wondering if there is any way to write a script with jQuery or javascript (I'm not sure which one) to pick a random element from an array (All of the reviews will have a seperate element, each person having a different div ID with a different review) and display that element inside a div called #reviews, and hide the other elements? It sounds very confusing, but basically I need either jQuery or javascript to pick a review and put it in the review section. If anyone could help it'd be greatly appreciated.
Here's one way jsFiddle
<aside id="reviews">
<article class="review"></article>
<article class="review"></article>
<article class="review"></article>
</aside>
and
var $reviews = $('#reviews .review').hide();
$reviews.eq(Math.random()*$reviews.length).show();
But unless you plan on revealing the other reviews at some point (slider, or pagination), you should really do this server side. Sending people content just to hide it is a little wasteful.
jQuery is JavaScript. To get a random element from an array, use Math.random():
function getRandomElement(a) {
return a[Math.floor(Math.random() * a.length)];
}
I would use knockout.js which is great for binding JavaScript objects to DOM. You can fill an array of your object and data-bind it to the div. Each time you will bind a random element of the array to the div. The data binding is very simple:
<p>First name: <input data-bind="value: firstName" /></p>
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.firstName = "Bert";
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());
For more examples see the great tutorials in:
Knockout.js tutorials
Hi I am trying to implement some drag and drop functionality into a project but am having a bit of difficulty with a few aspects. One of the problems I am having is with the creation of a custom avatar, I am able to achieve a custom avatar but have to cheat slightly and hide some elements with CSS.
Below is a snippet of my code not the entire code, I have required all necessary pacakges etc.
dojo.query(".canDrag").forEach(function(node, index, nodelist){
var createSource = new dojo.dnd.Source(
node, {copyOnly: true, creator: createAvatar}
);
function createAvatar(item, hint) {
var node = dojo.doc.createElement("span");
dojo.addClass(node, "avatarStyle");
if ( hint == "avatar" ) {
var dHtml = item;
console.log("trying " + dHtml);
node.innerHTML = item;
}
else {
console.log("if this show remove redudant call");
}
return {node: node, data: item, type: "product", copyOnly: true};
};
Ok so as you can see I create my DnD source then give it a custom creator where I attempt to build my own custom Avatar. The actyual draggable markup is below:
<div class="canDrag panelLeft">
<div class="dojoDndItem" dndType="product" title="digitalCamera" id="12345">
<h3 class="productTitle">Samsung ES71</h3>
<p class="productType">Compact Digital Camera</p>
<img src="img/small_Cam.png" class="imgLMargin" alt="Samsung ES71"/>
<div class="dragHandle">
</div>
</div>
</div>
Rather than append the entire div from canDrag and down I would like to grab different elements such as the image and .product title and just display those. If any one has any ideas I thank you in advance also if my question has not been clear enough I can try to rephrase.
Cheers
CSS should be fine. Otherwise, you can either use the dndData attribute for your items, or add the items manually to your DnD source object.
When using dojoDndItem class, Dojo expects the visualization of the avatar to be already resolved in the markup itself. That's why it passes the inner HTML as the item data. This is for the most simple and common case, when you would not use a custom creator. I think using CSS to customize the avatar is fine. Even if you don't use a custom creator to set the avatarStyle class, you can take advantage of the fact that Dojo puts the avatar inside its own container marked with the class dojoDndAvatar (or dojoDndAvatarItem). Take a look at the source at dojo/dnd/Avatar.js.
If you still want to use a custom creator, you have a couple of options:
Add a dndData attribute to your items. In that case, that's what gets passed to the creator function as the item parameter. It can be anything, and you can use that to further customize the avatar. Eg. you could serialize a JSON object and dynamically create the avatar from that object, or you could set it to a unique id and then use dojo.query() to access the nodes below it.
Remove the items entirely add them programmatically with the insertNodes() method. In that case, your creator function must implement both the case for the avatar and the case for the actual item.
It doesn't address your question in particular, but here's an excellent Dojo DnD tutorial.
The Dojo reference guide is also helpful, once you understand what's happening. And of course, use the source Luke!