2019-06-02Vue系列二之品牌管理案例

2019-06-02  本文已影响0人  啊_6424

布局页面

<div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">添加品牌</h3>
      </div>
      <div class="panel-body">
        <label>ID: <input type="text" class="form-control"> </label>
        <label>Name: <input type="text" class="form-control"> </label>
        <input type="button" value="添加" class="btn btn-primary">
      </div>
 </div>
 <table class="table table-bordered table-hover table-striped">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Ctime</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item,index) in list" :key="item.id">
          <td>{{ item.id }}</td>
          <td v-text="item.name"></td>
          <td>{{ item.ctime }}</td>
          <td><a href="">删除</a></td>
        </tr>
      </tbody>
      <tbody>
        
      </tbody>
    </table>
image.png

我们的input跟lable里的描述并没有在一行,这是,可以在外层class加上form-inline

加上以后就在一行了

实现添加功能

  1. 获取到id和name,可以直接从data里边取
  2. 组织出一个对象
  3. 把这个对象,调用 数组的相关方法,添加到data里的list。添加到尾部用push,添加到头部用unshift
    在vue中,已经实现了数据的双向绑定,当data里的数据发生改变时,vue监听到数据改动会自动把数据渲染到页面上。开发者主要从事VM中Model数据的操作,同时,在操作Model数据时指定的业务逻辑操作。
 <div class="panel-body form-inline">
        <label>ID: <input type="text" class="form-control" v-model="id"> </label>
        <label>Name: <input type="text" class="form-control" v-model="name"> </label>
        <input type="button" value="添加" class="btn btn-primary" @click="add">
 </div>
<script>
  var vm = new Vue({
    el:"#app",
    data:{
      list:[
        {id:1,name:"大宝",ctime:new Date()},
        {id:2,name:"品牌二",ctime:new Date()},
        {id:3,name:"品牌三",ctime:new Date()},
      ],
      id:"",
      name:"",
    },
    methods:{
      add: function(){
        this.list.push({id:this.id,name:this.name,ctime:new Date()});
      }
    }
  });
</script>
添加成功
@click="add"跟@click="add()"是一样的,区别在于后者可以传参了

代码问题:

添加完后,应该要清除input里上次填写的数据
等号从右到左运算,先把空字符串赋值给this.name ,再将this.name(空字符串)赋值给this.id

      add: function(){
        this.list.push({id:this.id,name:this.name,ctime:new Date()});
        this.id = this.name = "";
      }

根据id删除产品

  1. 给a标签绑定一个click事件并传入id,并且要组织默认事件,否则会出现刷新页面。
  2. 如何根据对象的id找出对象在数组中的索引值
  3. 找到索引值后,调用数组的splice()方法删除。
    这里不能定义为delete(id)方法,因为不执行
<td><a href="" @click.prevent="del(item.id)">删除</a></td>
......
del(id){
        //查看是否传入id
        console.log(id);
        //找到索引值,forEach,some都可以遍历数组,但这里的需求是找到后就删除并终止遍历,所以some更合适,
        // forEach适用于数组每一个元素都需要做相应的操作的时候
        this.list.some((item,index) =>{
          if(item.id === id){
            this.list.splice(index,1);
            //在数组的some方法中,return true就会终止后续循环
            return true;
          }
        });
//或者这样写
        var index = this.list.findIndex(item =>{
          //回调函数
          if(item.id === id){
            return true;
          }
        });
        this.list.splice(index,1);
      },

熟悉数组的相关方法:splice(index,len),some(),findIndex(),forEach()

搜索功能的实现

根据搜索框输入的关键字,table要动态显示匹配的内容,故不能再写死一个List了。

  1. 之前的v-for都是从data的list里边直接渲染出来的,而现在我们可以给v-for定义一个search函数,并将输入的关键字作为参数传入。
  2. 在search函数内部,通过执行for循环,把所有符合搜索关键字的数据保存到一个新数组中并返回。
<label>请输入搜索的关键字: <input type="text" class="form-control" v-model="keywords"> </label>
......
<tr v-for="(item,index) in search(keywords)" :key="item.id">
......
data:{
  keywords:"",
},
methods:{
     search(keywords){
        var newList = [];
        this.list.forEach(item => {
          //匹配keywords对应的index,如果没有匹配到的话就返回 -1
          //但keywords为空字符串时,返回0
          if(item.name.indexOf(keywords) != -1){
            newList.push(item);
          }
        });
        return newList
// 或者是
        // 数组的新方法:some(), forEach(), filter(), findIndex() 都会对数组的每一项进行遍历并执行相关操作
        return this.list.filter(item => {
          // if(item.name.IndexOf(keywords) != -1)
          // es6中,新增一个 String.prototype.includes('要包含的字符串')
          // 如果包含,则返回true ,否则返回false
          //jQuery里的contains()方法
          if(item.name.includes(keywords)){
            return item;
          }
        });
     }
}

熟悉数组的相关方法:filter()
String方法:IndexOf() ,includes()

格式化时间的显示

可以用Vue提供的过滤器实现,先来了解一下过滤器

过滤器

做数据渲染前的最后一次处理,它只是中间做了一层处理,原数据并没有被修改。类似于ps,照片中的人变了,但照相的人是没有变的。
Vue.js允许开发者自定义过滤器,可被用作一些常见文本的格式化,过滤器可被用在两个地方:mustache插值 和 v-bind表达式。过滤器应该被添加在JavaScript表达式的尾部,由“管道”符指示。

过滤器的定义语法
Vue.filter("过滤器的名字",function(data){
   //对数据的处理操作
 });
过滤器调用时的格式
{{ name | 过滤器的名字 }}
小例子,将所有"单纯"换为"邪恶"
 <div id="app">
    <p>{{ msg | msgFormat }}</p>
    <p>{{ msg }}</p>
  </div>
......

<script>
  Vue.filter("msgFormat", function(data){
    console.log(data);
    // 这样写的话只会替换第一个单纯
    // return data.replace("单纯", "邪恶");
    // replace的第一个参数可以是字符串,还可以是正则表达式
    return data.replace(/单纯/g, "邪恶");
  })
  new Vue({
    el: "#app",
    data: {
      msg: "曾经,她是这个世界上最单纯的人,她总是单纯的问我,谁是这个世界上最单纯的人?她最单纯吗?"
    },
    methods: {

    }
  });
</script>
image.png

第一个p因为过滤器所以显示的内容变了,但msg并没有被改变,所以第二个p显示的跟第一个p里的内容不一样。输出的内容就是msg,即filter里边的data,是由第一个p里边传过去的。
现在,需求变了,不改为邪恶,改为疯狂,如果不重新写过滤器的话,能否定义一个参数接收要改的目标内容

    <p>{{ msg | msgFormat("疯狂") }}</p>
.......
 Vue.filter("msgFormat", function(data,str){
    return data.replace(/单纯/g, str);
  })
替换成功
也可以传递多个参数

能多次调用过滤器吗?不妨一试

    <p>{{ msg | msgFormat("疯狂") | test }}</p>
......
  Vue.filter("msgFormat", function(data,str){
    return data.replace(/单纯/g, str);
  });
  Vue.filter("test", function(data){
    return data + "========";
  });
答案是可以的
它会依次处理,最后一个处理完后将内容显示在当前位置

Stirng的方法:replace()

言归正传,用过滤器实现格式化时间

          <td>{{ item.ctime | dateFormat }}</td>
.......
   Vue.filter("dateFormat",function(data){
    //根据给定的时间字符串,得到特定的时间
    var dt = new Date(data);
    //自己拼接出一个自己想要的格式 yyyy-mm-dd
    var year = dt.getFullYear();
    //getmonth()的返回值是 0(一月) 到 11(十二月) 之间的一个整数
    var month = dt.getMonth() + 1;
    var day = dt.getDate();
    //这句话这样写没啥可读性,看着也乱,我们可以用模板字符串
    // return year + "-"+ month + "-" + day;
    return `${year}-${month}-${day}`;
  });
这样就显示了我们想要的格式
更进一步,做一个可以根据用户传入参数进行匹配用户想要的时间格式而不是写死的时间格式
  1. 用户可能传入yyyy-MM-DD 或者YYYY-MM-DD,我们拿到参数时第一步就进行字符转换,比如全转换为小写字符,参数调用
  2. 可能用户没有传入任何东西时,默认用户想要一个全的日期
<td>{{ item.ctime | dateFormat("") }}</td>
......
  Vue.filter("dateFormat",function(data,pattern){
    //根据给定的时间字符串,得到特定的时间
    var dt = new Date(data);
    //自己拼接出一个自己想要的格式 yyyy-mm-dd
    var year = dt.getFullYear();
    //getmonth()的返回值是 0(一月) 到 11(十二月) 之间的一个整数
    var month = dt.getMonth() + 1;
    var day = dt.getDate();
    
    if(pattern.toLowerCase() === "yyyy-mm-dd"){
      //这句话这样写没啥可读性,看着也乱,我们可以用模板字符串
      // return year + "-"+ month + "-" + day;
      return `${year}-${month}-${day}`;
    } else {
      var hh = dt.getHours();
      var mm = dt.getMinutes();
      var ss = dt.getSeconds();
      return `${year}-${month}-${day} ${hh}:${mm}:${ss}`;
    }
  });

这样写有一个弊端,当用户什么都没有输入时,pattern为undefiend,不能调用toLowerCase函数,我们可以改为这样:

    if(pattern && pattern.toLowerCase() === "yyyy-mm-dd"){

这时即便什么都没有输入

          <td>{{ item.ctime | dateFormat() }}</td>
还是能正确显示不会报错

还可以这样改:给形参一个默认值

  Vue.filter("dateFormat",function(data,pattern=""){
  if(pattern.toLowerCase() === "yyyy-mm-dd"){

私有过滤器

上面的写法是全局过滤器,任何vue实例都可以用。那如果自定义一个私有过滤器(局部)?

    <p>{{ dt | dtFormat }}</p>
.......
<script>
  new Vue({
    el: "#app",
    data: {
      dt: new Date()
    },
    methods: {

    },
    filters:{
      //是一个对象
      //在这里面定义私有过滤器,过滤器有两个条件:过滤器名称 和 处理函数
      dtFormat:function(data,pattern=""){
        var dt = new Date(data);
        var year = dt.getFullYear();
        var month = dt.getMonth() + 1;
        var day = dt.getDate();
        if(pattern.toLowerCase() === "yyyy-mm-dd"){
          return `${year}-${month}-${day}`;
        } else {
          var hh = dt.getHours();
          var mm = dt.getMinutes();
          var ss = dt.getSeconds();
          return `${year}-${month}-${day} ${hh}:${mm}:${ss}`;
        }
      }
    }
  });
</script>

过滤器调用采取就近原则,如果Vue实例的私有过滤器与公共过滤器重名了,调用时优先调用私有过滤器。

toLowerCase()
Date相关的函数:getFullYear() , getMonth(),getDate(),getHours(),getMinutes() ,getSeconds(),
参数默认值,没有传入参数时参数为undefined
Vue.filter:定义全局过滤器

继续优化日期格式,在月份,天数,时,分,秒不足两位的时候,在开头补零

在es6中,用String.prototype.padStart(maxLength,fillString=""),或用String.prototype.padEnd(maxLength,fillString="")来填充字符串。
1.需要在前面补零,用padStart()方法
2.padStart()是针对字符串的方法,故要先把取到的月份等转为字符串,用toString()

// Vue.filter("dateFormat",function(data,pattern){
  Vue.filter("dateFormat",function(data,pattern=""){
    //根据给定的时间字符串,得到特定的时间
    var dt = new Date(data);
    //自己拼接出一个自己想要的格式 yyyy-mm-dd
    var year = dt.getFullYear();
    //getmonth()的返回值是 0(一月) 到 11(十二月) 之间的一个整数
    var month = (dt.getMonth() + 1).toString().padStart(2,"0");
    var day = dt.getDate().toString().padStart(2,"0");
    
    // if(pattern && pattern.toLowerCase() === "yyyy-mm-dd"){
    if(pattern.toLowerCase() === "yyyy-mm-dd"){
      //这句话这样写没啥可读性,看着也乱,我们可以用模板字符串
      // return year + "-"+ month + "-" + day;
      return `${year}-${month}-${day}`;
    } else {
      var hh = dt.getHours().toString().padStart(2,"0");
      var mm = dt.getMinutes().toString().padStart(2,"0");
      var ss = dt.getSeconds().toString().padStart(2,"0");
      return `${year}-${month}-${day} ${hh}:${mm}:${ss}`;
    }
  });
image.png

String的函数:padEnd(),padStart()

有一新需求,写完id跟姓名后按回车键也能完成添加功能

  1. 按回车键完成添加--属于键盘事件
        <label>Name: <input type="text" class="form-control" v-model="name" @keyup="add"> </label>
每写一个字符,就执行一次add函数,我们需要标识enter键,只有按下enter键的时候执行函数

实现这一功能可通过按键修饰符,或者键码

<label>Name: <input type="text" class="form-control" v-model="name" @keyup.enter="add"> </label>
//或者
<label>Name: <input type="text" class="form-control" v-model="name" @keyup.13="add"> </label>

键值修饰符
自定义全局按键修饰符:Vue.config.keyCodes.名称 = 键盘值
 <label>Name: <input type="text" class="form-control" v-model="name" @keyup.n="add"> </label>
 Vue.config.keyCodes.n = 78;

键盘修饰符 , 如何自定义按键修饰符, 键盘值,keyup事件,Vue.config.

实现需求一刷新或者一进页面鼠标就聚焦在按关键字搜索的输入框里

有以下三种方法:

  1. 获取到DOM元素,利用原生JS的focus()方法获取焦点
    该方法不演示
  2. 在vue中不提倡操作DOM元素,可以通过自定义指令实现这一功能
    在Vue中,调用指令都以v-开头,就像在Angular中都以"ng-"开头,这是个约定,我们自定义指令时也尽量保持这一约定
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
指令钩子函数会被传入以下参数:参数名可以自定义,但参数里的属性名不可更改
(copy了官网,哭笑)
小例子
<label>请输入搜索的关键字: <input type="text" class="form-control" v-model="keywords" v-focus> </label>
//定义全局的指令
  //第一个参数是指令的名称,注意不要使用v-前缀,
  // 第二个参数是一个对象,它身上有一些与指令相关的钩子函数,这些函数可以在特定的阶段执行相应的操作
  //在所有的钩子函数中,第一个参数是el,是我们要绑定的元素 el参数是一个原生的JS对象(DOM对象,有DOM对象的方法)
  Vue.directive("focus",{
    bind: function(el){
//每当指令绑定到元素上的时候会执行这个函数,只执行一次,因为只绑定一次,早于inserted

      el.focus();
    },
    inserted: function(){
// 元素插入到DOM中的时候,触发一次
    },
    update: function(){
//当VNode更新的的时候,可能会触发多次
    },
    componentUpdated: function(){

    },
    unbind: function(){

    }

  })

这样写的话,并没有完成我们的需求,因为只是绑定到了元素身上,我们需要弄清楚这些钩子函数的执行顺序。
浏览器是先解析代码,生成DOM树,再把元素渲染到页面中去,我们绑定焦点只能在元素放入到DOM中后绑定。
bind钩子函数的时候,并没有把元素放到DOM中去,故绑定焦点失败,但注意不会报错。

    inserted: function(el){
// 元素插入到DOM中的时候,触发一次
      el.focus();
    },

这样就可以了

  1. 利用html元素的autofocus属性
    它在移动版safari上不生效
<label>请输入搜索的关键字: <input type="text" class="form-control" v-model="keywords" autofocus> </label>

深入学习一下自定义指令,通过指令设置元素字体颜色

1.因为函数的el是原生DOM对象,故我们可以通过DOM对象的属性进行设置

 <label>请输入搜索的关键字: <input type="text" class="form-control" v-model="keywords" v-focus v-color> </label>
......
 Vue.directive("color",{
    bind: function(el){
      el.style.color = "red";
    },
 });
颜色变啦
这里可以放在bind钩子函数,上面不可以放在bind钩子函数里的原因:

设置颜色只是对DOM元素的属性设置,解析DOM元素时也会一起解析,而获取焦点是一个行为,得有行为的发起者,故只能在插入DOM后再添加行为。

也可以给这个指令传递一个color的值。

 <label>请输入搜索的关键字: <input type="text" class="form-control" v-model="keywords" v-focus v-color="'blue'"> </label>

注意这里写的是v-color="'blue'",而不是 v-color="blue"。写v-color="'blue'"是有一个字符串blue,而写 v-color="blue"则是写了一个blue变量,会到data里边找变量。

  Vue.directive("color",{
    bind: function(el, binding){
      console.log(binding.value);
      console.log(binding.expression);
      el.style.color= binding.value;
    },
  });
变为蓝的了
区分value和expression
学了这么多,那么如何定义私有指令呢?

跟过滤器类似

  <div id="app">
    <p v-fontweight="200">一个p标签</p>
  </div>
......
<script>
var vm = new Vue({
  el: "#app",
  data:{},
  methods:{},
  filters:{
    //定义私有过滤器
  },
  directives:{
    //定义私有自定义指令,指令名称不能写成fontWeight,必须全部小写。
    "fontweight":{
      bind: function(el,binding){
        console.log(binding.value);
        el.style.fontWeight = binding.value;
      }
    }
  }
});
</script>
可以看到正确输出了
指令函数简写形式:
Vue.directive('color-swatch', function (el, binding) {
  el.style.backgroundColor = binding.value
})
    <p v-fontsize="'200px'">一个p标签</p>
    <p v-fontsize="200">一个p标签</p>
......
  directives:{
    "fontsize": function (el, binding) {
        console.log(binding.value);
//这样写提高了代码的健壮性
        el.style.fontSize = parseInt(binding.value) + "px";
      }
    }

相当于在 bind 和 update里同时写了el.style.fontSize = parseInt(binding.value) + "px";

Vue.directive定义全局自定义指令
directives:{}定义私有自定义指令
指令名必须全部小写,bind(),update(),inserted(),el,binding.name,binding.value,binding.expression
el是DOM对象,具有DOM对象的属性和方法
指令参数传递,传递字符串需用单引号引起来,否则会当成data里的变量去处理

上一篇下一篇

猜你喜欢

热点阅读