0

I am trying to dynamically style an element based on a pokemon's type (eg. fire gets a fire icon, fire/flying gets 1 fire 1 flying).

I tried to do it with a ternary operator and :style but it got really long and messy, so I'd prefer not to. Currently, I have it set as a method, and pass in an array, which look like this:

types: ["water", "flying"] //or sometimes just one value eg: types: ['fire']

here is my method:

methods: {
    typeStyle: function (types) {
      const backgroundImageUrls = []
      for (const i in types) {
        backgroundImageUrls.push('url(../assets/' + types[i] + '.svg)')
      }
      console.log(backgroundImageUrls)
      let backgroundPosition = 'center'

      if (backgroundImageUrls.length > 1) {
        backgroundPosition = 'left right'
      }

      return {
        backgroundImage: backgroundImageUrls.join(','),
        backgroundPosition
      }
    }
  }

and this is the html template it is called in:

<li
class="card"
v-for="(mon, index) in team"
:key="index"
>
  <div class="cardfront-images"
  :style="typeStyle(mon.types)"
  >
...
</li>

but it's not working. I would also like to apply a 2nd effect, background-position and do background-position: "center" if there is 1 type, and background-position: "left right" if there are two. but I get an error because of the hyphen in the CSS property.

EDIT

So I have it working to where it makes a url() for the background image (yay!), but unfortunately styling is not applied. So something isn't working, obviously. I have also updated my codeblocks to reflect changes.

EDIT2: So this solution did work, and I have given the check. For some reason, it didn't like my local assets being string-literaled in, so I just called the images from my git repo. Which I guess is good enough since I'm really just making this for my own education.

2 Answers 2

1

What you can do is to store all the background URLs into an array, and then join the array before the return statement.

With regards to the background-position property, remember that in JS all CSS properties are kebab cased, because - will cause JS to interpret it as an arithmetic operation, so using backgroundImage should be the way to do it.

Assuming that the url key in each object in the types array of object contains the actual path to the image, you can do this. Note that you should really avoid using arrow functions when defining a Vue method, because the this in the function will no longer refer to the component instance.

new Vue({
  el: '#app',
  data: function() {
    return {
      team: [{
        ability: "Keen Eye",
        name: "Wingull",
        id: 278,
        types: [{
            type: {
              name: "water",
              url: "http://via.placeholder.com/100x100?text=water"
            },
          },
          {
            type: {
              name: "flying",
              url: "http://via.placeholder.com/100x100?text=flying"
            }
          }
        ]
      }]
    };
  },
  methods: {
    typeStyle: function(types) {
      // Collect all URLs into an array with the template literal applied so we can use it directly in CSS
      const backgroundImageUrls = types.map(entry => `url(${entry.type.url})`);

      // Default value of background-position
      let backgroundPosition = 'center';

      if (backgroundImageUrls.length > 1) {
        // Example: you want one image on the top left, and another on the top right
        backgroundPosition = 'top left, top right';
      }

      return {
        backgroundImage: backgroundImageUrls.join(','),
        backgroundPosition
      };
    }
  }
});
.cardfront-images {
  width: 500px;
  height: 100px;
  background-repeat: no-repeat;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <ul>
    <li class="card" v-for="(mon, index) in team" :key="index">
      {{ mon.ability }}, {{ mon.name }}, {{ mon.id }}
      <div class="cardfront-images" :style="typeStyle(mon.types)"></div>
    </li>
  </ul>
</div>

Sign up to request clarification or add additional context in comments.

10 Comments

Hmm. I will try this. Thanks for pointing out the arrow function thing. This was an amalgamation of several other posts and resources.
I was met with Uncaught (in promise) TypeError: _ctx.typeStyle is not a function. I tried wrapping it in square brackets in the template region, but same error (im not even sure that's an appropriate solution)
@noscodemos What do you mean by square brackets? Did you copy and paste the function definition correctly in the methods object?
square brackets@:style="[typeStyle(mon.types)]" (since removed) and yes.
That's not how you're supposed to use it. Updated my answer with the basic data you've provided, it should work.
|
1

There are a few different ways you can do this:

For conditional styling you can use dynamic classes like:

<div :class="{ mon.type }"

This will then automatically take the type name ('fire', 'water', etc.) on which you can use CSS if you'd like.

For rendering the icons properly, you will need to make some adjustments to the JSON object you give it.

But for that I would need to know whether you have all the information available or otherwise in what format you will fetch it.

I will update my answer when you let me know.

3 Comments

I pull them from two sources (the pasted team source and an API call to get the pokedex number (not relevant here) and the types) and combine it in a store.state.team array, which contains all 6 objects (1 object for each pokemon on the team).
Okay, that's more to work with. Can you edit your post to also show the structure of the JSON object of one pokemon?
I included an abbreviated example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.