Astro JS if statement - astrojs

How could i put a condition in a component astro JS.
for example, how could i display a item if only is "DOG" ?
---
const items = ["Dog", "Cat", "Platypus"];
---
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
https://docs.astro.build/en/core-concepts/astro-components/#dynamic-html

You could use the JS's filter method on the array to only use the items that apply to your condition.
---
const items = ["Dog", "Cat", "Platypus"];
---
<ul>
{items.filter(item => item === "Dog").map(item => (
<li>{item}</li>
))}
</ul>
Another way would be to use nested conditional rendering, which is not ideal nor very appealing to the eye.
---
const items = ["Dog", "Cat", "Platypus"];
---
<ul>
{items.map(item => (
{item === "Dog" && <li>{item}</li>}
))}
</ul>

Related

How to self reference Astro component

I have this component Astro component (located at "src/components/Menu.astro");
---
export interface MenuItem {
name: string;
link: string;
items?: MenuItem[];
}
export interface Props {
items: MenuItem[];
depth: number;
}
const { items, depth } = Astro.props;
---
<ul data-depth={depth}>
{
items.map(({ name, link, items: subItems }) => {
if (subItems && subItems.length > 0) {
return (
<li>
<div class="dropdown">
{link ? <a href={link}>{name}</a> : <button>{name}</button>}
<Menu items={subItems} depth={depth + 1} />
</div>
</li>
);
}
return (
<li>
<a href={link}>{name}</a>
</li>
);
})
}
</ul>
On line 28 (where the line reads <Menu items={subItems} depth={depth + 1} />) an error thrown saying;
ReferenceError: Menu is not defined
How can I self reference an Astro component in this case? Thanks in advance.
PS: "Menu" is the component file's name.
Astro has a built in method for this called Astro.self that you can use
Astro.self
Example from docs:
---
const { items } = Astro.props;
---
<ul class="nested-list">
{items.map((item) => (
<li>
<!-- If there is a nested data-structure we render `<Astro.self>` -->
<!-- and can pass props through with the recursive call -->
{Array.isArray(item) ? (
<Astro.self items={item} />
) : (
item
)}
</li>
))}
</ul>
In the front-matter, i.e. between the two ---, you write import Menu from "./Menu.astro".
You'll get new errors, as you can't use statements in the body.
Change
items.map(({ name, link, items: subItems }) => {
if (subItems && subItems.length > 0) {
return (
<li>
<div class="dropdown">
{link ? <a href={link}>{name}</a> : <button>{name}</button>}
<Menu items={subItems} depth={depth + 1} />
</div>
</li>
);
}
return (
<li>
<a href={link}>{name}</a>
</li>
);
})
to
items.map(({ name, link, items: subItems }) =>
(subItems && subItems.length > 0)
? <li>
<div class="dropdown">
{link ? <a href={link}>{name}</a> : <button>{name}</button>}
<Menu items={subItems} depth={depth + 1} />
</div>
</li>
: <li>
<a href={link}>{name}</a>
</li>
)

Get dynamic menu data not working in production Laravel Vue

I have a problem after deploy my Laravel, VueJS, InertiaJS website to hosting. The problem is navigation menu is not showing. My menu view code:
<li :class="{ 'current' : $page.props.route === 'Home'}">
<Link href="/">Home</Link>
</li>
<li v-for="(item_parent, index) in $page.props.menu" :key="index" :class="item_parent.child === null ? { 'current': $page.url.startsWith(`/page/${ item_parent.link }`) }:`dropdown `">
<Link :href="item_parent.static_page === 1 ? `/page/${ item_parent.link }`: `${ item_parent.link }`" v-if="item_parent.static_page === 0">{{ item_parent.menu_name }}</Link>
<Link :href="item_parent.child === null ? `/page/${ item_parent.link }`:`#`" v-if="item_parent.static_page === 1">{{ item_parent.menu_name }}</Link>
<ul>
<li v-for="(item_child, index) in item_parent.child" :key="index">
<Link :href="item_child.static_page === 1 ? `/page/${ item_child.link }` : `/${ item_child.link }`">
{{ item_child.menu_name }}</Link>
</li>
</ul>
</li>
<li :class="{ 'current': $page.url.startsWith('/download') }">
<Link href="/download">Download</Link>
</li>
My Controller get menu data from database:
$parent = Menu::where('active', 1)->where('parent_id', 0)->orderBy('order', 'ASC')->get();
foreach ($parent as $key_parent => $item_parent){
$this->menu[$key_parent] = array ("parent_id" => $item_parent->parent_id, "menu_name" => $item_parent->menu_name, "link" => $item_parent->link, "static_page" => $item_parent->static_page);
$this->menu[$key_parent]['child'] = null;
$child = Menu::where('active', 1)->where('parent_id', $item_parent->id)->orderBy('order', 'ASC')->get();
if(!$child->isEmpty()){
foreach ($child as $key_child => $item_child){
$this->menu[$key_parent]['child'][$key_child] = array ("parent_id" => $item_child->parent_id, "menu_name" => $item_child->menu_name, "link" => $item_child->link, "static_page" => $item_child->static_page);
}
}
}
return Inertia::render('Frontend/Home', [
'menu' => $this->menu,
]);
And my menu table database structure is like this
I confused its work in local development mode, and didnt work in hosting production mode. I also try to change app_debug: true to show error message, but its nothing.
Local:
After Deploy:
dump menu data:
SOLVED
I dont know why, the if condition should be === '1' not only === 1

Why react 'key' props doesn't work for my below case?

I used both (item.id and index) to show unique "key" but it's never working shows those warning (Each child in a list should have a unique "key" prop). Also all my server get disconnected after a while. So, How do I solve both issue? here is my code..
let details = [
{ id: 0, Name: "Pervej", Age: 20 },
{ id: 1, Name: "Hossain", Age: 21 },
];
const [array, SetArray] = useState(details);
const ClearClick = () => {
SetArray([]);
};
<div className="box">
{array.map((item, index) => {
return (
<>
<h2 key={item.id}>
Name: {item.Name} , Age: {item.Age},
</h2>
</>
);
})}
<button type="button" onClick={ClearClick}>
Clear
</button>
</div>
Because the key should be on the top most element. You can't set props for the empty shorthand <>. But you can set it to <React.Fragment>.
Watch out when using index as keys, though (your ids look indexes). Read this: https://reactjs.org/docs/lists-and-keys.html
So, your code should be:
<div className="box">
{array.map((item, index) => {
return (
<React.Fragment key={item.id}>
<h2>
Name: {item.Name} , Age: {item.Age},
</h2>
</React.Fragment>
);
})}

Filter items by category in Laravel October

I have a document model with a $belongsTo categories relationship, everything is working well and I'm able to assign categories to documents and list them all on the frontend, but what I'm struggling with is filtering the results by category.
I have 3 categories and 3 col-md-4 columns, in each column, the documents should be listed and filtered via their category, how do I do this with twig using components?
My documents.default component file looks like this:
{% set documents = __SELF__.documents %}
<ul class="record-list list-unstyled ">
{% for document in documents %}
<li class="ul-text-black">
{{document.name }}
</li>
{% endfor %}
</ul>
My documents component code:
<?php namespace Myplugin\Documents\Components;
use Cms\Classes\ComponentBase;
use Myplugin\Documents\Models\Document;
class Documents extends ComponentBase
{
public function componentDetails(){
return [
'name' => 'Documents List',
'description' => 'Custom Component to list documents by category'
];
}
public function defineProperties(){
return [
'results' => [
'title' => 'Number of Documents',
'description' => 'How many documents do you want to display?',
'default' => 24,
'validationPattern' => '^[0-9]+$',
'validationMessage' => 'Only numbers allowed'
],
'sortOrder' => [
'title' => 'Sort Documents',
'description' => 'Sort documents',
'type' => 'dropdown',
'default' => 'name asc',
]];
}
public function getSortOrderOptions(){
return [
'name asc' => 'Name (ascending)',
'name desc' => 'Name (descending)',
];
}
public function onRun()
{
$this->documents = $this->loadDocuments();
}
protected function loadDocuments(){
$query = Document::all();
if ($this->property('sortOrder') == 'name asc') {
$query = $query->sortBy('name');
}
if ($this->property('sortOrder') == 'name desc') {
$query = $query->sortByDesc('name');
}
if ($this->property('results') > 0) {
$query = $query->take($this->property('results'));
}
return $query;
}
public $documents;
}
My page looks like this
<div class="row">
<div class="col-md-4">
<p class="text-black">
<strong>FINANCIAL</strong>
</p>
{% component 'documents' %} // Only documents from financial category
</div>
<div class="col-md-4">
<p class="text-black">
<strong>ANALYTICS</strong>
</p>
{% component 'documents' %} // Only documents from analytics category
</div>
<div class="col-md-4">
<p class="text-black">
<strong>INVESTMENT</strong>
</p>
{% component 'documents' %} // Only documents from investment category
</div>
How do I display the documents list but filter them by category? Something like this?
{% partial "documents-financials" category="Financials" %}
You should be able to access the properties in your Documents component using $category = $this->property('category');
There's documentation on accessing component properties on the OctoberCMS website: https://octobercms.com/docs/plugin/components#component-properties
I can then see that you're loading all documents and filtering them using the Laravel Collection. I would suggest changing this and doing it on the database first. It'll be far more efficient.
$category = $this->property('category');
$results = $this->property('results');
$documents = Document::whereHas('category', function ($query) use ($category) {
return $query->where('name', $category);
})
->orderBy('name', 'ASC')
->take($results)
->get();
If you want to group your documents and then output them, you could do the following:
$this->documents = Document::with('category')
->orderBy('name', 'ASC')
->take($results)
->get()
->groupBy('category.name');
See https://laravel.com/docs/5.3/collections#method-groupby
Then in your component template:
<div class="row">
{% for group in documents %}
<div class="col-md-4">
<p class="text-black">
<strong>FINANCIAL</strong>
</p>
<ul class="record-list list-unstyled">
{% for document in group %}
<li class="ul-text-black">{{document.name }}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>

How to select a group of nodes based on their relative distance to the document root

I have been thinking about this problem for quite a while but I can't seem to find a plausible solution: How is it possible to select a group of nodes based on their relative distance to the document root?
Given a sample HTML fragment like so that has been parsed with Nokogiri:
<body>
<nav>
<ol>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ol>
</nav>
<ul>
<li>Feedback</li>
<li>Follow us</li>
</ul>
</body>
Assuming the only thing I know about this document, is that the nodes I am looking for are:
a group of <a> tags that have the same relative distance to the <body> tag and the highest amount of matches. In the above example that would mean matching the group inside the <ol>, because it has the highest amount of nodes that have the same distance to the <body>.
Any thoughts on how to tackle this?
It's an odd need/request, but I'd start with this:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<body>
<nav>
<ol>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ol>
</nav>
<ul>
<li>Feedback</li>
<li>Follow us</li>
</ul>
</body>
EOT
a_tags = doc.search('a')
.group_by{ |n| n.path.split('/').count }
.max_by{ |c, n| c }
.last
a_tags
# => [#(Element:0x3fe3a6012a60 {
# name = "a",
# attributes = [ #(Attr:0x3fe3a6098750 { name = "href", value = "#" })],
# children = [ #(Text "Home")]
# }),
# #(Element:0x3fe3a6012830 {
# name = "a",
# attributes = [ #(Attr:0x3fe3a6096914 { name = "href", value = "#" })],
# children = [ #(Text "About")]
# }),
# #(Element:0x3fe3a601240c {
# name = "a",
# attributes = [ #(Attr:0x3fe3a6070ef8 { name = "href", value = "#" })],
# children = [ #(Text "Contact")]
# })]
The end result in a_tags is an array of nodes that are at an equal distance from the root.
From that I tried:
a_tags = doc.search('a').group_by{ |n| n.path.gsub(/\[\d+\]/, '') }.max_by{ |p, n| n.size }
# => ["/html/body/nav/ol/li/a",
# [#(Element:0x3ff6f10d2c08 {
# name = "a",
# attributes = [ #(Attr:0x3ff6f10d1cf4 { name = "href", value = "#" })],
# children = [ #(Text "Home")]
# }),
# #(Element:0x3ff6f10d2b18 {
# name = "a",
# attributes = [ #(Attr:0x3ff6f10cc254 { name = "href", value = "#" })],
# children = [ #(Text "About")]
# }),
# #(Element:0x3ff6f10d2a8c {
# name = "a",
# attributes = [ #(Attr:0x3ff6f10c3b2c { name = "href", value = "#" })],
# children = [ #(Text "Contact")]
# })]]
That returns an array consisting of the CSS path (with indexed values removed) and the list of nodes under it. I'm not entirely happy with the use of the CSS path to identify the clustered target tags.
It's possible to walk backwards from a node to the root of the document and add those nodes to an array. Then, using Array's "set" capabilities you should be able to identify exactly which nodes lie under the same parent nodes. I'm not sure how to work around the fact that the <li> nodes differ.
Hopefully those should give you some ideas to work from.

Resources