Get number of components in placeholder, Sitecore - loops

I have a component that needs to know how many components thats currently added to the very same placeholder, this since it needs to update a value of an html-attribute based on its index within the placeholder.
Is there anyway to get either the number of components thats already added to a placeholder, or to get the current-renderers index?
Usually I would just use a simple for-loop and set the attribute, but since its a placeholder with components thats not an option.
Thanks in advance!

Try this:
var placeholder = "my-placeholder";
var renderingReferences = Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, true);
var renderingsInPlaceholder = renderingReferences.Where(r => r.Placeholder.EndsWith('/' + placeholder, StringComparison.OrdinalIgnoreCase));
var numberOfRenderingsInPlaceholder = renderingsInPlaceholder.Count();
Update: Changed search for placeholder key from IndexOf to EndsWith.

What is the HTML attribute you are trying to update and how are you planning on update this value? From C# code at render time? Or do you want to store this value when a component is added in the Page Editor and store the value against a Sitecore Item?
It depends on what your use-case is, but by suggestion for front-end use would be to execute this logic using JavaScript, otherwise you have to hook into Sitecore pipelines, find your HTML element and the add the attribute appropriately. Saving this value when a component is added means you need to r-run the login for the entire placeholder and update any stored values, since it will (should) be possible for the user to components anywhere...
Something like the following to add a order order data-attribute to a list of elements. If this is being used by another plugin the make sure you run this code before initializing your plugin.
// get element + its siblings
var $els = $('.selector').siblings().addBack();
// loop and add data-attr with index number
$els.each(function(index, element) {
$(this).attr('data-sortorder', index);
}

Related

Error: Method “props” is only meant to be run on a single node. 2 found instead

it('should call setCampaignDate on click', function () {
let spySetCampaign = sinon.spy(wrapper.instance(), 'setCampaignDate');
let datePickers = wrapper.find('.campaign-date-tab').dive().find(Datepicker);
assert.equal(datePickers.length, 2);
console.log(datePickers);
var date = new Date();
for (let index = 0; index < datePickers.length; index++) {
datePickers.simulate('change');
sinon.assert.calledOnce(spySetCampaign.withArgs(date, 'startDate'));
}
});
I am trying to simulate my 'change' function and trying to test whether 'setCampaignDate' is called or not. The problem here is that the length of my shallow component returned by find is 2:
let datePickers = wrapper.find('.campaign-date-tab').dive().find(Datepicker);
When trying to call simulate on 'datepickers', it gives an error as below:
'Error: Method “props” is only meant to be run on a single node. 2 found instead.'.
Not sure how to simulate on components having nodes greater than 1.
The answer for multiple components without changing your code is to use the enzyme API to get the correct index of the button you want.
wrapper.find(Component).at(index).simulate('click');
With component being the name of whatever you're testing and index being the number you want.
I think you should add .first().
Something like this:
const selectedCompoent = Wrapper.find(exactlyComponent).first()
In your case:
let datePickers = wrapper.find('.campaign-date-tab').dive().find(Datepicker).first()
Problem:
It means that when you query for '.campaign-date-tab' and 'Datepicker', finder is returning 2 elements. We can't perform any event (like click) at a time on 2 different elements.
Solution:
What ever the element that you want to access, make it unique.
Example: set some attribute to identify an element uniquely
<CampaignDateTab class="campaign-date-tab" data-test="dateTab1"/>
And query for it like wrapper.find('[data-test="dateTab1"]')
Again if CampaignDateTab component has multiple Datepicker components. Then you have to differentiate them with some class name or attribute name etc.
You can simulate an event at a time only on one element. Querying for some element should not return multiple elements.
You are doing a for loop which then uses datePickers (array) instead of the singular datePicker.
You should be able to do:
datePickers[index].simulate('change');

How to add a class to the element class in EXTJs

I'm using a chart to render a piechart for my app. I have:
Ext.query('.highcharts-container')
that gives me an array of highchart-containers and I'd like to add a custom class to every one of them to add some custom css. I tried:
Ext.query('.highcharts-container').addCls("test")
but says "addCls" is not a function.
below is the img of how it looks:
and accord to the answers below, i tried adding it in seperate app, and it works, but for some reason in this case it constant shows undefined.no clue whts going on?
Ext.query returns an array. Instead, use Ext.select which returns a CompositeElement. It lets you run Ext.dom.Element methods on a group of elements:
Ext.select('.highcharts-container').addCls("test");
Ext.query will return an array of matching components. If you're sure there will always be exactly 1 item in the array, you can use:
Ext.ComponentQuery.query('.highcharts-container')[0].addCls("test")
Otherwise, you should loop through the items and add the class to each component.
Ext.ComponentQuery.query('.highcharts-container').forEach(function (item) {
item.addCls("test");
});

Mutating object properties within an array with Polymer

I not sure how to solve this issue. I am sure someone will know this very quickly.
I have an array of objects and modifying a property. I have a firebase listener 'child_changed'. When firebase is updated need to update the array. Here is the code below.
dbRefList.on('child_changed', function(snap) {
var len = this.grocerylist.length;
for(var i=len; i--;) {
if(this.grocerylist[i].key === snap.key) {
this.set(['grocerylist', i, 'selected'], snap.val().selected);
}
}
this.notifyPath('grocerylist', this.grocerylist.slice());
}.bind(this));
When the array is modified I want the template repeat-dom to trigger. I know this.set will not trigger array mutation sub properties but again I am not sure how to solve this. I done research and tried so many solutions.
I can force a render on the template dom-repeat but I would prefer the data binding way.
So this code (just the this.set you have in there now) should cause the value of grocerylist.i.selected to update inside the dom-repeat (assuming it's bound in there so it's actually showing up).
What behavior are you seeing? Are you trying to filter or sort the list based on the selected value? In that case, you might need to add observe="selected" on the dom-repeat.
(Also—have you confirmed that the child-changed callback is being called with the this value you expect—the element—rather than window or something else?)
You should be able to force a refresh by doing this.grocerylist = this.grocerylist.slice() or this.set('grocerylist', this.grocerylist.slice()); ... notifyPath doesn't work here because notifyPath doesn't change the value, it notifies the element about a prior change (the second argument is effectively ignored).

Dynamically Populate Drop Down List inside Multifield in AEM

I have two tabs in my dialog.First Tab is having a pathfield and second tab is having a multifield inside that only one widget of xtype selection(drop down) exist.I want to send the pathfield path as a query parameter to a servlet and want to populate json in the list.
I have done this by having a listener under drop-down widget.
i am using property render and its value:
function(){
var dialog = this.findParentByType('dialog');
var path=dialog.findById('path');
$.getJSON('/bin/demo?path=' + path.value,
function(jsonData){
this.setOptions(jsonData);
this.doLayout(false,false);
}
);
}
My JSON response is coming but setOptions is not a function error is coming.
Please Help!!!!
this value depends upon the context where you are making use of this.
I believe that is the problem here. this value would differ inside and outside the $.getJSON. You would need to bind the value of this object for the function.
The link has given the example also. Either you need to store reference of this to a variable or bind this reference using the bind method. Refer this for more details

How can I select elements in ExtJS?

I have a ExtJS page that is outputting the following DOM.
I want to select a specific element so that I can respond to a click on it.
However, the following code never selected the elements that are built with the extJS library. JQuery can't seem to select them either.
ExtJS can't even find an element that is defined in the <body> element in the HTML file itself. However, JQuery can indeed select an element defined in the <body> element.
I have been reading this guide which seems to indicate it should be no problem to just use Ext.get() and Ext.select() to get elements, however, as the following code shows, the elements are never selected.
How do I select an element in ExtJS?
var members = Ext.select('x-panel');
for (var i = members.length - 1; i >= 0; i--){
element = members[i];
element.on('click', function() { alert('test');})
};
Try Ext.select('.x-panel') with a period in the selector, which is required for selecting by class name.
bmoeskau is right, you can use Ext.select('.x-panel') which returns a collection of divs with class x-panel.
Note that Ext.get() just returns a single element, so if you want to get multiple matches you should use Ext.select() or Ext.query().
And what's more, Ext.select() returns Ext.CompositeElement, though the document does not list but it supports all the methods of Ext.Element so your example can be simply written as:
Ext.select('.x-panel').on('click', function(){ alert('test') });
Code above will add event handler for click operation to each x-panel.
Use:
Ext.get(el);
if you need to get the flyweight element, use:
Ext.fly(el);
See the API documentation for the Static Ext object for details

Resources