I want to add an Id to the element which is being edited using a tine mce instance ? Is there any way ?
Yes, there is a way. You need to get the selections parent node and apply the attribute id there:
ed.onDesiredEvent.add(function(editor, event) {
node = editor.selection.getNode();
node.setAttribute('id','the_id_to_be_assigned');
}
Related
As an example, I have a Master\Detail grid.
Master\Detail defined as key-relation model and on getDetailRowData method parent node data exists in params
but how to get parent node data from child view?
Tried via context-menu:
On right click - getContextMenuItems got executed which has an input params
On this sample, child-grid doesn't have any row and node in context-params is null, it's ok,
but anyway it should be possible to retrieve the parent details at least via grid API, isn't it ?
Then I've tried to get parent via node:
but instead of detail_173 as you can see its ROOT_NODE_ID, and here is a confusion for me.
So question is that how to get parent node data (RecordID 173 just in case) through child-view context menu or any other possible way (without storing temp value, cuz multiple children's could be opened at the same time)
Yes, I've read this part Accessing Detail Grid API, and still unclear how to get parent-node via child-grid.
For React Hook users without access to lifecycle methods, use Ag-Grid Context to pass the parent node (or parent data) to the detail grid via detailGridOptions. No need to traverse the DOM or use a detailCellRenderer unless you want to :)
https://www.ag-grid.com/javascript-grid-context/
detailCellRendererParams: (masterGridParams) => ({
detailGridOptions: {
...
context: {
masterGrid: {
node: masterGridParams.node.parent,
data: masterGridParams.data
}
},
onCellClicked: detailGridParams => {
console.log(detailGridParams.context.masterGrid.node);
console.log(detailGridParams.context.masterGrid.data);
}
}
})
Able to achieve it. Have a look at the plunk I've created: Get master record from child record - Editing Cells with Master / Detail
Right click on any child grid's cell, and check the console log. You'll be able to see parent record in the log for the child record on which you've click.
The implementation is somewhat tricky. We need to traverse the DOM inside our component code. Luckily ag-grid has provided us the access of it.
Get the child grid's wrapper HTML node from the params - Here in the code, I get it as the 6th element of the gridOptionsWrapper.layoutElements array.
Get it's 3rd level parent element - which is the actual row of the parent. Get it's row-id.
Use it to get the row of the parent grid using parent grid's gridApi.
getContextMenuItems: (params): void => {
var masterId = params.node.gridOptionsWrapper.layoutElements[6]
.parentElement.parentElement.parentElement.getAttribute('row-id');
// get the parent's id
var id = masterId.replace( /^\D+/g, '');
console.log(id);
var masterRecord = this.gridApi.getRowNode(id).data;
console.log(masterRecord);
},
defaultColDef: { editable: true },
onFirstDataRendered(params) {
params.api.sizeColumnsToFit();
}
Note: Master grid's rowIds are defined with [getRowNodeId]="getRowNodeId" assuming that account is the primary key of the parent grid.
A very reliable solution is to create your own detailCellRenderer.
on the init method:
this.masterNode = params.node.parent;
when creating the detail grid:
detailGridOptions = {
...
onCellClicked: params => console.log(this.masterNode.data)
}
Here is a plunker demonstrating this:
https://next.plnkr.co/edit/8EIHpxQnlxsqe7EO
I struggled a lot in finding a solution to this problem without creating custom details renderer but I could not find any viable solution. So the real good solution is already answered. I am just trying to share another way to avoid creating custom renderer.
So What I did is that I changed the details data on the fly and added the field I required from the parent.
getDetailRowData: function (params: any) {
params?.data?.children?.forEach((child:any) => {
//You can assign any other parameter.
child.parentId= params?.data?.id;
});
params.successCallback(params?.data?.children);
}
When you expand the row to render details the method getDetailRowData gets called, so it takes in params as the current row for which we are expanding the details and the details table is set by invoking params.successCallback. So before setting the row data I am iterating and updating the parentId.
I have got a template that shows tiles in a particular order:
<template name="container">
{{#each tiles}}{{>tile}}{{/each}}
</template>
Now the container is a list of tiles that is stored as an array in mongodb.
Since I want the tiles to be shown in the same order as they appear in that array, I'm using the following helper:
Template.container.tiles = function () {
return _.map(this.tiles || [], function(tileId) {
return _.extend({
container: this
}, Tiles.findOne({_id: tileId}));
}, this);
};
};
The problem is, that I:
Do not want the entire container to rerender when the any of it's contain tiles changes. (Only the relevent tile should be invalidated).
Do not want the entire container to rerender when a new tile is inserted. The rendered tile should be simply appended or insteted at the respective location.
Do not want the entire container to rerender when the order of the tiles is changed. Instead, when the order changes, the DOM objects that represent the tile should be rearranged without re-rendering the tile itself.
With the above approach I will not meet the requirements, because the each tiles data is marked as a dependency (when running Tiles.findOne({_id: tileId})) of the entire container and the entire array of tile-ids is part of the containers data and if that changes the entire container template is invalidated.
I'm thinking I should mark the cursor for the container as non-reactive. Something like this:
Containers.findOne({_id: containerId}, {reactive:false});
But I still need to find out when this container changes it's tiles array.
So something like
Deps.autorun(function() {
updateContainer(Containers.findOne({_id: containerId}));
});
But I want that container template to be highly reusable. So whatever solution there it should not require some preparations with dependencies.
Where do declare I run that autorun function? (surely i cannot do that in that helper, right?)
Is this the right approach?
Does anybody have better ideas on how to solve this problem?
Thanks in advance...
The way I usually approach this problem is by creating an auxiliary Collection object and populate it with a help of appropriate observer. In your case this might be something like:
// this one should be "global"
var tiles = new Meteor.Collection(null); // empty name
Now, depending on the current container, you can populate the tiles collection with corresponding data. Also, you'll probably need to remember each object's index:
Deps.autorun(function () {
var containerId = ... // get it somehow, e.g. from Session dictionary
var tilesIDs = Containers.findOne({_id:containerId}).tiles;
tiles.remove({}); // this will be performed any time
// the current container changes
Tiles.find({ _id : { $in : tilesIDs } }).observeChanges({
added: function (id, fields) {
tiles.insert(_.extend({
_index : _.indexOf(tilesIDs, id),
_id : id,
}, fields);
},
changed: ... // you'll also need to implement
removed: ... // these to guys
});
});
The helper code is now really simple:
Template.container.tiles = function () {
return tiles.find({}, {sort : {_index : 1}});
}
EDIT:
Please note, that in order to prevent the whole list being rerendered every time the container object changes (e.g. the order of tiles changes), you'll need to make a separate listOfTiles template that does not depend on the container object itself.
I'm trying to prevent the dragging and dropping of nodes outside of the parent node ("LLCA") with no luck.
Any suggestions?
Image of Treeview
I ended up getting it to work using your code below:
function onDrop(e) {
var dst = e.destinationNode;
var first = $('.k-item:first');
var pos = e.dropPosition;
if (dst && dst.uid === first.uid && pos !== "over") {
e.setValid(false);
}
}
Lets define the treeview:
var tree = $("#tree").kendoTreeView({
dataSource :content,
dragAndDrop:true
}).data("kendoTreeView");
What I'm going to do is add a drop callback where I will control that:
We are not dropping outside the tree
We are not dropping before or after the first node of the tree
The definition of the tree would be:
var tree = $("#tree").kendoTreeView({
dataSource :content,
dragAndDrop:true,
drop :function (ev) {
var dst = tree.dataItem(ev.destinationNode);
var first = tree.dataItem(".k-item:first");
var pos = ev.dropPosition;
if (dst && dst.uid === first.uid && pos !== "over") {
console.log("invalid");
ev.setValid(false);
}
}
}).data("kendoTreeView");
Check http://docs.kendoui.com/api/web/treeview#drop for information on drop event.
Because I cannot comment on an answer, I will write my own.
User Mithrilhall asked about MVC wrappers, also the top answer only prevents movement to the root node.
I will attempt to answer both Mithrilhall and provide an example where you can only move a child within the context of its parent. To put it another way, to only allow children of any parent to change their order within the parent.
Firstly, for MithrilHall, this is how you get to the events in MVC.
#(Html.Kendo().TreeView()
.Name("ourTreeView")
.Events(e => e.Drop("treeViewDrop"))
There are other events in treeview, you can take a gander for yourself. The argument is the name of a javascript function. Here is an example javascript function for this MVC wrapper to prevent children from moving outside of their parent, but allowing them to still move within the parent.
<script type="text/javascript">
function treeViewDrop(dropEvent) {
var treeView = $("#ourTreeView").data("kendoTreeView");
var destination = treeView.dataItem(dropEvent.destinationNode);
var source = treeView.dataItem(dropEvent.sourceNode);
if (!(destination && destination.parentID == source.parentID)) {
dropEvent.setValid(false);
}
}
</script>
I had a parentID field modeled in my datasource. You could accomplish this in many ways. The dataItem method returns a kendo treeview item, so it has all of your modeled fields in it.
Also understand, this solution does not change the widget to show an X when you are moving to a place you cannot drop to. This is another problem with another solution.
I hope this helps, good luck!
I am having difficulties by selecting child nodes when parent is also selected , also would like to open subfolders and select all childs (not sure if im making it clear) so I have know how to get all the child nodes by:
selected_nodes = $("#demo").jstree("get_selected", null, true);
var tree = jQuery.jstree._reference('#demo');
var children = tree._get_children(selected_nodes);
but doest really select or open child folder and nodes
This might not be what OP wanted but on JSTree 3.3. I used this to open all nodes under a selected parent.
$(treeContainer).bind("select_node.jstree", function (e, data) {
return data.instance.open_all(data.node);
});
I gave up on this Idea.. now im just doing ajax requests to load the childs and receive a json object, and from there i can see if its a file or directory, if its a directory again another request and like this i have the whole structure
You have to turn on two_state checkboxes to allow parent and child nodes to be selected indepentently. Below I've configured the "two_state" parameter for the checkbox plugin:
$("#docTree").jstree({
"themes": {
"theme": "classic",
"url": "jstree/themes/classic/style.css"
},
"plugins": ["themes", "ui", "checkbox", "json_data"],
"checkbox": { "two_state" : true }
})
Check documentation here: http://www.jstree.com/documentation/checkbox
If I understand this correctly you want to select all of the lowest descendants in a jsTree (the files) when one of their parents (the folders) have been selected.
Unfortunately I haven't found a more direct approach than the one below. I'm using jsTree 3.1.1 and managed to solve this problem using the following:
var $demo = $("#demo");
var nodes = $demo.jstree("get_top_selected", true);
//Selects all of the children of each selected node including folders
if(nodes.length > 0){
nodes.forEach(function(node, i){
$demo.jstree("select_node", node.children_d, true, false);
});
}
var fileNodes = $demo.jstree("get_bottom_selected", false);
//We now need to deselect everything and only select the file nodes
$demo.jstree("deselect_all", true);
$demo.jstree("select_node", fileNodes, true, false);
The above code allows the user to "select" multiple folders and have those files selected.
The parameter with the value true for the "get_top_selected" function returns the full node instead of just ID's. This lets you get access to the node's children.
Function "select_node" takes three parameters. The first is the nodes to select (in this case the descendants of the current parent node). If the second parameter is set to true it prevents the changed.jstree event from firing for each selected node. The third parameter is set to false as setting to true prevents the children folders from opening.
The function "get_bottom_selected" only returns a node if it has both been selected and it itself does not have any children. As we are now only interested in the node ID's we can pass the parameter false (or omit it completely).
Passing a parameter of true to the function "deselect_all" again prevents the changed.jstree event from firing.
If you want to check out the jsTree API docs you can find them here. That list is filtered to only include the select functions and events. A full list of the API can be found here.
I hope this helps but let me know if you require further clarification on any of the code ^_^
select all child nodes when parent selected ,
$(data.rslt.obj).find("li").each( function( idx, listItem ) {
var child = $(listItem); // child object
$.jstree._reference("#associateRightHandTree").check_node(child);
});
unselect all child nodes when parent unselected ,
$(data.rslt.obj).find("li").each( function( idx, listItem ) {
var child = $(listItem); // child object
$.jstree._reference("#associateRightHandTree").uncheck_node(child);
});
I have looked at the this link for a tutorial on dojo drag and drop feature. But one thing I have noticed is that in all cases of the examples, the items to be dragged around are always a simple item, just a string object...
I need to create something like an item group where you can drag an item into the item group to append into the group and to drag the item group around as a whole.
Hence my question, is it possible to drag and drop a dojo.dnd.Source item into another dojo.dnd.Source item?
Short answer: no. Many people tried to patch it, but found more and more non-working edge cases, so those patches never made the Dojo proper.
If you truly need to show and manipulate a hierarchical data, consider a Tree Dijit.
The problem is that when you start dragging and you drag over a Source of a child container, everything gets messed up. (Not exactly sure how). What you can do, is hide those child sources so that their overSource events never trigger:
1) Overrode the checkAcceptance function in Source.js. Just added the following for the if(!flag) return false;:
if(!flag){
/**
* Main Source
* - Group 1
* -- Child 1
* -- Child 2
* - Group 2
*/
var node = dojo.byId(this.node);
// If the node being moved is the source, skip, but don't hide from view.
if('#'+dojo.attr(source.node, 'id') != '#'+dojo.attr(node, 'id')){
// If the node being moved is an immediate child of the container, you can move it.
if(dojo.query('#'+dojo.attr(source.node, 'id') + '>#'+dojo.attr(node, 'id')).length) {
return true;
}
// If this source is not a parent of the element, hide it.
if(dojo.query('#'+dojo.attr(node, 'id') + ' #'+dojo.attr(source.node, 'id')).length == 0)
dojo.addClass(node, 'hiddenSource');
}
return false;
}
2) You need to also add the following as the first line under if(this.isDragging) in onMouseMove (important)
var node = dojo.byId(this.node);
// If this is immeditae child, drop it.
if(dojo.query('#'+dojo.attr(m.source.node, 'id') + '>#'+dojo.attr(node, 'id')).length){
m.canDrop(true);
return;
}
3) Extended onDndDrop to remove the added class to re display the hidden elements.
onDndDrop: function(source, nodes, copy, target)
{
this.inherited(arguments);
dojo.forEach(dojo.query('.hiddenSource'),
function(el){dojo.removeClass(el, 'hiddenSource');}
);
}
4) Extend onDndCancel to do the above
onDndCanel: function()
{
this.inherited(arguments);
dojo.forEach(dojo.query('.hiddenSource'),
function(el){dojo.removeClass(el, 'hiddenSource');}
);
}
This isn't the best solution since it hides the elements that can't be used with the current element that you are positioning, but it worked for me.