Making small components on runtime - vue.js

I am having a problem working with JQuery DataTable. I had to use that plugin since I had no other choice allowed due to my project requirements.
So the problem is that, I am adding rows to DataTable and in the row there's a column with button HTML tag. Now I want to bind an on click handler to the button.
dt.Rows.Add({
column_1_data,
column_2_data,
"<button #click='itsVueTime'>MyButton</button>"
});
Here dt is the DataTable's instance. Now the problem is #click won't work. I understand that its not being rendered by Vue thats why its not working.
Is there a way to bind click event in this condition?

Without knowing more context, I would recommend this way of doing it
In your component with the method you want to use, you can expose the component like this. (I use mounted, but you can use other lifecycle methods too like created)
mounted() {
window.app = this;
}
then you can use
<button onclick="app.holler()">Say Hello</button>
you can also expose just the function you want to use like so
mounted() {
window.itsVueTime = this.itsVueTime;
}

Related

Vue Chart JS and options reactivity

I'm trying to recreate the doughnut to pie change in behavior as seen here:
https://www.chartjs.org/samples/latest/scriptable/pie.html
I'm using VueJS version of Chart JS and after recreating this it seems to not be reactive at all.
Here is the method that I use to change the chart to the other one:
togglePieDoughnut() {
this.options.cutoutPercentage = 50;
}
As you can see it does not work as intended, even tough I used reactiveprop mixin.
EDIT: To be precise I want to recreate the chart update behaviour as seen in the example on chartjs.org website. I do not want to rerender the chart, rather update it so the transition remains smooth.
Seems like the issue is the template isn't reacting to the data changes. Best way to force template re-render is to bind a key, for our example, we are changing this value, the template will update when its changed:
:key="options.cutoutPercentage"
Codepen example:
https://codesandbox.io/s/vue-chartjs-demo-t8vxu?file=/src/App.vue
What if we add a watch to the options variable and rerender the chart when it happens?
watch: {
options: function() {
this._chart.destroy();
this.renderChart(this.donut, this.options);
}
}
When you look at the code of Piechart.vue, it seems that it only render one time on mounted. Thats why when changing the options, it not gonna reflected in the chart because there is no function to rerender.
The only way is you have to remove the old pie chart and create a new one when options changed. There's a lot of way to do the force re-render, but still the cleanest way is as procoib said, attach a key to it.
When using object as props and update one property in it, the reactivity system will not trigger the change, because the object is the same and only one property updated. Thus, the child component will not get the updated value.
What can you do is to recreated the object with the updated property. See below code:
this.options = Object.assign({}, this.options, { cutoutPercentage: 50 });
And in the child component, use watcher re-render the chart
watch: {
options(newVal) {
}
}

Vuetify and require.js: How do I show a dynamic component?

I am creating a tab component that loads its v-tab-item components dynamically, given an array of configuration objects that consist of tabName, id, and tabContent which is a resource location for the component. I have it successfully loading the components. However, they don't actually initialize (or run their created() methods) until I switch tabs. I just get empty tabs with the correct labels. Using the DOM inspector initially shows just <componentId></componentId>, and then when I switch tabs, those tags are replaced with all of the component's content.
How do I get the dynamic components to initialize as soon as they are loaded?
EDIT: I created a CodePen here:
https://codepen.io/sgarfio/project/editor/DKgQON
But as this is my first CodePen, I haven't yet figured out how to reference other files in the project (i.e. what to set tabContent to so that require.js can load them up). I'm seeing "Access is denied" in the console, which makes it sound like it found the files but isn't allowed to access them, which is weird because all the files belong to the same project. So my CodePen doesn't even work as well as my actual project. But maybe it will help someone understand what I'm trying to do.
Also, after poking around a bit more, I found this:
http://michaelnthiessen.com/force-re-render/
that says I should change the key on the component and that will force the component to re-render. I also found this:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Which has a pretty good example of what I'm trying to do, but it doesn't force the async component to initialize in the first place. That's what I need the async components to do - they don't initialize until I switch tabs. In fact they don't even show up in the network calls. Vue is simply generating a placeholder for them.
I got it working! What I ended up doing was to emit an event from the code that loads the async components to indicate that that component was loaded. The listener for that event keeps a count of how many components have been loaded (it already knows how many there should be), and as soon as it receives the right number of these events, it changes the value of this.active (v-model value for the v-tabs component, which indicates which tab is currently active) to "0". I tried this because as I noted before, the async components were loading/rendering whenever I switched tabs. I also have prev/next buttons to set this.active, and today I noticed that if I used the "next" button instead of clicking on a tab, it would load the async components but not advance the tab. I had already figured out how to emit an event from the loading code, so all I had to do at that point was capture the number of loaded components and then manipulate this.active.
I might try to update my CodePen to reflect this, and if I do I'll come back and comment accordingly. For now, here's a sample of what I ended up with. I'm still adding things to make it more robust (e.g. in case the configuration object contains a non-existent component URL), but this is the basic gist of it.
created: function() {
this.$on("componentLoaded", () => {
this.numTabsInitialized++;
if(this.numTabsInitialized == this.numTabs) {
// All tabs loaded; update active to force them to load
this.active = "0";
}
})
},
methods: {
loadComponent: function(config) {
var id = config.id;
var compPath = config.tabContent;
var self = this;
require([compPath], function(comp) {
Vue.component(id, comp);
self.$emit("componentLoaded");
});
}
}

How to call gridready event of AG-Grid on a click Handler function

I am working on a project in my organization where I have to implement ag-grid .I know ag-grid has a gridready event function which fires initially and sets the params with all grid api properties . My rowdata is constantly changing which is coming from some other component ,I have added a handler (function) where I want to call gridready event again which sets my rowdata with the latest values . The problem is gridapi properties will be initialized only at 1st time ,I want to access those (gridapi) properties in my handler as well which is becoming undefined now ,how to access gridapi properties even after grid initialization and scope is lost. I don't have the exact code ,I have added some code snapshots which will describe my situation exactly.
grid.component.ts
#Input() gridData
ngOnit() {
}
handler() {
want to call grid ready but params will be undefined ,how to set this gridapi properties.
gridready()
}
gridready(params) {
this.gridapi =params.api;
this.columnapi =params.clumnapi
this.gridapi.setRowData(this.gridData.rowData)
}
According to ag-Grid's documentation, the Grid API (both api and columnApi) will only be available after the gridReady event has been fired.
Here's an alternative way of achieving the same thing if I understood your problem correctly.
You can try adding a local variable, isGridAPIReady, in your own grid.component.ts to track that state.
Set isGridAPIReady to true when ag-grid GridReadyEvent fires.
And eventually set the new rowData using the grid api:
//grid.component.ts
#Input()
rowData: any[];
ngOnChanges(changes: SimpleChanges): void {
if (this.isGridAPIReady && changes['rowData']) {
this.gridOptions.api.setRowData(this.rowData);
}
}
Hope it helps!

How to add a click function to an imported component in Vue

So I have a Vue2 app. I have create a component "u-button"
when i import this and use it in another component, I want to be able to add a click function to it. However at the moment it looks for a function on the u-button component rather than the component it is being used in.
so for example, in the below if i click the first button nothing happens, if i click the second button i get the console log.
<template>
<div>
<u_button #click="clicked">Click me</u_button>
<button #click="clicked">Click me</button>
</div>
</template>
<script>
import u_button from '../components/unify/u_button'
export default {
components: {
u_button
},
methods: {
clicked() {
console.log("Working!");
}
}
}
</script>
However if i add a method on the u-button component, then it calls that. So how can i get my below example to work ? The only thing I can think of is to wrap it in another div and add the click function to that. but I'm wondering if there is a better way?? I dont want to use events to do this either as that gets messy very quickly.
As you can imagine having a reusable button that when clicked always performs the same function is a bit pointless.
It's because of the nature of components for example if we had a (virtual) iframe component which had a button in it and we'd like to detect click event on it we might name the event click and listen for it in the parent component; therefore, Vue introduced a feature called event modifiers for example in Vue, We have .native modifier (you can read more about the Vue modifiers here)
Now, to make your code work, You should add a .native after #click like this:
<u_button #click.native="clicked">Click me</u_button>
By the way, it's better to develop a naming convention for yourself It'd become handy when your projects get larger.

How to close collapse on clicking on a button using vuejs and quasar?

I'm using <q-collapsible> tag for a collapse action. That is working fine. But I need to close the collapse by clicking some other button through Vue js only.
Since I'm using quasar it has some functionalities like open() and close() and I don't know how to implement it. So if possible someone helps me how to proceed.
This can be done easily since quasar provides this functionality out of the box.
First, reference your quasar component
<q-collapsible ref="myCollapsible">
...
</q-collapsible>
<button #click="toggleCollapsible">toggle collapsible</button>
Then, execute the needed method
methods: {
toggleCollapsible () {
this.$refs.myCollapsible.toggle()
}
}
This is for toggle, you can do the same with open and close.