nos codemos
nos codemos

Reputation: 671

vue style binding based on object value

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.

Upvotes: 0

Views: 797

Answers (2)

Terry
Terry

Reputation: 66103

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>

Upvotes: 1

Thaeke Hekkenberg
Thaeke Hekkenberg

Reputation: 378

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.

Upvotes: 1

Related Questions