Using a component inside of a Vuetify data table - vue.js

I have a Nuxt 2.9.0 app where I'm using #nuxtjs/vuetify 1.11.3.
I'm refactoring some HTML tables that we have in the app and replacing them with Vuetify's v-data-table components. Now, in our old tables we had these bar chart components that we would render inside the table cells, like this:
<th>
{{ totalAvg }}
<RatingBar :color="ratingColor(totalAvg)" />
</th>
That RatingBar component is basically just an empty div with a background color, as you can see below
What's the proper way in which I can keep this functionality when moving onto the Vuetify data tables?
I know that Vuetify implements some slots, for example the #header slot, but I'm not sure exactly how the slots work when it comes to table cells, since the #header slot, for example, seems to just append whatever <template> you write down for the slot, instead of actually replacing the header

Vuetify's v-data-table supports custom rendering for a specific column thru #item.<name>. Link to API doc.

Related

Vue, sharing child components from one parent component to the other

this is a question about best practices, in short: what is the best way to implement this function
I used the vue cli to create a project to train on. And so the normal template it provided me with was a side header thing with the content on the side, and so I made some modifications:
the issue is visualized down if the text explaination wasn't clear
and so what I had in mind was to add a slot in the header "the left side" to add the adding button, and the button wouldn't need to be visible in the other tabs, like the help tab.
App.vue
<template lang="pug">
TheHeader
routerView( v-slot="{ Component }" )
transition( name='slide-fade' mode='out-in' )
component( :is="Component" )
</template>
but here comes the issue, as you can see the tabs are in router views and the router view is beside the header component. the solution I had in mind was to:
add a list of strings in the App.vue with ["help", "course", ...] in the script section
the strings are linked to what router is being used (not very sure how to do this but I guess I could do a v-model to the v-slot being used)
pass the string to the header component
include a v-if statement with every tab's little widget
but I felt like this alone will jank the code a lot and thought if maybe there was an easier way to pass an entire component from one child to another it would be great. if there isn't I'd just like to know if it's the best practice I could do and proceed with this solution
issue visualization:
wanted behavior mock-up:
the solution was to use the Built In <Teleport> Vue component. this way I just type <Teleport to="..."> and it will go where I want

Vue Material components in existing application

I'd like to use the md-card material from Vue Material in an existing application. The example on the website (https://vuematerial.io/components/card/) uses the following:
<template>
<div>
<md-card>
...
</md-card>
<md-card md-with-hover>
...
</md-card>
</div>
</template>
When I use this in the HTML page and create a Vue object for the containing div, the components do show, but the layout is not working. I have tinkered with the layout classes, but the behavior is not matching the example. How can I configure the layout in a plain HTML page to match the example's layout behavior? Is the example page adding another layer to the example div to create the layout behavior?
Updated: I tried a similar scenario in JsFiddle, and it works perfectly: https://jsfiddle.net/w9m6q05f/3/ . In my application, the cards are always in a column regardless of the width of the view, and the bottom card is stretched towards the bottom of the view. Do I need to set the class of the containing div? It may be getting overwritten by my application somewhere else.
Update 2: The culprit is which has height: 100%

VueJs - Deleting a File-input component from a list

I'm still pretty new to vue.js.
I recently created some vue components to tidy up my html. The components are different inputs like text and checkbox. They update the parents data using $emit('input', val). And make sure that the components are up-to-date using props and watch() on those props.
Now there is a problem with using watch() on <input type="file />, since you cannot set the input value like you can on other input types.
Using this example:
https://jsfiddle.net/minde281/nyu73dz6/25/
I have a list of, in this case, items on a shoppinglist. You can add an image for each item.
The image is loaded and added to the list. This works fine.
To get my problem:
add an image to item1
delete that item by clicking the X-button
The result is now that item2 will have that image on the <input type="file" />. The preview works as expected since this can be set through script and therefore use watch(). But somehow vue removes the wrong part of the html markup causing the last one to be removed.
Is there a different way to solve this problem? Or is there a way to tell vue to remove the correct part of the html markup?
-Minde
Vue tries to reuse the component and thats why the selected file is in the second input after deleting the component.
To prevent that you should use the key binding like this:
<li v-for="(item, index) in items" :key="item.name">
Now the hole component gets removed and everything should work as expected.

Vue component not updating after parent state change

I have Vue component which receives json data from props, after this render child components using v-for and pass this data as prop. Everything works fine until i try to remove one item from this list, when i remove it, it removes element incorrectly. In vue devtools i see that parent receives data correctly but not renders it properly. can anyone help me?
here is my code: https://gist.github.com/giokaxo/3d291b9b7b8ef97f81dc83799c430302
Use "key" attribute when rendering elements using v-for, for example:
<p v-for="(product, index) in order.products" :key="i">..</p>
The relevant documentation is here:
You can directly use v-for on a custom component, like any normal
element:
<my-component v-for="item in items" :key="item.id"></my-component>
In 2.2.0+, when using v-for with a component, a key is now required.
However, this won’t automatically
pass any data to the component, because components have isolated
scopes of their own. In order to pass the iterated data into the
component, we should also use props:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id">
</my-component>
The reason for not automatically injecting item into the component is because that makes the component
tightly coupled to how v-for works. Being explicit about where its
data comes from makes the component reusable in other situations.
And here:
When Vue is updating a list of elements rendered with v-for, it by
default uses an “in-place patch” strategy. If the order of the data
items has changed, instead of moving the DOM elements to match the
order of the items, Vue will simply patch each element in-place and
make sure it reflects what should be rendered at that particular
index.
...
To give Vue a hint so that it can track each node’s identity, and thus
reuse and reorder existing elements, you need to provide a unique key
attribute for each item. An ideal value for key would be the unique id
of each item.

Aurelia: How can I modify sidebar content from inside a router view?

I'm trying to wrap my head around how "inner components" can adjust the content of "outer components". Let's say I have an application template that looks something like this:
<template>
<div class="sidebar">
<div>Some app-wide content</div>
<div>
<!-- I want to put some view-specific content here -->
</div>
</div>
<div class="main-body">
<router-view></router-view>
</div>
</template>
Each subview wants to render different content to the sidebar. Obviously this would be easy if the subview included the sidebar area itself, but let's say it is important to preserve the structure and we don't want to replicate the boilerplate of the sidebar across every view.
Is there any way for a child view to declare "export this extra component for display in another place?" I imagine something like injecting the parent view and calling a method on it, but I can't figure it out from the documentation.
Simple demo:
It's fairly simple, actually. Just import and inject your sidebar or any other viewmodel and call a method or update a property.
https://gist.run/?id=745b6792a07d92cbe7e9937020063260
Solution with Compose:
If you wanted to get more elaborate, you could set a compose view.bind variable to that your sidebar would pull in a different view/viewmodel based on the set value.
https://gist.run/?id=ac765dde74a30e009f4aba0f1acadcc5
Alternate approach:
If you don't want to import, you could also use the eventAggregator to publish an event from the router view and subscribe to listen to that event from your sidebar and act accordingly. This would work well for a large app setting where you didn't want to tie them too closely together but wanted the sidebar to react correctly to unpredictable routing patterns by always responding when triggers were published.
https://gist.run/?id=28447bcb4b0c67cff472aae397fd66c0
#LStarkey's <compose> solution is what I was looking for, but to help others I think it's worth mentioning two other viable solutions that were suggested to me in other forums:
View ports. You can specify multiple named router views in a template and then populate them by passing in a viewPorts object to the router instead of specifying a single moduleId. The best source of documentation is a brief blurb in the "Cheat Sheet" of the Aurelia docs.
Custom elements. It's a little more "inside-out" but you could define most of the outer content as a custom element that has slots for the sidebar and the main body; each child view would define this custom element and pass in the appropriate pieces.