vuejs conditional wrapper
Asked Answered
B

2

8

In my nativescript-vue application I have a singleFile component called profileForm. I want to use that component in two ways, if the user is logged in I want to have that component as the slot of my layout component to edit profile. Otherwise I want is as a registration form.

I don't want to create an extra component for handling this. So in one case I would like to have my component wrapped in a tag so I can navigate to it, with Vue.$navigateTo, in another case I'd like to wrap it in a component. But the component has a tag in itself so in that case I don't want the anymore.

In django templates I would do something like this:

<template>
  <Page v-if="is_signup" actionBarHidden="true">
  <AppLayout v-else title="Profile">
  ...
  </AppLayout v-else>
  </Page v-if="is_signup">
</template>

But of course this would not work in vuejs. Is there any way to achieve this in vuejs?

I could create a new component and wrap this there like so:

<template>
  <Page v-if="is_signup" actionBarHidden="true">
    <ProfileForm/>
  </Page>
  <AppLayout v-else title="Profile">
    <ProfileForm/>
  </AppLayout>
</template>

But I'd rather not create a new component for this task.

I was hoping to get something like this:

<template>
  <template :is="is_signup?'Page':'AppLayout'" :v-bind="is_signup?{actionBarHidden:true}:{title:'Profile'}">
    ...
  </template>
</template>

Does nativescript-vue have such syntax?

Brag answered 14/9, 2019 at 14:2 Comment(9)
With the way vue works. you can't nest the applayout within the conditional page... your second bit of code is the only way the layout would work that way. Since the page fails the v-if the renderer never enters the block: which would include the applayout.Alar
after your edit I remembered seeing this :is binding somewhere. I think it is discussed here... not much mention of it anywhere in the docs nativescript.org/blog/…Alar
My way would be to work with a boolean variable, say it isLogin and depending on route make it true or false.Moro
@VaritJPatel I don't really understand what you are saying.Brag
Maybe you can provide the code snippet where you think the error lies? What you want to do is possible in vue and described in a lot of resources - I cannot tell what exactly your problem is to help you any furtherTrafalgar
@Trafalgar This would be a simplified version of my problem: how Can I wrap the template of a component inside a tag based on a prop. so if the prop is true the template will be wrapped in a <Page> tag. otherwise it will be naked. This is not exactly my problem but if you tell me how to solve this I can solve that myself.Brag
@ShawnPacarar thank you so much for that link! It opened new doors to me:PBrag
@yukashimahuksay sounds like you might have it. looking at the <component :is /> dynamic component and the v-if="" - v-else directive is probably the right direction.Trafalgar
@Trafalgar can you post an answer that would make the template be <Page actionBarHidden="true"><Label text="hello"/></Page> if is_signup is true and <Label text="hello"/> otherwise?Brag
T
2

One way to do it:

I would go with a v-if / v-else logic if you have only two distinct but predefined options. Simply more semantic.

<template>
  <Page
    v-if="is_signup"
    :actionBarHidden="true"
  >
    <Label :text="'hello'" />
  </Page>
  <Label
    v-else
    :text="'hello'"
  />
</template>

<script>
import Label from '../components/Label.vue';
import Page from '../components/Page.vue';

export default {
  components: {
    Page,
    Label,
  },

  data() {
    return {
      is_signup: false, // you will have to find a way to set is_signup correctly
    };
  },
}
</script>

There are several other ways to achieve the same result; however without knowing more about the requirements I will just leave you with this first option :)

Trafalgar answered 14/9, 2019 at 16:44 Comment(6)
I'm sorry for my lack of explanation in my comment. As you can see in my question, I'm aware of this solution, however, I don't actually have a Label tag, I can make a new component but I don't want to, in other words I don't want to write the text <Label :text="'hello'" /> twice.Brag
@yukashimahuksay As written, this is only the most simple solution for the problem you described. If you give us more concrete requirements we can help you further. You can also use the <slot /> feature to nest components into other components. This also works with dynamic components, e.g. <component :is="dynamicComponent"><Label /></component>; then you could make the dynamic component your <Page> or alternatively a <span> .Trafalgar
The problem is I guessed the solution to my problem and my guess was correct:)) So in fact I have included the answer to my question in my question itself:)) I don't know what to do now. I think this is a useful question so I don't want to delete it maybe I should edit it and post the solution as an answer instead.Brag
The other thing was that after thinking of the solution I thought, is it also possible to have <Page> or nothing instead of <Page> or <span>? so I asked you that in the comments.Brag
You can not entirely remove a component in Vue without also removing its children/nested components.Trafalgar
Don't do that, use the solution provided by @Greengrocery - duplicating code like that is a big no-no and paves the road to maintainability hell.Esp
G
14

This can be solved by using a generic component element:

<template>
    <component :is="is_signup ? 'Page' : 'AppLayout'" actionBarHidden="true">
    ...
   </component>
</template>

See https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
Note that passing a prop not declared in the component (for instance, actionBarHidden being passed to AppLayout, which does not use it) has no effect and is completely safe.

This is an old question but I found myself in the same situation, so I came back to add this answer. Edit: @MarcRo already provided the same answer, somewhat hidden in a comment above.

Greengrocery answered 7/9, 2020 at 10:5 Comment(1)
I also done it this way, helps to avoid a lot of code duplication. Whats also cool you can also use normal html tags, so you can use 'div' als fallbackKochi
T
2

One way to do it:

I would go with a v-if / v-else logic if you have only two distinct but predefined options. Simply more semantic.

<template>
  <Page
    v-if="is_signup"
    :actionBarHidden="true"
  >
    <Label :text="'hello'" />
  </Page>
  <Label
    v-else
    :text="'hello'"
  />
</template>

<script>
import Label from '../components/Label.vue';
import Page from '../components/Page.vue';

export default {
  components: {
    Page,
    Label,
  },

  data() {
    return {
      is_signup: false, // you will have to find a way to set is_signup correctly
    };
  },
}
</script>

There are several other ways to achieve the same result; however without knowing more about the requirements I will just leave you with this first option :)

Trafalgar answered 14/9, 2019 at 16:44 Comment(6)
I'm sorry for my lack of explanation in my comment. As you can see in my question, I'm aware of this solution, however, I don't actually have a Label tag, I can make a new component but I don't want to, in other words I don't want to write the text <Label :text="'hello'" /> twice.Brag
@yukashimahuksay As written, this is only the most simple solution for the problem you described. If you give us more concrete requirements we can help you further. You can also use the <slot /> feature to nest components into other components. This also works with dynamic components, e.g. <component :is="dynamicComponent"><Label /></component>; then you could make the dynamic component your <Page> or alternatively a <span> .Trafalgar
The problem is I guessed the solution to my problem and my guess was correct:)) So in fact I have included the answer to my question in my question itself:)) I don't know what to do now. I think this is a useful question so I don't want to delete it maybe I should edit it and post the solution as an answer instead.Brag
The other thing was that after thinking of the solution I thought, is it also possible to have <Page> or nothing instead of <Page> or <span>? so I asked you that in the comments.Brag
You can not entirely remove a component in Vue without also removing its children/nested components.Trafalgar
Don't do that, use the solution provided by @Greengrocery - duplicating code like that is a big no-no and paves the road to maintainability hell.Esp

© 2022 - 2024 — McMap. All rights reserved.