angularjs 1.3 综合学习 (one way bind , ng-if , ng-switch , ng-messages, ng-form ,ng-model )

主要讲解1.3后的一些新功能,和一些以前没有介绍的小功能 (ng-if,ng-switch).

1.one way bind 

 这个之前的版本已经有人自己实现了,但是在1.3之后,angularjs 有自带的了。用法极其简单 . 

<div ng-app="app" ng-controller="ctrl">
    {{ ::value }}
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>
    var app = angular.module("app", []);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.value = "keatkeat";
        $timeout(function () {
            $scope.value = "xinyao";
        },2000);
    }]);
</script>

看到吗 ?只是把从前的 {{ value }} 改成 {{ ::value }} . 加了 :: 就表示这个值只是要单向绑定,之后$scope改变了也不会在同步到模板了。

2.ng-if

 ng-if 是用来做动态模板的,如果你的模板有一部分内容是依据某些数据来决定的就可以用啦。

   他和ng-hide主要区别在,ng-if 如果是false 的情况它不会生成dom,这有时对性能是好的。

   ng-if 为true时,angularjs 会使用之前clone好的模板,然后compile了append出去。 

<div ng-app="app" ng-controller="ctrl">
        <div ng-if="isTrue">
        ok
        </div>
        <div ng-if="!isTrue">
        no
        </div>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>
    var app = angular.module("app", []);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {        
        $scope.isTrue = false;
        $timeout(function () {
            $scope.isTrue = true;
        }, 2000);
    }]);
</script>

angularjs 没有 else 指令(不过有人自己实现了) , 不过我们可以用上面的写法来模拟 else 

ng-if 会自动创建新的继承 scope . 在有ng-model 的情况下多留意,有坑.

<div ng-app="app" ng-controller="ctrl">
    <div ng-form="form">
        parent value : {{value}} 
        <br />
        model value :{{ form.age.$modelValue }}
        <div ng-if="isTrue">
            <input type="text" name="age" ng-model="value" />
            <br />
            child value : {{value}}
        </div>
    </div>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>
    var app = angular.module("app", []);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { 
        $scope.isTrue = true;
        $scope.value = "keatkeat";         
    }]);
</script>

当我们输入 text 的时候 , $modelValue 和 child value 都会更新,但是 parent value 却不会了。这是正确的原型概念 (http://www.cnblogs.com/keatkeat/p/3983952.html) 

一般情况我们在做form 时, 通常我们设计一个对象用于装所有的 value ,这样就不必担心 继承scope引起的问题了 . 

稍微改一下就可以了.

<div ng-app="app" ng-controller="ctrl">
    <div ng-form="form">
        parent value : {{value.age}} 
        <br />
        model value :{{ form.age.$modelValue }}
        <div ng-if="isTrue">
            <input type="text" name="age" ng-model="value.age" />
            <br />
            child value : {{value.age}}
        </div>
    </div>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>
    var app = angular.module("app", []);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { 
        $scope.isTrue = true;
        $scope.value = { age : 13 }; //换成对象         
    }]);
</script>
View Code

3.ng-switch 

 这个和 ng-if 差不多,和一般js switch 一样的概念 . 它也是会创建继承scope哦.

    <div ng-app="app" ng-controller="ctrl"> 
        <div ng-switch="switchCase"> 
            <div ng-switch-when="case1">case2</div><!--case1 必须是一个 string, 不可以是表达式 , (case1 != $scope.case1)-->
            <div ng-switch-when="case2">case2</div>
            <div ng-switch-default>default case</div>
        </div>
    </div>
    <script src="../../js/Stooges.js"></script>
    <script src="../../js/ng-1.3.10/angular.js"></script>
    <script>
        var app = angular.module("app", []);
        app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {         
            $scope.switchCase = "case2";
            $timeout(function () {
                $scope.switchCase = "case1";
            }, 2000);
        }]);
    </script>

 4. ng-messages

    这个主要用于form 验证错误时的error message.

<div ng-app="app" ng-controller="ctrl">       
    <form novalidate>
        <div ng-form="form">
            <input type="email" ng-minlength="2" required name="email" ng-model="formData.email" />                    
            <div ng-messages="form.email.$error" ng-messages-multiple>
                <div ng-message="required">must fill in email</div>
                <div ng-message="email">no a email format</div>
                <div ng-message="minlength">at least 2 words</div>
            </div>
        </div>
    </form>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script src="../../js/ng-1.3.10/angular-messages.js"></script>
<script>
    var app = angular.module("app", ['ngMessages']);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.formData = {};
    }]);
</script>

注意这是一个而且的模块必须加载 ['ngMessages'] 和引入文件 angular-messages.js

上面是一个能匹配多个错误信息的例子. 当错误出现时,特定的div就会被显示出来了。form.email.$error 是一个这样的对象 :  { "email": true, "minlength": true } , true 表示验证失败了.

5. ng-model and ng-form 

这2个我之前写过一篇了 http://www.cnblogs.com/keatkeat/p/3912530.html

当时还在1.3 beta , 现在我会把一些常用到的在讲解一篇.还有一些新新特性也一起说说。

首先要强调,如果你想把表单做好,做的很 "angular way" 的话, ng-form , ng-model , 自定义指令, 验证, 都必须很清楚。你把它们连在一起才能强大。

5.1 ngModel.$formatters 和 ngModel.$parsers

这2个东西是 ng-model 在同步数据的时候的 pipeline , ngModel 有2个主要属性 ($viewValue , $modelValue) 

顾名思义啦, 这2个值通常是一样的。但是有时候我们希望他们不一样,比如日期格式。在 $modelValue 是 datetime , 在 $viewValue 是 string . 

这时我们就可以通过拦截 pipeline , 做一些convertion 了.

<div ng-app="app" ng-controller="ctrl">       
    <form novalidate autocomplete="off">
        <div ng-form="form">
            model value : {{ form.name.$modelValue }} , Type : {{ getType(form.name.$modelValue) }} <br />
            view value : {{ form.name.$viewValue }} , Type : {{ getType(form.name.$viewValue) }} <br />
            <input type="text" name="name" ng-model="formData.name" my-datetime-convert /><br />
               
        </div>
    </form>
</div> 
<script src="../../js/ng-1.3.10/angular.js"></script> 
<script>
    var app = angular.module("app", []);  
    app.directive("myDatetimeConvert", [function () {
        return {
            restrict: "A",
            require: "ngModel",
            link: function (scope, elem, attrs, ngModel) {
                ngModel.$formatters.push(function (value) {
                    return value.toDateString(); //把 datetime 各式转换成 string 格式.
                });
                ngModel.$parsers.push(function (value) {
                    try {
                        return new Date(value);
                    } catch (e) {
                        return undefined;
                    }                        
                });
            }
        }
    }]);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.formData = {
            name : new Date()
        };
        $scope.getType = function (value) {
            return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
        }
    }]);
</script>

上面就是一个处理 datetime 的例子。

$formatters 触发当 model -> view 

$parsers 触发当 view -> model 

 5.2 ng-model-options

 ng-model-options="{ updateOn: 'blur' , debounce: 2000 }" 

 updateOn 表示当什么 event 触发时写入 $viewValue ,  debounce 表示delay多久后把 $viewValue 同步去 $modelValue 

 上面这个case , 如果我们写入一个 "keatkeat" , 当onblur 时 $viewValue 马上会是 "keatkeat" , 2秒后 $modelValue 会是 "keatkeat" . 

值得注意的是 : 如果我们只是写了一个 debounce , 那么 $viewValue 并不会马上有"keatkeat",而是 2 秒后  $viewValue 和 $modelValue 同时是 "keatkeat" . 建议不一起使用 ! 

 

5.3 ng-model 属性方法

方法 : 

$render 

render中文是渲染,当$modelValue 被修改 $digest 后会同步到 $viewValue (中间经过 pipeline) , 之后angular会调用 $render. 

在做自定义指令时,我们就是靠注册这个方法来完成我们 dom 修改的。 

在方法中,我们应该使用 $viewValue 工作,而不是 $modelValue 哦。

$rollbackViewValue

调用这个方法,$modeValue 的值将同步到 $viewModel 上 

<input type="text" name="name" ng-model="formData.name" ng-model-options="{ updateOn: 'blur' }" ng-keyup="keyup($event,form.name)" />

$scope.keyup = function (event, ngModel) {
    if (event.keyCode == 27) {
        ngModel.$rollbackViewValue();
    }
}

一般上是配合 keyup Esc 和 updateOn : 'blur' 使用

$commitViewValue

这个和 $rollbackViewValue 相反,是马上把 $viewValue 同步到 $modelValue 上。 

属性 : 

$validators

顾名思义就是做验证的啦,1.3以前我们是通过pipeline来做验证的,很麻烦。

现在呢用 $validators 就可以了 

<div ng-app="app" ng-controller="ctrl">
    <form novalidate autocomplete="off">
        <div ng-form="form">
            model value : {{ form.name.$modelValue }}<br />
            view value : {{ form.name.$viewValue }}<br />
            error : {{ form.name.$error | json }}
            <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 2000 }" my-valid />
            <br />              
        </div>
    </form>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>     
    var app = angular.module("app", []);
    app.directive("myValid", [function () {
        return {
            restrict: "A",
            require: "ngModel",
            link: function (scope, elem, attrCollection, ngModel) {
                ngModel.$validators["myValid"] = function (modelValue, viewValue) {
                    return modelValue == "kknd";                        
                }
            }
        }
    }]);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.formData = { name: "kknd" }; 
    }]);
</script>

$asyncValidators & $pending

而且还支持异步的 

<div ng-app="app" ng-controller="ctrl">
    <form novalidate autocomplete="off">
        <div ng-form="form">
            model value : {{ form.name.$modelValue }}<br />
            view value : {{ form.name.$viewValue }}<br />
            error : {{ form.name.$error | json }} <br />
            pending : {{ form.name.$pending }}
            <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 1000 }" my-valid />
            <br />              
        </div>
    </form>
</div>
<script src="../../js/Stooges.js"></script>
<script src="../../js/ng-1.3.10/angular.js"></script>
<script>     
    var app = angular.module("app", []);
    app.directive("myValid", ["$q",function ($q) {
        return {
            restrict: "A",
            require: "ngModel",
            link: function (scope, elem, attrCollection, ngModel) {
                ngModel.$asyncValidators["myValid"] = function (modelValue, viewValue) {
                    var defer = $q.defer();
                    log("async valid send");
                    setTimeout(function () {
                        log("async valid back");
                        if (modelValue == "kknd") {
                            defer.resolve(true);
                        }
                        else {
                            defer.reject(false);
                        }                            
                    }, 3000);
                    return defer.promise;                         
                }
            }
        }
    }]);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.formData = { name: "kknd33" }; 
    }]);
</script>

$pending = { myValid : true } 表示正在验证. 

注册到$asyncValidators 中的方法,我们需要返回一个 promise 对象就可以了.  

验证如果不通过,$modelValue 将会是 undefined

$setViewValue

这个方法主要是用于自定义指令时,我们通过事件来更新ng model 的值 

比如 input text , 就是写一个 keyup event , 当keyup 触发时就会调用 $setViewValue(inputValue); 

这个方法会更新 $viewValue , $modelValue , 也会触发 pipeline , validators . 

注:此方法不会触发 $digest (官方网站是这样写的啦)

But ! 我自己在1.4.5 版本用好像是会的

如果 value 和之前一样的话,digest 会触发,但是$validate 不会触发

$validate()

这个方法是手动调用验证

注:此方法不会触发 $digest (真的不会哦)

ng-form 

有点懒, 这个就不怎么介绍了。

只说重点 ,下次才补上吧 

<form novalidate autocomplete="off">
    <div ng-form="form">
        model value : {{ form.name.$modelValue }}<br />
        view value : {{ form.name.$viewValue }}<br />
        error : {{ form.name.$error | json }} <br />
        pending : {{ form.name.$pending }}
        <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 1000 }" my-valid />
        <div ng-click="click(form.name)">ok</div>
        <br />              
    </div>
</form>

angular 的form 支持嵌套 , 我觉得最佳做法就是上面这个样子,最外层写一个 form , 内层全部用 div + ng-form 指令 

在form 里面,每一个涉及ng-model 的节点,如果有附带 name 的话,都会直接添加进 form controllers 里面 . 

所以上面 form.name 可以访问到 ngModelCtrl

form 有一个 $submitted 属性,可以用于判断是否提交。

要reset 一个form , 除了把ng-model 值 clear 完之外,还必须 $setPristine() 和 $setUntouched();

ng-model 的valid , dirty 等等都会和 form 牵连, form 也会和parent form 牵连 . (这个概念要懂)

好啦,基本上就是这样了,下次我才给一个完整的案例吧 ^^

 

原文地址:https://www.cnblogs.com/keatkeat/p/4299191.html