I'm cloning a flash app (http://bqgh6e.axshare.com/module.html) using vue and I need to create transitions between items created with v-for.
I've got transitions working between different components on my App.vue https://github.com/alansutherland/BodyGuard/blob/master/src/App.vue
However, within Module1.vue I'm generating slides using v-for https://github.com/alansutherland/BodyGuard/blob/master/src/components/Module1.vue
Is it possible to dynamically create router links for each item generated within the module and transition to them from one another?
Here is a hosted demo of the project so far:
https://bodyguard-9c7b0.firebaseapp.com/module-1
My current solution is to wrap the slides within a large parent and navigate them as a carousel. Not sure if this is a good solution for optimization and it doesn't feel like the vue way to do things.
I'm also running in to trouble trying to $emit back to App.vue, I'm able to pass slidePosition as a prop to the child using:
<router-view :slidePosition="slidePosition" class="view"></router-view>
In my module I try to $emit back using this:
<span v-on:click="increment" v-on:increment="incrementPosition">Start</span>
methods: {
increment: function () {
this.slidePosition += 10
this.$emit('increment')
}
}
This is based off this SO answer vuejs update parent data from child component. Transitioning between slides using the router would be a far neater solution.
Or do I not even need to use the router? do I just use transition-groups?
https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions
I found a really simple solution based on this demo http://matthiashager.com/blog/image-slider-vuejs-tutorial
Rather than use v-for I just change the slide content by incrementing my slidePosition which I then use to call each item within my slide object. As a simple example, say my slides object was this:
slides: [
{title: 'Slide One'},
{title: 'Slide Two'},
{title: 'Slide Three'},
{title: 'Slide Four'}
]
and within data I've set slidePosition: 0
I can then increment the position using a button
<button v-on:click="incrementPosition">Next</button>
then within my methods:
incrementPosition: function () {
this.slidePosition = this.slidePosition + 1
}
And finally in my template:
<h3>{{slides[slidePosition].title}}</h3>
Related
Using Angular + Angular Material 12
If you want to close the MatSidenav, then almost every solution I have found says to:
(click)="sidenav.close()" in the html component.
But I need that (click) for my logout function (click)="onLogoutSideNav()"
onLogoutSideNav() {
this.authService.logout();
}
I need to close MatSidenav from inside a method in the component instead of from the html. The only solution I could find says to:
sidenav!: MatSidenav
...
onLogoutSideNav() {
this.authService.logout();
this.sidenav.close();
}
But doing so returns undefined for this.sidenav.
There are a ton of solutions to use #ViewChild, but I haven't split my navs into header and sidebar components. I'm keeping it simple, doing so from within the app.component.
<mat-list-item *ngIf="isAuth" routerLink="/"><button mat-icon-button><mat-icon>logout</mat-icon><span class="sidenav-span" (click)="onLogoutSideNav()">Logout</span></button></mat-list-item>
What am I missing here?
you can call multiple functions on click event.
eg =>
(click)="onLogoutSideNav();test()"
hope this answers your question.
I am stuck trying pass data from Child A ($emit) component to Parent and from Parent to Child B (props).
Using nuxt.js I have:
layouts/default.vue
This default template will load a lot of components.
Those components will be used or not based on variable from child, the variable will set the v-if directive.
The children are the pages like:
pages/blog/index.vue
pages/about/index.vue
...
The goal is the Child set on Parent what components would be used, the flag can change anytime, the user can choose what will be rendered on admin area.
I have tried use local computed methods on child component, and vuex, no luck with both.
The idea on layouts/default.vue.
<template>
<div>
<TopBar v-if=showTopBar></TopBar>
<Nav v-if=showNav></Nav>
etc...
<nuxt />
</div>
</template>
<script>
import TopBar from "../components/TopBar";
import Nav from "../components/Nav";
etc...
export default {
data() {
return {
showTopBar: false,
showNav: false
etc...
};
},
}
</script>
On child already have use the $emit but no luck.
Child on this situation are pages, and the layout of those pages will be defined by variable from a fetch on the API, user can change the layout anytime.
The goal is have someting like double way between Child Components, example:
Calling route /blog will call pages/blog/index.vue
This would send to layout/default.vue using $emit what components would be rendered (choosed from user in admin area and fetched from API) and the component ID. (example: {topBar: true, topBarID: 2})
On layouts/default.vue after get the $emit from pages/blog/index.vue I would have for example TopBar false, and then not render it, or have received true with an ID, this Id will be send to TopBar as prop for render the customized TopBar made by user on Admin area.
Would be possible someone show an example how to get the pass those data for this specific cenario please?
(Does not matter if using local variables from the Child component or vuex, just looking for an example how to get the contents of variable from Child instead an plain object or undefinied object).
PS.: If there an better approach to deal with dynamic layouts, I am accepting suggestions too.
PS2.: I know I would use specific template per page, like layout/blog and layout/contact, etc... but since the idea is make an CMS, this would not fit on this scenario, I mean, from the admin area user should be able to create pages enabling or disabling components through an page Wizard (the idea is getting something like Wix, every component customization from user will be stored in the database using an Id, and on layouts user choose the previous components mounting the page, in the end all call will be made using the ids of those), and not need to add specific layouts programing, because this the Idea of set all possible components and layouts in layout/default.vue sounds at this moment an better approach, but if is not, I would love see other ways to get same goal.
The correct way to do it would be:
<child-component-1 :showNav.sync="showNav">
And within the child component you would update that by doing:
this.$emit('update:showNav', value)
The parent would define this property:
data() {
return {
showNav: default_value
}
}
You would have to pass that variable to every child component. Every child component would have to define it as a property.
Perhaps a better way to do it would be to instead create a simple store within nuxt and use that to house the settings.
Currently building a web page in Vue, and have hit a bit of an issue parsing and then rendering the <slot>'s child components.
I need to be able to take the slot, parse the components into an array, and then render those components for the end-user.
What I've Tried
I've tried many variations of things, most starting with this: this.$slots.default
This is the last version I tried
let slotComponents = [];
this.$slots.default.forEach(vNode => {
slotComponents.push(vNode);
});
But I've also tried selecting the elements within the vNode and using things like $childeren to select the components. No luck so far.
Potential Issues
The cause could be any number of things, but here is what I thought was going on (in order)
I'm not getting the components into the array properly
I'm not rendering them properly or missed something about how they render
Vue isn't supposed to do this?
Edit - Context
Seems like it would be easier if I gave you the full context of my specific problem.
Goal
To create a dynamic tab component. Should look like this.
// Example of component use
<tab-container>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<trash>
<!-- This one won't show up -->
</trash>
</tab-container>
In order to parse through this content, I needed to get the slot data out.
// Inside the <tabs-container> component
computed: {
tabs: function() {
let tabs = []
this.$slots.default.forEach(vNode => {
tabs.push(vNode);
});
return tabs;
}
}
// Inside the <tabs-container> template
<div>
{{tabs[currentTab]}}
</div>
You shouldn't be using template and computed properties if you want to programmatically render out <tab> inside <tab-container>. {{}} in templates are designed to perform basic operations of JS. The most appropriate way will be to use render function.
Render functions - Vue docs
Here is a working example that takes in few tabs components and shows only active tab component: https://jsfiddle.net/ajitid/eywraw8t/403667/
I'm trying to dynamically render some HTML in a Vue.js component. I'm successfully rendering the HTML. However, I don't know how to wire-up the events for the dynamically rendered elements. I've created a small example. This example probably looks like I've over complicated things. However, it's just a small part of the real example. The example can be seen in this JSFiddle, and the code looks like this:
new Vue({
el: '#app',
data: {
items: [
{
name:'Item 1',
isHtml:true,
mold: function() {
return '<button #click="onButtonOneClick">click</button>';
}
},
{
name: 'Item 2',
isHtml: false
},
{
name:'Item 3',
isHtml: true,
mold: function() {
return '<button #click="onButtonThreeClick">click</button>';
}
}
]
},
methods: {
getHtml: function(i) {
return i.mold();
},
onButtonOneClick: function() {
alert('First Item Clicked');
},
onButtonThreeClick: function() {
alert('Third Item Clicked')
}
}
})
If you run this fiddle, you'll notice that my two buttons look fine on the screen. However, the related click events don't get fired when you actually click the buttons. From what I can see, it looks like the HTML doesn't get fully compiled. I may be wrong. But, it's what it looks like based on what i see in the Chrome Dev Tools.
How do I wire up events for dynamically generated HTML in a Vue.js component?
You're trying to render 'functional html' which is basically the essence of what Vue does for you. Just render your list as you do, and add in your data in an attribute like 'is_button', and output the html for the button in the v-for and add its events. e.g.
<div v-for="(item, index) in items">
<button v-if="item.is_button" #click="onButtonClick(index)">
<div>item.content</div>
</div>
Good luck
Your example goes against everything that Vue is trying to accomplish. I suggest reading up on Vue in their docs or following some tutorials. I am not saying it can't be done because you could certainly bind events later but it is not wise.
Certainly something like this is not going to work:
mold: function() {
return '<button #click="onButtonThreeClick">click</button>';
}
That is because Vue has already rendered the markup when you inject this.
It is difficult to tell what you are trying to achieve exactly but perhaps something like this can help you out:
https://jsfiddle.net/ozf8kq1z/2/
(Open your console)
I'm sorry to be a nuisance, but why have you got vue markup in your data :? This is never going to work. Can you explain what led you down this path? Why can't your markup stay in a template :?
Vue does have a v-html directive for popping little bits of markup out of javascript into templates, but Vue tags in this markup are not processed, and it's one of those features you should use with a bad conscience.
Event listeners outside of Vue, attached after Vue has rendered, do function, but then you've really got to look at yourself in the mirror and ask "oh what have I done"?
I have created two components in Angular 2:
ReaderComponent: The one that initiates and controls all functionality to Owl Carousel (initiate, add slide, remove slide and so on)
PageComponent: Each slide is a PageComponent and has events to handle input from the user (click, pinch, doubletap)
The ReaderComponent is created at start of the application and initiates a request to a service to get all data for each of the PageComponents.
Everything works fine until we add a slide that is a PageComponent. I have tried to add the PageComponent selector to owl Carousel:
this.slider.trigger("add.owl.carousel", ["<my-page-component></my-page-component>"]);
This does add an element of <my-page-component> but does not render the template or handles any of the PageComponents events.
I have tried to add all the PageComponents to an array and render it in ReaderComponents template:
<div *ng-for="#page of pages">
<my-page></my-page>
</div>
This renders correct but by that time all pages is rendered Owl is already initiated and no pages is visible.
So to summarize all of this: I need to know how to add a custom component via javascript (in this case the add functionality of Owl)? Is this even possible? Or is there another way to handle this so that I can add PageComponent in any way?
The first method you mentioned would require you to force angular to re-check it's bindings. This is probably possible, but I don't know off the top of my head.
The second method is much easier. You can use the lifecycle events of the Page or Reader Components to trigger the adding. They are as follows:
export var LIFECYCLE_HOOKS_VALUES = [
LifecycleHooks.OnInit,
LifecycleHooks.OnDestroy,
LifecycleHooks.DoCheck,
LifecycleHooks.OnChanges,
LifecycleHooks.AfterContentInit,
LifecycleHooks.AfterContentChecked,
LifecycleHooks.AfterViewInit,
LifecycleHooks.AfterViewChecked
];
If you add a listener to your PageComponent class, you can probably use OnInit or AfterViewChecked and then get it to add it's own element reference to the carousel (basic example). From a quick look at their documentation, it doesn't look like owl supports adding a new element, so you could have the PageComponents all on your page somewhere hidden and then just add the raw html from the elementref, then remove it again in the OnDestroy function.
If you do it in ReaderComponent you should look at OnChanges or add some clever checks into the DoCheck function and then just get it to reload all items inside it (perhaps owl.reinit?). I've not used the owl carousel before so can't be more specific there I'm afraid.
These are exported from the angular class as interfaces, so you should be extending your classes from them. An example is available on the Angular 2 website here: https://angular.io/docs/ts/latest/api/lifecycle_hooks/AfterViewChecked-interface.html