I have two ExtJs TreePanel. One tree is fully loaded (call it FLTree) and second one is partially loaded (call it PL tree). When user clicks on a node in fully loaded tree & that node is present in partially loaded tree, I want to fire the checkchange event for that node in partially loaded tree.
Is this possible?
Yes, Ext.tree.Panel has the itemclick event which is fired when an item is clicked (you need to add it in the controller or in the treepanel's listeners property.
The attributes are: someFunctionName: function(treeview, record, item, index, e, eOpts) { ... }
From the record variable you can get the data needed from the first tree's selected node.
To find the other treepanel you can use the up() and down() methods on the treeview:
var parentContainer = treeview.up('container_xtype[someProperty1=someValue1]');
you can walk up in the component hierarchy (get that parent container which contains both treepanels).
var pLtree = parentContainer.down('treepanel[someProperty2=someValue2]');
If the two treepanel doesn't have common parent, then you can use the var pLtree = Ext.ComponentQuery.query('treepanel[someProperty2=someValue2]')[0]; global method which returns an array of matched components. BUT make sure that you use a good component selector query (you can check if the returned array's length == 1).
Finally you need to use the pLtree.fireEvent('checkchange', ...); which is described HERE.
Related
Note: I will only accept pure js answers, not jquery or any other js library.
I have a onchange method attached to a selector
const filter = (element) => {
const value = document.getElementById(element).value;
elements = document.querySelectorAll("table:not("+value+")");
console.log(value, elements, "table:not("+value+")");
}
We get the element's value which in this case could be of two options: is-room-type-false and is-room-type-true. These classes are attached to every single table on the page (there could be hundreds) When I run this and get the console.log
I see:
is-room-type-true // Selected value
8200 tables, node list. // The list of elements that supposedly do not contain this class.
Inspecting the giant array is showing me:
0: table.is-room-type-false.venue-id-1
1: table // Child should not be here
2: table .... (repeats for children tables) // none of these should be here.
x (some number later): table.is-room-type-true.venue-id-1 // Should not be here ...
I wonder if its because of the second class attached to tables(?)
The tables can have nested tables, so I need just the parent tables (if possible) to come back, not their children.
Also as you can see from the sample output, my value is is-room-type-true but as we can see not only do I get children back, but also tables with the class in question when I specifically stated: tables WITH OUT this class.
So the question is:
Whats wrong with my JS function to return me every single table including ones with the class applied.
How do query such that my node list only shows parent tables? (if possible)
The idea is to then take these tables that do not have said class that was selected and then hide them:
[].forEach.call(document.querySelectorAll("table:not("+value+")"), function (el) {
el.style.visibility = 'hidden';
});
But if I add this code to the above function, ALL TABLES hide (which is wrong)
Thoughts?
"table:not("+value+")" needs to be "table:not(."+value+")" so that it treats value as a class name.
I'm trying to implement a sortable list of objects with JQuery-UI in the manner described at http://differential.com/blog/sortable-lists-in-meteor-using-jquery-ui.
However, rather than sort a list of separate documents, I'm sorting a list of objects embedded within a single document. That is, I have a document like so:
{ name: "Name of this Rolodex",
cards: [{name: "...", rank: 0, id: "some-unique-id"},
{name: "...", rank: 1, id: "some-other-unique-id"}, ... ]
}
And I just want to make the cards sortable. My template is as follows -- it's passed a single Rolodex as the context:
<template name="rolodex">
Rolodex Name: {{name}}
<div class="cards-list">
{{#each sortedCards}}
{{> cardTemplate}}
{{/each}}
</div>
</template>
And the helper JS file:
Template.rolodex.helpers({
sortedCards: function() {
return this.cards.sort(function(cardA, cardB) {
return cardA.rank - cardB.rank;
});
}
});
Template.rolodex.rendered = function() {
this.$(".cards-list").sortable({
stop: function(e, ui) {
// Get dragged HTML element and one immediately before / after
var el = ui.item.get(0)
var before = ui.item.prev().get(0)
var after = ui.item.next().get(0)
// Calculate new rank based on ranks of items before / after
var newRank;
if(!before) {
// First position => set rank to be less than card immediately after
newRank = Blaze.getData(after).rank - 1;
} else if(!after) {
// Last position => set rank to be more than card immediately after
newRank = Blaze.getData(before).rank + 1;
} else {
// Average before and after
newRank = (Blaze.getData(after).rank +
Blaze.getData(before).rank) / 2;
}
// Meteor method that updates an attribute for a single card in a
// Rolodex based on IDs for the Rolodex and Card
Meteor.call('cards/update',
Blaze.getData(ui.item.parent().get(0))._id, // Rolodex ID
Blaze.getData(el).id, // Card ID
{rank: newRank});
}
});
};
The problem I'm running into is that after sorting a card into its expected position, the DOM is then updated with the card in a new, wrong position. The server has the correct rankings stored though, and refreshing the page causes the card to be listed in its correct position (at least until another sort is attempted).
My best guess as to what is happening is that Meteor's templating system doesn't seem to understand that the JQuery-UI has moved the DOM elements around and is reactively updating my template in the wrong order.
For example, suppose my cards are: A, B, C. I move C such that we now have C, A, B. JQuery-UI updates the DOM accordingly and fires an event which results in C's rank being changed to be less than A's.
However, Meteor doesn't know that the DOM has already been altered by JQuery-UI. It does, however, see the change in rank to C and reactively updates the order of the list based on prior assumptions about what the list order was. In this case, we end up with B, C, A.
Any suggestions as to what I'm doing wrong here?
Meteor/Blaze uses an _id attribute to identify data objects and link them to DOM elements. This applies not only to arrays of documents returned by a Collection cursor, but to any array of objects. So in the above issue, the problem was that I used an id value to identify each card rather than _id. Switching id to _id fixes the issue and allows Blaze to properly update the DOM, even if the DOM has previously been modified by JQuery-UI's sortable plugin.
The Meteor reactivity force you to choose who is in charge of DOM updates.
While it is ok to let Blaze render the DOM and then manipulate it with a third party library (usually a jQuery plugin invoked in a .rendered() method), you are now in a condition in which Blaze doesn't know what happened to your DOM, so every subsequent reactive update could not be consistent.
This is the reason why, for interactive and reactive interface elements, we need a new class of plugins/components/packages Meteor-aware (or better reactive-aware). See for example the difficulties of porting datatables.net to Meteor versus the Meteoric reactive-tables.
All that said, my hack to overcome this problem is to write some reactive code that takes care of detroying and rebuilding the plugin whenever the DB gets updated. This way, you restore the original DOM which Blaze is aware of, let him update it and then reinvoke your jQuery plugin. This is a far (very far) from optimal solution, but saved my day a couple of times.
For .sortable() maybe the best solution is to disable reactivity with option {reactive: false} in the Collection.find() so that when you update the attribute of a card with Meteor.call no redraw takes place but your interface is already consistent.
On my page I have 2 trees.
There is form on the page. One tree is shown on the page and another inside the form.
I have create trees as below:
var myTree1 = Ext.create('Ext.tree.Panel', {
store: store, //where store is hardcoded json tree data
....
});
similarly myTree2 is declared with different store. It is shown inside a form.
When I select on any node on myTree2 and click on create button then I must be able to add a new leaf node inside myTree1 at the same index.
Help I need are:
1. How to get index of selected node.
2. How to go to the index in myTree1, should be equal to the selected index myTree2.
3. How to add a leaf node at specific index.
For reference:
Ext JS 4 TreePanel documentation
Ext JS 4 NodeInterface documentation
The select event provides both the index and the record. If you need general access to it, myTree1.getSelectionModel().getSelection() will return an array of records, which you can then check against the store to get the index.
I'm assuming you want to compare the selected records on both trees. Given the sample in #1, try this:
var tree1rec = myTree1.getSelectionModel().getSelection[0],
tree2rec = myTree2.getSelectionModel().getSelection[0];
// Perform comparison logic here
// Note that if you're using two different stores, you can't
// just do an equality test. You'll probably have to compare
// individual store values.
Nodes are inserted relative to existing nodes on the tree. So if you have a button click event:
function onButtonClick(button, eOpts) {
var rec = myTree1.getSelectionModel().getSelection[0];
if (rec) {
var childNode = rec.createNode(/* some object here */);
rec.appendChild(childNode);
}
}
The details will vary depending on your actual data, but this is the general pattern.
Currently i have a setup that looks like this:
ko.applyBindings(viewModel);
$(".removeButton").live("click", function() {
viewModel.ProductCategories.destroy(ko.dataFor(this));
});
$(".renameButton").live("click", function() {
ko.dataFor(this).Name("Renamed Successfully!");
});
This is working fine for me until i introduce the concept of child elements. At that point the remove event no longer works for those items.
Is there a more generic way of "destroying" an element?
KO obviously knows the element i am clicking (as i am able to rename).
[{"Id":1,"Name":"Bikes","Parent":null,"Children":[{"Id":5,"Name":"Mountain Bikes","Parent":1,"Children":null},{"Id":6,"Name":"Road Bikes","Parent":1,"Children":null},{"Id":7,"Name":"Touring Bikes","Parent":1,"Children":null}]},{"Id":2,"Name":"Components","Parent":null,"Children":[{"Id":8,"Name":"Handlebars","Parent":2,"Children":null},{"Id":9,"Name":"Bottom Brackets","Parent":2,"Children":null},{"Id":10,"Name":"Brakes","Parent":2,"Children":null},{"Id":11,"Name":"Chains","Parent":2,"Children":null]}]
The events above will all work on any element (child or otherwise) except for remove which only works on root elements.
Can i call remove on an element itself or will I have to add some way of working out where it lives inside the array and destroying it like that?
for example; this is preferable:
$(".removeButton").live("click", function() {
ko.dataFor(this).destroy();
});
to this:
$(".removeButton").live("click", function() {
viewModel.ProductCategories[someindex].Children.destroy(ko.dataFor(this));
});
Thanks,
Kohan
The main issue is determining who the parent array is when trying to destroy an item.
Several options:
Rather than ko.dataFor, you can use ko.contextFor which will return an object that includes properties like $data, $parent, $parents and $root.
If your arrays have the same name, then you could do something like: http://jsfiddle.net/rniemeyer/xJjK8/
If your arrays have different names, then you could add a hint on the button element to understand the name of the parent like: http://jsfiddle.net/rniemeyer/arpNx/
Otherwise, if you really wanted it to be generic, then you could use the with binding to force a scope block, which would allow you to access the parent array through $parent. However, this will be the unwrapped array and we really would want the observableArray. With some extra work, you could loop through the properties of the parent's parent and compare the underlying array with your unwrapped array to locate the actual observableArray that you would want to call destroy with your item. Like this: http://jsfiddle.net/rniemeyer/bBVrE/
Finally, if you take care in the way that your objects are created you can push the destroy functionality to the item itself rather than needing access directly to the parent. Here is a sample that shows adding a destroyMe method to an object that uses the parent that was passed to the constructor function: http://jsfiddle.net/rniemeyer/Eeryh/
I have this controller with a value.
App.xcontroller = SC.ArrayController.create({
...some code...
array_values = [],
..more code...
})
Now i have somewhere in a view this valueBinding
valueBinding: 'App.xController.array_values',
When I change values in the array the view does not get updated. but when i do
the following in the controller:
var array_values = this.get('array_values');
... adding / removing values to the array....
if (x_values.contains(x)){
x_values.removeObject(x)
} else {
x_values.pushObject(x);
};
this.set('array_values', array_values.copy());
the binding works, the view gets updated. But ONLY with the copy().
I don't want to make a copy of the array, IMHO this is not efficient. I just want to
let the valueBinding know content has changed..
the x values are just a bunch of integers.
The reason i want this: I want to change the value key of a SegmentedItemView. I want to change the active buttons. But I do not know on forehand how many segmentedviews I have
so I thought i bind the value of every generated segemented view to some common array and change that common array to be able to change the active buttons on all of the segmented views. Since each button represents an item with an unique key it works fine. except that i have to copy the array each time.
set the content property of the xcontroller
Bind to the arrangedObjects property of the xcontroller
You need to use KVO compliant methods on the array to get the bindings to fire. The ArrayController itself has an addObject and removeObject methods. Arrays in SC have been augmented with a pushObject method (among others), which is also KVO compliant. So if you use the KVO methods the view should update.
The reason your view does not update is because you are bound to the array, but the array itself did not change. When you do a copy, the array itself changes, so the bindings fire.
You might also want to try
this.notifyPropertyChange('x_values');
in the controller after you make the changes, but that is less preferable to using the built in KVO functionality.