【问题标题】:How to show a component in Vue.js when an API condition has been met?满足 API 条件时如何在 Vue.js 中显示组件?
【发布时间】:2021-04-19 13:14:56
【问题描述】:

我已尝试使用 v-if,但出现此错误:Uncaught TypeError: Cannot read property '0' of undefined

我了解问题所在,代码无法运行,因为尚未获取数据。但我不知道如何仅在按下 Enter 键时触发。

如果我删除 v-if="weather.weather[0].main=='Clouds'" 和 v-if="weather.weather[0].main=='Clear'",代码运行正常,但如果我这样做,所有动画都会同时显示。

代码如下:

<template>
  <div id="container" :class="containerTemperature">
    <h1>Better<br>Weather</h1>
    <main id="app" :class="appTemperature">
        <div class="search-box">
          <input 
            type="text" 
            class="search-bar" 
            placeholder="Search..."
            v-model="query"
            @keypress="fetchWeather" 
          />
        </div>
        
        

        <CloudsAnimation v-if="weather.weather[0].main=='Clouds'">
        </CloudsAnimation>

        <SunAnimation v-else-if="weather.weather[0].main=='Clear'">
        </SunAnimation>

        <NoAnimation v-else>
        </NoAnimation>

        <div class="weather-wrap" v-if="typeof weather.main != 'undefined'">
          <div class="location-box">
            <div class="location">{{ weather.name }}, {{ weather.sys.country }}</div>
            <div class="date">{{ dateBuilder() }}</div>
          </div>
          <div class="weather-box">
            <div class="temp">{{ Math.round(weather.main.temp) }}°c</div>
            <div class="weather">{{ weather.weather[0].main }}</div>
          </div>
        </div>
    </main>
  </div>
</template>

<script>
import CloudsAnimation from "./components/CloudsAnimation"
import SunAnimation from "./components/SunAnimation"
import NoAnimation from "./components/NoAnimation"

export default {
  name: 'App',
  components: { 
    CloudsAnimation, 
    SunAnimation,
    NoAnimation
    },
  data () {
    return {
      api_key: '08f1525958fbc6584f628b6dac25a906',
      url_base: 'https://api.openweathermap.org/data/2.5/',
      query: '',
      weather: {}
    }
  },
  computed: {
    containerTemperature: function () {
    return {
      'warm-container': typeof this.weather.main != 'undefined' && this.weather.main.temp > 20,
      'cold-container': typeof this.weather.main != 'undefined' && this.weather.main.temp < 9
    }
  }, appTemperature: function () {
    return {
      'warm': typeof this.weather.main != 'undefined' && this.weather.main.temp > 20,
      'cold': typeof this.weather.main != 'undefined' && this.weather.main.temp < 9
    }
  }
  },
  methods: {
    fetchWeather (e) {
      if (e.key == "Enter") {
        
        fetch(`${this.url_base}weather?q=${this.query}&units=metric&APPID=${this.api_key}`)
          .then(res => {
            return res.json();
          }).then(this.setResults)
          .then(this.query = "");
      } 
    },
    setResults (results) {
      this.weather = results;
    },
    dateBuilder () {
      let d = new Date();
      let months = ["January", "February", "March", "August", "September", "October", "November", "December"];
      let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

      let day = days[d.getDay()];
      let date = d.getDate();
      let month = months[d.getMonth()];
      let year = d.getFullYear();

      return `${day} ${date} ${month} ${year}`;
    }
  }
}
</script>

<style lang="scss">
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@300&display=swap');


* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;

  body {
    font-family: 'Montserrat', sans-serif;

    #container {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      height: 100vh;
      background: rgb(167,207,242);
      background: linear-gradient(to bottom, #BDF2F2 0%, #34A6BF 50%, #074E8C 100%);

      h1 {
        position: absolute;
        top: 4vh;
        color: white;
        text-shadow: 1px 3px 2px rgba(0, 0, 0, 0.25);
        font-style: oblique;
        text-align: center;
        font-family: 'Rajdhani'
      }

      #app {
      background-image: url('./assets/cold.jpg');
      background-size: cover;
      background-position: bottom;
      transition: 0.4s;
      width: 350px;
      height: 500px;
      border-radius: 20px;
      box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.25);
      position: relative;
      overflow: hidden;
      }

      #app.warm {
        background-image: url('./assets/warm.jpg')
      }

      #app.cold {
        background-image: url('./assets/coldd.jpg')
      }

      main {
        height: 100vh;
        padding: 25px;
        background-image: linear-gradient(to bottom, rgba(0,0,0,0.25), rgba(0,0,0,0.75))
      }

      .search-box {
        width: 100%;
        margin-bottom: 30px;
      }

      .search-box .search-bar {
        display: block;
        width: 100%;
        padding: 15px;
        color: #313131;
        font-size: 20px;
        appearance: none;
        border: none;
        outline: none;
        background: none;
        box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.25);
        background-color: rgba(255, 255, 255, 0.5);
        border-radius: 0px 16px 0px 16px;
        transition: 0.4s;
      }

      .search-box .search-bar:focus {
        box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.25);
        background-color: rgba(255, 255, 255, 0.75);
        border-radius: 16px 0px 16px 0px;
      }

      .location-box .location {
        color: white;
        font-size: 32px;
        font-weight: 500;
        text-align: center;
        text-shadow: 1px 2px 2px rgba(0, 0, 0, 0.4);
      }

      .location-box .date {
        color: white;
        font-size: 20px;
        font-weight: 300;
        font-style: italic;
        text-align: center;
        text-shadow: 1px 2px 2px rgba(0, 0, 0, 0.4);
      }

      .weather-box {
        text-align: center;
      }

      .weather-box .temp {
        display: inline-block;
        padding: 10px 25px;
        color: white;
        font-size: 82px;
        font-weight: 900;
        text-shadow: 3px 6px rgba(0, 0, 0, 0.4);
        background-color: rgba(255, 255, 255, 0.4);
        border-radius: 16px;
        margin: 30px 0px;
        box-shadow: 3px 6px rgba(0, 0, 0, 0.4);
      }

      .weather-box .weather {
        color: white;
        font-size: 48px;
        font-weight: 700;
        font-style: italic;
        text-shadow: 3px 6px 1px rgba(0, 0, 0, 0.25)
      }
    }
    #container.warm-container{
        background: rgb(242,160,160);
        background: linear-gradient(to bottom right, rgba(242,160,160,1) 0%, rgba(242,82,170,1) 50%, rgba(34,32,89,1) 100%); 
      }
    #container.cold-container {
        background: #D9D9D9;
        background: linear-gradient(to bottom left, #f0efef 0%, #B4B7BF 50%, #909197 100%);
      }
  }
}

</style>

【问题讨论】:

    标签: javascript vue.js single-page-application


    【解决方案1】:

    在访问对象的属性之前,您可以通过添加额外的检查来确保它已被获取,如下所示:

     <CloudsAnimation v-if="weather && weather.weather[0].main=='Clouds'">
            </CloudsAnimation>
    

    并在数据中首先将其声明为空

    data () {
        return {
          api_key: '08f1525958fbc6584f628b6dac25a906',
          url_base: 'https://api.openweathermap.org/data/2.5/',
          query: '',
          weather: null
        }
    

    如果它没有渲染,它首先检查它是否为空,如果它不为空,它会检查数据。你应该把这个检查放在每个组件或包装div上

    【讨论】:

    • 嘿,谢谢你的回答,我试过了,但这次它说“TypeError: Cannot read property 'main' of null”...
    • @Mikado94 你应该做同样的检查 v-if="weather && typeof weather.main != 'undefined'"。如果这不起作用,请更新您的问题并告诉我您到底尝试了什么
    【解决方案2】:

    既然你理解了这个问题,就不需要解释了。我喜欢让模板免于冗长的if 检查,而是使用计算属性。所以我要做的是在对空值进行长时间检查时将 if 语句更改为计算属性,例如:

    <CloudsAnimation v-if="computedWeather === 'Clouds'"></CloudsAnimation>
    
    <SunAnimation v-else-if="computedWeather === 'Clear'"></SunAnimation>
    

    然后是计算属性:

    computedWeather: function() {
      return (this.weather && this.weather.weather && this.weather.weather[0] && this.weather.weather[0].main) || null;
    }
    

    如您所见,如果签入模板会很长,所以我更喜欢计算。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      • 2014-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多