Umbraco AncestorOrSelf(int) - what does it do? - umbraco

When using:
#Model.AncestorOrSelf(3)
In a .cshtml template in Umbraco, this would presumably limit the node traversal to 3 levels. Is this correct, and if so can anyone also confirm if the current node has the index zero?

#Model.AncestorOrSelf(3)
Model.Content is the current page that we're on. AncestorsOrSelf is all
of the ancestors this page has in the tree. (level) means: go up to
level 1/2/3/... and stop looking for more ancestors when you get
there.
Above is the comment that you get with Umbraco 7.x rc version.
Take an example of the content tree below that is kind of similar to that you normally see in contents section in umbraco admin area:
Each content document has a level and by default it starts with 1.
In a .cshtml template in Umbraco, this would presumably limit the node
traversal to 3 levels
As you can see in the example below, the level gets on increasing - level + 1. so, it starts by 1 and then just go on adding 1 to your sub levels.
- Content
-- Home (level = 1)
-- About Us (level = 2)
-- Contact Us (level = 2)
-- News Area (level = 2)
-- News Item 1 (level = 3)
-- News Item 2 (level = 3)
-- Other Node (level = 1)
So when you mention 3 as parameter for AncestorOrSelf, you are asking to move to 3rd level in the tree from the current element that can be any document/partial view and stop looking for any more ancestors when its found.
AncestorOrSelf(level) returns a single item which if of type DynamicPublishContent i.e. you will have access to many properties like id, name, url, etc.
#CurrentPage.AncestorOrSelf(1)
// based on content structure above, the above statement will give you an item - Home.
It is basically for fetching ancestors by level, doesn't matter what your current level or currentpage object is.
For example, if you want to create a navigation in your main layout so as to share it on all pages of your site, you will do something like this in your template:
<ul>
#foreach(var page in #CurrentPage.AncestorOrSelf(1).Children)
{
<li>#page.Name</li>
}
</ul>
Based on our example, it will give you:
About Us, Contact Us, News Area (in list form and with proper links)

Adding to the answer from SiddharthP I think the OP is possibly looking for the #CurrentPage.Up(int) method - this is traverses up the tree from the current level by the specified amount of levels.
So if you want the grandfather of the current node - #CurrentPage.Up(2) or #Model.Content.Up(2) for the strongly typed version.
Think of it as Ancestor starts from the root of the content tree down and Up starts from where you are going towards the root.
I think the confusing bit is you use the CurrentPage object but start traversing from the top root node towards the CurrentPage. When we think of our Ancestors in humanity we don't start from the beginning of time!

If my understanding of the code is correct, .AncestorOrSelf(int) returns the ancestor (or self) of a node at the given level in the argument.
Taken from lines 948 & 956 of https://github.com/umbraco/Umbraco-CMS/blob/6.2.0/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs
public DynamicNode AncestorOrSelf(int level)
{
return AncestorOrSelf(node => node.Level == level);
}
public DynamicNode AncestorOrSelf(Func<DynamicNode, bool> func)
{
if (func(this)) return this;
var content = this;
while (content.Level > 1) // while we have a parent, consider the parent
{
// see note in .Parent - strange things can happen
var parent = content.Parent;
if (parent == content) return null;
content = parent;
if (func(content))
return content;
}
return null;
}
Hope I've understood that correctly and that this helps.

Related

How to Add a field in cascade of 2 many to one on Easyadmin 4 Symfony 6

I read and try a lots of things just to add a field witch is in relation.
One Dance have a level (beginner, improver...) and one Level have a Style (Country music, disco...). So for a dance I can get the level and associate style. Dance is MTO with Level, and Level is MTO with Style. It work fine in traditionnel controller and in Dance Index twig I can do
{{ dance.level.style }}
It's work fine.
Impossible for me to do that in EasyAdmin: In Danse Crud Controller
yield AssociationField::new('level');
is naturally working fine but how adding the style name? I'm not familiar with Queribuilder if it's the solution. I read Symfony Documentation easyadmin about unmapped fields but I don't undestand "createIndexQueryBuilder" parameters. If you can help me to progress. Thank's in advance
I don't find examples in stack with Easyadmin 4. And (I'm sorry), documentation is not very clear for me.
Example:
class UserCrudController extends AbstractCrudController
{
// ...
public function configureFields(string $pageName): iterable
{
return [
TextField::new('fullName'),
// ...
];
}
public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
{
$queryBuilder = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
// if user defined sort is not set
if (0 === count($searchDto->getSort())) {
$queryBuilder
->addSelect('CONCAT(entity.first_name, \' \', entity.last_name) AS HIDDEN full_name')
->addOrderBy('full_name', 'DESC');
}
return $queryBuilder;
}
}
Why we have "entity.first_name" (why entity word and not entityDto...). dump parameters don't give me persuasive results
Easy finally.
You can choice the field you want to be rendered. Basically add __toString in Entity.
In my case just add for a many to many relation:
AssociationField::new('dances')
->setFormTypeOption('choice_label','level.style'),

Best way to show a list of complex objects and emit event int vaadin 14

For sample I have one order that my user can add one store and in each store a lot of itens:
Amazon
- cup - $ 1
- notebook - $ 3
- $ 4 Ebay
- bike - $ 10
- boat - $ 13
- $ 23
Total order: $ 27 [Button to send the order]
And in my project I have 3 components:
CartView - (show the total from order and a button)
- StoreView (show de Store name, and total)
---StoreitemView (show the item and the button to remove)
lets to the doubts:
1 - The best way to show thats lists are usingo a GRID with one ROW? some like this:
Grid<StoreView> myGrid = new Grid();
myGrid.addComponentColumn(this::createStoreView).setAutoWidth(true);
or Its best to use a for just like this:
Div myDiv = new Div();
for(Store store in StoreList){
muDiv.add(new StoreView(store);
}
2 - How can I pass event to parent? so when my user click a button at StoreItemView I need to emit that event to my CartView(passing to StorView, and go to CarView to update the values and recreate the cart)
tks
Layout
The layout depends on how many stores and items you expect. If the number is low, just looping through and adding components is a decent approach.
If you have many items, using a Grid with one column is a better approach, as all items aren't rendered at once. An even better approach might be the IronList, or since Vaadin 21 the VirtualList.
If your views are complex with a lot of components, it's a good idea to use a TemplateRenderer instead of a ComponentRenderer. If writing views in HTML instead of Java is not your cup of tea, you can keep on using the ComponentRenderer, especially if performance seems to be good.
If there are a lot of items per store, you can consider having a virtual list for the store items inside the store also. Especially if there are only a few stores.
Events
There is no correct answer here. It seems like your StoreView and StoreItemView are tightly coupled, so the communication can be tightly coupled too. For example, the StoreItemView can expose the button directly with a getRemoveButton() method.
I would use the ComponentEvent for the removal event, something like the code below. Your logic for how the StoreItemViews are created would be different, and you could add a method in the StoreView for adding the event listener. You might want to add some data to the event, too.
public class StoreView extends HorizontalLayout {
public StoreView() {
// Create your store items as you see fit
StoreItemView storeItemView = new StoreItemView();
storeItemView.getRemoveButton().addClickListener(e -> {
remove(storeItemView);
fireEvent(new StoreItemRemovedEvent(storeItemView));
});
add(storeItemView);
}
public static class StoreItemRemovedEvent extends
ComponentEvent<StoreItemView> {
public StoreItemRemovedEvent(StoreItemView source) {
super(source, false);
}
}
}
public class CartView extends VerticalLayout {
public CartView() {
StoreView storeView = new StoreView();
ComponentUtil.addListener(storeView, StoreView.StoreItemRemovedEvent.class, e ->
Notification.show("Store item removed"));
add(storeView);
}
}

How to Create a view that shows all existing attributes DOORS in all formal modules under a single project?

I have a project under which there are 50+ formal modules in IBM DOORS,
I want to create a single view for all modules ( As default view )
This view should display all the attributes that are available for that particular module when I open it.
And the number of attributes in some modules vary.
If anyone in stack-overflow knows a way on this, It would be really helpful!
Before anything, you should probably be aware that there is a maximum number of attributes that can be loaded into a view. You can check out this thread for more information regarding max columns:
https://www.ibm.com/developerworks/community/forums/html/topic?id=1861480b-7aa0-43b2-bf77-be677f5f778e
Now as for how to do this. If you're looking for an automated solution using DXL here's some sample code that you can modify for your purposes. The current code will add object-level attributes that aren't system attributes to the current view of the module you run this code from.
AttrDef ad
Module m = current Module
string sAttrName
int count = 0
Column col
for col in m do {count++}
for ad in m do
{
if ((ad.object) && (!ad.system))
{
sAttrName = ad.name
col = insert (column count)
attribute(col, sAttrName)
width(col, 200)
count++
}
}
Note: This code will only generate a view with all attributes in the module it is run from, it will not loop through all modules in a project or save the view.
To loop through a project and get all modules you'll need to create a recursive function using for itemRef in folder do {...}. Something like the following:
Folder f = current Folder
void recurseFolder(Folder f)
{
Item iRef
for iRef in f do
{
if (type(iRef) == "Formal")
(call your create views function here with parameter iRef)
else if (type(iRef) == "Folder" || type(iRef) == "Project")
recurseFolder(folder(iRef))
}
}
recurseFolder(f)
And then if you need additional code to save the view, you'll have to add appropriate code for that too using save(View v). You can look up additional information pertaining to setting view preferences and saving them in the DXL Reference Manual.

Request.Url.Host returns the wrong host?

I have a single Umbraco 7 instance that has 2 root nodes. One node points to stage.sctflash.com and the other node points to stage.bullydog.com. When I navigate to http://stage.bullydog.com/Products/accessories/podmount, the Request.Url.Host is stage.bullydog.com. Now, if I open up another tab and go to http://stage.sctflash.com/Products/accessories/podmount, the Request.Url.Host might be stage.sctflash.com, but sometimes, it just changes to stage.sctflash.com.
The one strange thing I noticed is that if I view the accessories node in Umbraco, I noticed the parent of it is stage.bullydog.com no matter if I am on stage.sctflash.com or stage.bullydog.com
I am using Request.Url.Host to determine as a query parameter to a database. I am basically getting the brand like this:
if(Request.Url.Host == "stage.sctflash.com")
return "sct";
else
return bullydog";
So essentially if Request.Url.Host is intermittently wrong, I get the wrong query parameter.
To see this in action, if you go to http://stage.sctflash.com/Products/accessories/podmount, you will see an SCT logo, then go to http://stage.bullydog.com/Products/accessories/podmount and you will see a Bully Dog logo. These logos are driven by Request.Url.Host. If you refresh http://stage.sctflash.com/Products/accessories/podmount, you most likely will see the Bully Dog logo now because Request.Url.Host is now stage.bullydog.com instead of stage.sctflash.com
This is the action method that is called when I go to this page: http://stage.sctflash.com/Products/accessories/podmount
public ActionResult GetAccessoriesByType(RenderModel model, string id)
{
string brand = Common.GetProductBrand(Request.Url.Host);
var productSearchResultsModel = new ProductSearchResultsModel
{
Accessories = _accessoryRepository.GetAccessoriesByType(id, brand)
};
return View("~/Views/accessories.cshtml", productSearchResultsModel);
}

How can I get children of a node?

How can I get all the children for the News item (in the standard project) in a partialview (razor)?
I've tried:
#{
var homePage = CurrentPage.AncestorsOrSelf(1).First();
var newsItems = homePage.Children.Where(x => x.GetProperty("Name").Value == "News");
}
But I get an error that states I can't do lambda expression, whitout casting it. "News" is a node in my webpage holding children and I want to create a macro listing the children. How and what can I cast it to?
Currently, you are looking for children of the home page, named "News". I think you want to go one level deeper.
I reccomend this approach:
// 1- Get root node
var site = Model.Content.AncestorOrSelf("Site");
// 2- Get news node
var news = site.Descendant("News");
var newsItems = news.Children;
Here you use the document type alias to traverse your tree, this is much more reliable than using names, as those can change. This of course may require to rework some elements.
Hope this helps!
Try this to get all of the news nodes that are children of homepage:
var newsItems = homePage.Children.Where("Name = #0", "News");
Then iterate through the children of News:
foreach (var newsChild in newsItems)
You could skip the first step if you already know the ID of your news node like so:
var newsNode = Umbraco.Content(1234);
This page has plenty of examples:
http://our.umbraco.org/documentation/reference/querying/DynamicNode/Collections#

Resources