I try to use v-for to get the data key - vuejs3

Such as the Title, I try to create a menu that I use v-for to get array items and keys. Then I need the keys to create the second layer menu.
const localSite = ref('us')
const products = ref({})
const softwares = ref({})
const menuShow = ref({})
const menuTxt = ref({
"us": {
"products": "Products",
"softwares": "Software"
}
})
<div id="menu-wrapper">
<div class="flex">
<div v-for="( item, dataKey ) in menuTxt[localSite]" class="menu-item">
<button class="text-white" :data-category="dataKey" #click="slideDown(dataKey)">
{{ item }}
<font-awesome-icon icon="angle-down" />
</button>
<div class="text-box menu-content">
<p v-for="itemA in dataKey">{{itemA}}</p>
</div>
</div>
</div>
</div>
I think the 'dataKey' is a variable which is the vue data Object.
But I can always get the wrong result.
What shoul I do ?

If I understand your problem correctly, you are trying to access the products and softwares properties by using the dataKey in the template. This is not possible the way you are doing it as it's referring to the key of menuTxt.
I made a StackBlitz to show a different kind of approach. Defining the child menu items along with it's parent in the same object. This also makes it more readable.

Related

Vue - requests and watch data on unmounted components

I'm new to vue and strugling with some props and attributes.
I have a vue application where the main app is calling three different components:
Navbar,
Sidebar
MapContainer
In Navbar user will fill a selector with information about city and states. After user presses search, the list of results will then show up in the Sidebar menu.
Sidebar itself is a simple component carrying only a router-view for it's children components which are Results and Details
Results will receive the result of the search performed in the Navbar component. When user clicks any item in the Results component, Sidebar will then load Details taking the place of Results with detailed information about that place.
the problem is that the data used to make the request(city and states) comes from the first component navbar. I'm passing this data to sub-components using vue-router params option. When component Results gets unmounted, I lost all the data that was passed, even adding a Watch couldn'd fix the problem and thus can't return back to the previous page. Even adding a Watch couldn'd fix the problem. What's the proper way to handle data across components that area unmounted?
Navbar.vue
<template>
<div class="navbar">
<div class="logo">
Logo
</div>
<div class="middle">
<div class="flex-selectors">
<div class="navbar-options">
<select v-model="state_id" class="main-selectors" #click="load_cities">
<option v-for="state in states" :value="state.state_id" :key="state">
{{ state.state }}
</option>
</select>
<select v-model="city_id" class="main-selectors">
<option v-for="city in cities" :value="city.city_id" :key="city">
{{ city.city }}
</option>
</select>
</div>
<div class="search">
<button #click="seach">
<router-link :to="{name: 'results', params: {state: state_id, city: city_id} }" aria-current="page" title="Resultados">
<div>Search</div>
</router-link>
</button>
</div>
</div>
</div>
</template>
Sidebar.vue
<template>
<div class="sidebar">
<router-view></router-view>
</div>
</template>
Results.vue
<template>
<div v-if="areas.length">
<router-link :to="{name: 'details' }" tag="div" class="container" #click="load(area)" v-for="area in areas" :key="area" :value="area">
<!-- BUNCH OF DIVS AND V-FOR -->
</router-link>
</div>
<div v-else>
NA
</div>
</template>
<script>
export default {
data() {
return {
state_id: this.$route.params.state,
city_id: this.$route.params.city,
areas: [],
}
},
methods: {
search(state_id, city_id) {
load_areas.get(state_id, city_id).then(
result => {
this.areas = result.data
}
)
}
},
mounted() {
this.emitter = inject('emitter')
this.searchAreas(this.state_id, this.city_id)
},
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
this.searchAreas(toParams.state, toParams.city)
}
)
},
watch: {}
}
</script>
What's the proper way to handle data across components that are
unmounted?
You cannot access data from an unmounted component.
When multiple components need to access or modify the same data, a good option to look into is state management. Pinia is the new official library recommendation for state management.
Instead of passing data through vue-router params, create a store for it. Set results of your search query from Navbar component and access it in Results component. When Results component gets unmounted, you won't lose the data.

How to create a page from the id parameter in the url

I'm trying to create a single page from the id that is passed as a parameter.
my routes structure:
When I pass the mouse over an item in my list I get the id of the firebase document so I need to create a page to show the data in the documents based on their ids.
http://localhost:3000/category/rent/541KqSMHpU17QuYLihFs
id:
541KqSMHpU17QuYLihFs
My listItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer
? listing.discountedPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
: listing.regularPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
The important thing here is it:
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
How do I make my [listingId] slug be the id page?
my [listingId].svelte so far:
<script>
import { page } from '$app/stores'
const listingId = $page.params.listingId
import { db } from '../../../../firebase.config.js'
// get the id parameter from the url
</script>
Happy new Year!!
I had a little trouble understanding your question at first.
As it stands now, your URIs are in the shape /category/id/[listingId], so http://localhost:3000/category/rent/541KqSMHpU17QuYLihFs won't get matched. What you need are URIs in the shape of /category/[id]/[listingId]. So you need to rename your id directory to [id] in order to make it dynamic.
You will then be able to retrieve the id the same way you do listingId:
<script>
import { page } from '$app/stores'
const { id, listingId } = $page.params
import { db } from '../../../../firebase.config.js'
// do stuff
</script>
With the above URL as an example, id will hold the value 'rent' and listingId will hold the value '541KqSMHpU17QuYLihFs'.
Hope this answers your question (and happy new year to you as well!)
Edit: a better, more explicit name for the [id] parameter would be [listingCategory] and would improve the readability of your code/the understanding of your intent.

Updating parent of recursive component in Vue

I've made a menu showing systems and their subsystems (can in theory be indefinitely) using a recursive component. A user can both add and delete systems, and the menu should therefore update accordingly.
The menu is constructed using a "tree"-object. This tree is therefore updated when a new system is added, or one deleted. But, I now have a problem; even though the new child component is added when the tree is rerendered, the classes of it's parent-component doesn't update. It is necessary to update this because it defines the menu-element to having children/subsystems, and therefore showing them.
Therefore, when adding a new subsystem, this is presented to the user:
<div class="">
<a href="#/Admin/364" class="item">
<i class=""></i>Testname
<div class=""></div>
</a>
</div>
Instead of this:
<div class="menu transition visible" style="display: block !important;">
<a href="#/Admin/364" class="item">
<i class=""></i>Testname
<div class=""></div>
</a>
</div>
It works fine adding a subsystem to a system which already have subsystems (since the menu-class is already present), but not when a subsystem is added to one without subsystems. In that case, the menu ends up looking like this:
The "opposite" problem also occurs on deletion, since the parent still has the menu-class:
Here's the code for the recursive component:
<template>
<router-link :to="{ name: 'Admin', params: { systemId: id } }" class="item" >
<i :class="{ dropdown: hasChildren, icon: hasChildren }"></i>{{name}}
<div :class="{ menu: hasChildren }">
<systems-menu-sub-menu v-for="child in children" :children="child.children" :name="child.name" :id="child.id" :key="child.id"/>
</div>
</router-link>
</template>
<script type = "text/javascript" >
export default {
props: ['name', 'children', 'id'],
name: 'SystemsMenuSubMenu',
data () {
return {
hasChildren: (this.children.length > 0)
}
}
}
</script>
I'm guessing this has to do with Vue trying to be efficient, and therefore not rerendering everything. Is there therefore any way to force a rerender, or is there any other workaround?
EDIT: JSFiddle https://jsfiddle.net/f6s5qzba/

How to change NgbCollapse default value of false?

The default value for ngbCollapse is false, as described at https://ng-bootstrap.github.io/#/components/collapse. The example given there uses the following code: https://ng-bootstrap.github.io/app/components/collapse/demos/basic/plnkr.html
<p>
<button type="button" class="btn btn-outline-primary" (click)="isCollapsed = !isCollapsed"
[attr.aria-expanded]="!isCollapsed" aria-controls="collapseExample">
Toggle
</button>
</p>
<div id="collapseExample" [ngbCollapse]="isCollapsed">
<div class="card">
<div class="card-block">
You can collapse this card by clicking Toggle
</div>
</div>
</div>
How does one over-ride the default so that the toolbar is collapsed by default?
Noticed the same thing. Initialize the variable in a constructor and it works fine.
export class AppComponent {
isCollapsed:boolean;
constructor() {
this.isCollapsed = true;
}
Modifying this within the Typescript seems to drive away from the intent of using the module's provided properties. Instead, by utilize [ngbCollapse] you do not need add to Typescript and have more control with the advantage of ngDirectives.
<div id="collapseExample" [ngbCollapse]="!isCollapsed">
Additionally, when used within dynamically generated content (*ngFor...[ngbCollapse]=) you can take advantage of ng-if-else conditional states
*ngIf="getIsEditing(buffer); then tableEdit; else tableView;"

Semantic-ui search - access object properties not used in 'searchFields'

I am using the Semantic UI search for the title property of my data object. data has other fields and I want to access them when an object is selected. For example, I want to put the value from the uuid property in a hidden input.
Is there a Semantic UI way of doing this? - I couldn't figure it out from the documentation (I know I can go and search through all data.title's for the selected one, but ... there probably is another way).
$('.ui.search').search({
source: data,
searchFields: [
'title'
]
,onSelect : function(event){
//...some other code
$("#tags").append('<input type="hidden" value="'+ value_from_my_uuid_field +'"');
}
});
<div class="ui search">
<div class="ui icon input">
<i class="search icon"></i>
<input class="prompt" type="text" placeholder="Search subjects...">
</div>
<div class="results"></div>
</div>
Thank you.
The search widget has an onSelect callback you can register (docs) When your user selects a suggestion from the search response, your callback will be called with the selection:
$searchWidget.search({
onSelect: function(result) {
// do something with result.id or whatever
}
});
I had a similar problem (but with ajax source data) and I finally ended up adding hidden content-tags to the results (on server side) like <div style='display:none;' class='id'>12345</div>.
And in the onSelect-callback I search the result (with jquery) for this content:
onSelect : function(event){
var $result = $(this);
var id = $result.find(".id").html();
...
// Return 'default' triggers the default select behaviour of the search module.
return "default";
}
HTH
Semantic UI actually provides a way of accessing any of the object's properties.
I used both dropdown and search classes, as shown in the docs with hidden input values for the properties.
<template name="search_drop">
<div class="ui floating dropdown labeled search icon button">
<i class="search icon"></i>
<span class="text">Search subjects...</span>
<div class="menu">
{{#each subjects}}
<div class="item" data-id="{{this.id}}" data-value="{{this.value}}" data-child="{{this.haschildren}}">{{this.name}}
</div>
{{/each}}
</div>
</div>
</template>
subjects contains my objects with id, name, value, haschildren properties.
Template.search_drop.rendered = function(){
$(".dropdown").dropdown({
onChange: function(value, text, $choice){
console.log(value); //will output the equivalent of {{this.name}}
console.log($choice[0].attributes); //list of attributes
console.log($choice[0].attributes["data-id"].value); //the equivalent of {{this.id}}
}
});
}

Resources