1. 程式人生 > >angularjs仿拉勾網webapp總結與記錄

angularjs仿拉勾網webapp總結與記錄

這是慕課網上的實戰課程,使用的技術棧有bower+less+angular1.x+gulp。沒有涉及到後端,資料是模擬的json資料。

gulp

less

關於less部分,只使用了一些最基礎的語法。如下:

  • 檔案引用

使用@import。

@import 'a.less';
@import 'b.less';
  • 定義變數

使用的了一個檔案variabel.less專門用來定義變數。在less中定義變數使用@。定義完成後以分號結束

@defaultColor: #fff;
@defaultWidth: 400px;

這樣在其他檔案裡,這要引入了這個檔案,就可以使用這些變數。

@import 'variable.less';
body {
  color: @defaultColor;
}
.box {
  width: @defaultWidth;
}
  • 樣式的巢狀

&表示引用自己。

body {
  color: @defaultColor;
  .box {
    background: red;
    &:hover {
      background: green;
    }
  }
}
  • unit函式

該函式用來刪除或更換單位。
引數:

  1. dimension: 帶單位或不帶單位的數字。
  2. unit: (可選) 目標單位,如果省略此引數,則刪除單位。
案例: unit(5, px)

輸出: 5px

案例: unit(5em)

輸出: 5
  • 函式
    定義一個函式,如下:
.pt(@px) {
  padding-top: unit(@px / 37.5, rem);
}

在使用的使用呼叫這個並傳入引數即可。

.box {
  .pt(20);
}

angular部分

angular使用的外掛有:

  • angular-ui-router
  • angular-validation
  • angular-cookie
  • angular-animate
// app.js
angular.module('app', ['ui.router'
, 'ngCookies', 'validation', 'ngAnimate'])

ui-router中,首先是定義路由,如下:

angular.module('app')
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
  $stateProvider
  .state('main', { // main是當前頁面的id,以後要跳轉到該頁面使用ui-sref="main"或者$state.go('main');
    url: '/main',
    templateUrl: 'view/mian.html',
    controller: 'mainCtrl'
  })
  .state('login', {
    // 定義其他路由等
  })

  // 除了上述路由外,處理其他不匹配的路由
  $urlRouterProvider.otherwise('main');
}])

ui-sref

使用ui-router自帶的ui-sref屬性來定義將要跳轉的頁面的id。

<li ui-sref="main">跳轉到main頁面</li>

如果要傳入引數,如下格式。()中的物件反映的是傳入的引數鍵值對。

<li ui-sref="position({id: item.id})">跳轉到position頁面</li>

在position頁面對應的控制器中,通過$state.params.id取得查詢鍵。

angular.module('app')
.controller('positionCtrl', ['$http', '$state', '$scope', function($http, $state, $scope) {
  $http.get('data/position.json', {
    params: {
      id: $state.params.id
    }
  })
  .then(function() {}, function() {})
}])

可以通過ui-sref-active給選中的標籤新增樣式。比如選單欄。

指令中的scope

$index/$last

在使用ng-repeat渲染元素時,可以使用兩個內建的屬性觀察渲染情況。$scope.$index來檢視渲染的子元素的索引,$scope.$last(boolean)來檢視是否執行完成,如果是true,表示ng-repeat執行完畢。

<button ng-click="showPositionList($index)" ng-repeat="cls in com.positionClass" ng-bind="cls.name"></button>

這個類似於導航欄的按鈕,點選後控制器拿到這個$index,根據不同的$index傳遞給view層不同的資料。

$id/$parent$root

在任何一個控制器中,通過$scope.$id$scope.$parent$scope.$root來表示當前作用域的id,父作用域,根作用域。

通常在angular應用的run方法中定義根作用域相關屬性。

angular.module('app',[]) // 必須引入ng-route
//config方法在整個頁面中最先執行,先於run,
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
  // 在這裡配置路由
}])
// 通常將路由事件定義在根作用域下
.run(function($rootScope) {
//console.log($rootScope);
  $rootScope.$on('$routeChangeStart', function () {
    console.log(this)
    console.log(arguments);
  })
})

value()和constant()

使用constant()來儲存一個常量。
使用value()函式來快取全域性變數(需要的時候可以直接使用)。

constant()和value()方法之間一個最主要的區別是,常量可以注入到配置函式(.config())中,而值不行。

angular.module('app')
.constant('apikey', 'real')
angular.module('app')
.value('dict', {}) // 快取全域性變數
.run(['dict', '$http', function(dict, $http) {
  $http.get('data/city.json').then(function(res) {
    dict.city = res.data;
  })
  $http.get('data/salary.json').then(function(res) {
    dict.salary = res.data;
  })
  $http.get('data/scale.json').then(function(res) {
    dict.scale = res.data;
  })
}])

自定義過濾器

<!--過濾item-->
<li ng-repeat="item in data | filterByObj:filterObj">

filterByObj是一個已經定義好的過濾器。它返回的函式中有兩個引數,第一個list表示上述的data,第二個obj表示filterByObj:後面的filterObj。這個filterObj對應父作用域的一個屬性,根據情況而改變。從而達到篩選效果。

angular.module('app')
.filter('filterByObj', [function() {
  return function(list, obj) {
      var result = [];
      angular.forEach(list, function(item) {
        var isEqual = true;
        for(var e in obj) {
          if(item[e] !== obj[e]) {
            isEqual = false;            
          }
        }
        if(isEqual) {
          result.push(item);
        }
      })

      return result;
  }
}])

$interval

var interval = $interval(fn, timedelay)
$inteval.cancel(interval); // 使用cancel取消這個定時器
// 在原生js中是interval = null;

阻止冒泡

ng-click事件有一個事件物件$eventstopPropagation不需要加括號。

<li ng-click="$event.stopPropagation;select(item);"></li>

$watch

如果在指令中用到了某個屬性,但是這個屬性的值依賴於非同步返回的結果,這時候就需要$watch這個值。否則報錯。

// 1 父作用域
<div app-company com="company"></div>
// 這是副作用域控制器執行的
$http.get('data/company.json?id='+id).then(function(res){
  $scope.company = res.data;
})

// 2
angular.module('app')
.directive('appPositionClass', [function() {
  return {
    restrict: 'A',
    replace: true,
    templateUrl: 'view/template/positionClass.html',
    scope: {
      com: '='
    },

    link: function($scope, element, attr) {
      $scope.$watch('com', function(newVal, oldVal, scope) {
        if(newVal){
          $scope.showPositionList(0);
        }
      })
    }
  }
}])

可以看到指令要想獲得com屬性,依賴於父作用域的$scope.company。而通過一個非同步操作才能返回。所以指令中就$watch這個變數。

angular-validation

首先在index.html頁面中引入angular-validation.js,然後再angular.module(‘app’, [‘validation’])加上依賴。

這個外掛定義了一個$validationProvider服務。用來設定表示式和驗證訊息。

// validation.js
angular.module('app')
.config(['$validationProvider', function($validationProvider) {
  var expression = {
    phone: /^1\d{10}$/,
    password: function(value) {
      return value.length > 5;
    },
    required: function(value) {
      return !!value;
    }
  }
  var defaultMsg = {
    phone: {
      success: '',
      error: '11位手機號'
    },
    password: {
      success: '',
      error: '長度至少6位'
    },
    required: {
      success: '',
      error: '不能為空'
    }
  }

  $validationProvider.setExpression(expression).setDefaultMsg(defaultMsg);
}])

在login頁面中,做了簡單的驗證。

<div class="login">
  <form class="d-b ta-c" name="form">
    <div class="form-line ta-l p-r">
      <span class="icon account va-t d-ib"></span>
      <input name="phone" validator="required,phone" ng-model="user.phone" class="d-ib" type="text" placeholder="輸入手機號">
    </div>
    <div class="form-line ta-l p-r">
      <span class="icon lock va-t d-ib"></span>
      <input name="password" validator="required,password" ng-model="user.password" class="d-ib" type="password" placeholder="輸入密碼">
    </div>
    <button class="login-btn" validation-submit="form" ng-click="submit();">登入</button>
    <button class="register-btn" ui-sref="register">註冊</button>
  </form>
</div>

注意inputvalidator屬性,這裡是驗證的表示式的內容,如果有多個,使用逗號隔開。這個內容對應validation.js中的var expression中的鍵。
注意<button validation-submit="form" ng-click="submit"></button>這裡,form是表單的name屬性。

factory/service

我們使用factory或者service定義服務。service使用this來儲存服務內容,類似建構函式。而factory使用返回值來儲存服務內容。我們定義了一個cache服務。$cookiesangular-cookies外掛提供的。兩種寫法如下:

// app.js中
angular.module('app', ['ui.router', 'ngCookies', 'validation', 'ngAnimate']);

服務:

angular.module('app')
.service('cache', ['$cookies', function($cookies) {
  this.put = function(key, value) {
    $cookies.put(key, value);
  };
  this.get = function(key) {
    return $cookies.get(key);
  };
  this.remove = function(key) {
    return $cookies.remove(key);
  }
}])
angular.module('app')
.factory('cache', ['$cookies', function($cookies) {
  return {
    put: function(key, value) {
      $cookies.put(key, value);
    },
    get: function(key) {
      return $cookies.get(key);
    },
    remove: function(key) {
      return $cookies.remove(key);
    }
  }
}])

講真,我覺得使用這個不如使用localStorage,簡單快捷(涉及到sessionID用cookie?)。

裝飾器

裝飾器是非常強大的,它不僅可以應用在我們的我們自己的服務上,也可以對AngularJS的核心服務進行攔截、中斷甚至替換功能的操作。事實上AngularJS中很多功能的測試就是藉助$provide.decorator()建立的。

由於這個專案沒有後端,為了模擬post請求,我們需要修改$http內建的post請求。

angular.module('app')
.config(['$provide', function($provide) {
  $provide.decorator('$http', ['$delegate', '$q', function($delegate, $q) {
    $delegate.post = function(url, data, config) {
      var def = $q.defer();
      $delegate.get(url).then(function(res) {
        def.resolve(res.data);
      }, function(err) {
        def.reject(err);
      })

      return {
        success: function(cb) {
          def.promise.then(cb);
        },
        error: function(cb) {
          def.promise.then(null, cb);
        }
      }
    }

    return $delegate;
  }])
}])

decorator()函式可以接受兩個引數。

  • name (字串)將要攔截的服務名稱
  • decoratorFn(函式)在服務例項化時呼叫該函式,這個函式由injector.invoke呼叫,可以將服務注入這個函式中。

$delegate是可以進行裝飾的最原始的服務,為了裝飾其他服務,需要將其注入進裝飾器。

$q

內建的$q服務用來在Angular中建立promise。如上所述,我們使用$q.deffer()建立一個延遲物件def。通常返回時通過return def.promise得到一個promise物件。這裡更進一步封裝了。

上述的def使用了兩種方法,分別是:

  • def.resolve(data)
  • def.reject(err)
  • def.notify(data) 上述未提到

如果返回值是def.promise,它的then方法有三個引數。

var promise = $http.post(someurl);
promise.then(function(data) {
  alert('success');
}, funciton(err) {
  alert('failed');
}, function(dataUpdate) {
  alert('update')
})

總結

晚上花了幾個小時寫了總結,學完這個專案之後,確實學到了很多。在寫部落格的過程中,為了表述完全,又特定找了一些資料,看以前的程式碼。更加深了理解。

從今以後,每寫一個專案都要做一次總結。