关于angularjs:angularjs-学习笔记

angularjs官网Awesome AngularJSangularjs菜鸟学院angular 中文网angular老手常常会碰到的坑angularjs外围性能介绍所有的货色都是绑在module下面的 angular .module('app', []) // module .factory() // 工厂 .service() // 服务 .controller() // 控制器 .directive() // 指令 .filter() // 过滤器 angular配置阶段 angular 过滤器具体解释 能够通过过滤器来进行各种顺叙插入等操作angular自定义过滤器demo<!DOCTYPE html><html lang="en"><head> <title>angular自定义过滤器demo</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"></head><body ng-app="myapp" ng-controller="my"> <ul> <li ng-repeat="userLayer in userLayers | reverse" ng-bind="userLayer"></li> </ul></body><script src="https://cdn.bootcss.com/angular.js/1.5.3/angular.min.js"></script><script> angular.module('myapp', []) .controller('my', function($scope, $timeout) { $scope.userLayers = [1, 2, 3, 4, 5, 6, 8]; }) .filter('reverse', function() { return function(items) { return items.slice().reverse(); } })</script></html>angular 罕用指令angular所有指令大全 ...

May 16, 2023 · 4 min · jiezi

关于angularjs:Y-分钟速成-AngularJS

源代码下载: learnangular-cn.html AngularJS 教程AngularJS 1.0 版在 2012 年公布。Miško Hevery, 一位 Google 员工, 从 2009 年开始开发 AngularJS。后果发现这个想法很好,从而该我的项目当初也被 Google 官网所反对了。 AngularJS 是一个 JavaScript 框架。它能够通过一个 "script" 标签增加到一个 HTML 页面中。AngularJS 通过指令扩大了 HTML 属性,并且通过表达式将数据绑定到 HTML。 你应该曾经理解了的常识在学习 AngularJS 之前, 你应该对以下常识有了根本的理解: HTMLCSSJavaScript// AngularJS 是一个 JavaScript 框架。它是一个用 JavaScript 写的库。// AngularJS 以一个 JavaScript 文件的模式公布,并且能通过一个 script 标签增加到一个网页中:// <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>///////////////////////////////////// AngularJS 扩大 HTML//AngularJS 通过 ng-directives 扩大 HTML。//ng-app 指令定义一个 AngularJS 利用。//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到利用的数据上。//ng-bind 指令将利用的数据绑定到 HTML 视图上。<!DOCTYPE html><html> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <body> <div ng-app=""> <p>Name: <input type="text" ng-model="name"></p> <p ng-bind="name"></p> </div> </body></html>/* * 例子解析: * AngularJS 在网页加载后主动开启。 * ng-app 指令通知 AngularJS: <div> 元素是 AngularJS 利用的 "所有者"。 * ng-model 指令将 input 输入框的值绑定到利用的 name 变量上。 * ng-bind 指令将 <p> 元素的 innerHTML 绑定到利用的 name 变量上。*/<tag> 这里是要解析的内容 </tag>///////////////////////////////////// AngularJS 表达式// AngularJS 表达式写在双括号内: {{ 表达式 }}。// AngularJS 表达式采纳和 ng-bind 指令一样的形式将数据绑定到 HTML。// AngularJS 将在编写表达式的原样地位上 "输入" 数据。// AngularJS 表达式十分像 JavaScript 表达式:它们能蕴含文本,运算符和变量。// 例如 {{ 5 + 5 }} 或 {{ firstName + " " + lastName }}<!DOCTYPE html><html> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <body> <div ng-app=""> <p>My first expression: {{ 5 + 5 }}</p> </div> </body></html>//如果你删除了 ng-app 指令, HTML 将原样显示表达式,不对它进行解析:<!DOCTYPE html><html> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <body> <div> <p>My first expression: {{ 5 + 5 }}</p> </div> </body></html>// AngularJS 表达式采纳和 ng-bind 指令一样的形式将 AngularJS 数据绑定到 HTML。<!DOCTYPE html><html><script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <body> <div ng-app=""> <p>Name: <input type="text" ng-model="name"></p> <p>{{name}}</p> </div> </body></html>// AngularJS 的数字相似 JavaScript 的数字:<div ng-app="" ng-init="quantity=1;cost=5"> <p>Total in dollar: {{ quantity * cost }}</p></div>//AngularJS 的字符串相似 JavaScript 的字符串:<div ng-app="" ng-init="firstName='John';lastName='Doe'"> <p>The name is <span ng-bind="firstName + ' ' + lastName"></span></p></div>//AngularJS 的对象相似 JavaScript 的对象:<div ng-app="" ng-init="person={firstName:'John',lastName:'Doe'}"> <p>The name is {{ person.lastName }}</p></div>//AngularJS 的数组相似 JavaScript 的数组:<div ng-app="" ng-init="points=[1,15,19,2,40]"> <p>The third result is {{ points[2] }}</p></div>// 和 JavaScript 表达式一样, AngularJS 表达式能蕴含文本,运算符和变量。// 和 JavaScript 表达式不同, AngularJS 表达式能写在 HTML 内。// AngularJS 表达式不反对条件,循环和异样,而 JavaScript 表达式却反对。// AngularJS 表达式反对过滤器,而 JavaScript 表达式不反对。///////////////////////////////////// AngularJS 指令//AngularJS 指令应用前缀 ng- 扩大 HTML 属性。//ng-app 指令初始化一个 AngularJS 利用。//ng-init 指令初始化利用的数据。//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到利用的数据上。<div ng-app="" ng-init="firstName='John'"> <p>Name: <input type="text" ng-model="firstName"></p> <p>You wrote: {{ firstName }}</p></div>//应用 ng-init 并不常见。你将在无关控制器的章节中学习如何初始化数据。//ng-repeat 指令会反复一个 HTML 元素:<div ng-app="" ng-init="names=['Jani','Hege','Kai']"> <ul> <li ng-repeat="x in names"> {{ x }} </li> </ul></div>//ng-repeat 指令用在一个对象数组上:<div ng-app="" ng-init="names=[{name:'Jani',country:'Norway'},{name:'Hege',country:'Sweden'},{name:'Kai',country:'Denmark'}]"> <ul> <li ng-repeat="x in names"> {{ x.name + ', ' + x.country }} </li> </ul></div>// AngularJS 最适宜用于数据库 CRUD (Create Read Update Delete) 的利用。// 只需构想这些对象都是来自一个数据库的记录。// ng-app 指令定义一个 AngularJS 利用的根元素。// ng-app 指令将在页面加载后主动启动(主动初始化)利用。// 稍后你将学习如何为 ng-app 设置一个值(如 ng-app="myModule"), 来连贯代码模块。// ng-init 指令为一个 AngularJS 利用定义初始值。// 通常,你不太应用 ng-init。你会转而应用一个控制器或模块。// 你将在稍后学到更多无关控制器和模块的内容。//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到利用的数据上。//ng-model 指令还能://为利用的数据提供类型验证 (number, email, required)。//为利用的数据提供状态信息 (invalid, dirty, touched, error)。//为 HTML 元素提供 CSS 类。//将 HTML 元素绑定到 HTML 表单。//ng-repeat 指令为汇合(一个数组)中的每个元素克隆出 HTML 元素。///////////////////////////////////// AngularJS 控制器// AngularJS 控制器管制 AngularJS 利用中的数据。// AngularJS 控制器就是惯例的 JavaScript 对象。// AngularJS 利用由控制器管制。// ng-controller 指令定义利用的控制器。// 一个控制器就是一个 JavaScript 对象, 通过规范的 JavaScript 对象构建器创立。<div ng-app="myApp" ng-controller="myCtrl">First Name: <input type="text" ng-model="firstName"><br>Last Name: <input type="text" ng-model="lastName"><br><br>Full Name: {{firstName + " " + lastName}}</div><script>var app = angular.module('myApp', []);app.controller('myCtrl', function($scope) { $scope.firstName = "John"; $scope.lastName = "Doe";});</script>//利用的解析://AngularJS 利用通过 ng-app="myApp" 定义。该利用运行在 <div> 内。//ng-controller="myCtrl" 属性是一个 AngularJS 指令。它定义了一个控制器。//myCtrl 函数是一个 JavaScript 函数。//AngularJS 将应用一个 $scope 对象来调用控制器。//AngularJS 中, $scope 就是该利用对象(利用的变量和函数的所有者)。//该控制器在 $scope 内创立了两个属性(即变量 firstName 和 lastName)。//ng-model 指令将输出表单项绑定到控制器的属性上(firstName 和 lastName)。//以上的例子演示了一个蕴含有两个属性 lastName 和 firstName 的控制器。//一个控制器也能够有办法(函数的变量):<div ng-app="myApp" ng-controller="personCtrl">First Name: <input type="text" ng-model="firstName"><br>Last Name: <input type="text" ng-model="lastName"><br><br>Full Name: {{fullName()}}</div><script>var app = angular.module('myApp', []);app.controller('personCtrl', function($scope) { $scope.firstName = "John"; $scope.lastName = "Doe"; $scope.fullName = function() { return $scope.firstName + " " + $scope.lastName; }});</script>//在较大型的利用中, 通常是将控制器代码保留在内部文件中。//只需将 <script> </script> 标签之间的代码复制到一个名为 personController.js 的内部文件中:<div ng-app="myApp" ng-controller="personCtrl">First Name: <input type="text" ng-model="firstName"><br>Last Name: <input type="text" ng-model="lastName"><br><br>Full Name: {{firstName + " " + lastName}}</div><script src="personController.js"></script>// 为不便下个例子应用,咱们将创立一个新的控制器文件:angular.module('myApp', []).controller('namesCtrl', function($scope) { $scope.names = [ {name:'Jani',country:'Norway'}, {name:'Hege',country:'Sweden'}, {name:'Kai',country:'Denmark'} ];});//将文件保留为 namesController.js://而后在一个利用中应用该控制器:<div ng-app="myApp" ng-controller="namesCtrl"><ul> <li ng-repeat="x in names"> {{ x.name + ', ' + x.country }} </li></ul></div><script src="namesController.js"></script>///////////////////////////////////// AngularJS 过滤器// 过滤器能够通过一个管道符增加到表达式和指令上。// AngularJS 过滤器能用来转换数据:- **currency**: 将一个数字格式化成货币格局。- **filter**: 从一个数组中抉择一组子集元素。- **lowercase**: 将一个字符串格式化成小写模式。- **orderBy**: 根据一个表达式排序一个数组。- **upper**: 将一个字符串格式化成大写模式。//一个过滤器能够通过一个管道符 (|) 及一个过滤器表达式增加到一个表达式上。//(在上面的两个例子中,咱们将应用前一章中的 person 控制器)//uppercase 过滤器将字符串格式化成大写格局:<div ng-app="myApp" ng-controller="personCtrl"><p>The name is {{ lastName | uppercase }}</p></div>//lowercase 过滤器将字符串格式化成小写格局:<div ng-app="myApp" ng-controller="personCtrl"><p>The name is {{ lastName | lowercase }}</p></div>//currency 过滤器将一个数字格式化成货币格局:<div ng-app="myApp" ng-controller="costCtrl"><input type="number" ng-model="quantity"><input type="number" ng-model="price"><p>Total = {{ (quantity * price) | currency }}</p></div> //一个过滤器能够通过一个管道符 (|) 及一个过滤器表达式增加到一个指令上。//orderBy 过滤器依据一个表达式排序一个数组:<div ng-app="myApp" ng-controller="namesCtrl"><ul> <li ng-repeat="x in names | orderBy:'country'"> {{ x.name + ', ' + x.country }} </li></ul><div>//一个输入框过滤器能够通过一个管道符 (|) //以及后跟一个冒号和模式名的 filter 增加到一个指令上。//该过滤器从一个数组中抉择一个子集:<div ng-app="myApp" ng-controller="namesCtrl"><p><input type="text" ng-model="test"></p><ul> <li ng-repeat="x in names | filter:test | orderBy:'country'"> {{ (x.name | uppercase) + ', ' + x.country }} </li></ul></div>///////////////////////////////////// AngularJS AJAX - $http//$http 是一个从近程服务器读取数据的 AngularJS 服务。// 以下数据可由一个 web 服务器提供:// http://www.w3schools.com/angular/customers.php// **拜访 URL 来查看数据格式**// AngularJS $http 是一个从 web 服务器上读取数据的外围服务。// $http.get(url) 这个函数用来读取服务器数据。<div ng-app="myApp" ng-controller="customersCtrl"> <ul> <li ng-repeat="x in names"> {{ x.Name + ', ' + x.Country }} </li></ul></div><script>var app = angular.module('myApp', []);app.controller('customersCtrl', function($scope, $http) { $http.get("http://www.w3schools.com/angular/customers.php") .success(function(response) {$scope.names = response.records;});});</script>// 利用解析:// AngularJS 利用由 ng-app 定义。该利用运行在一个 <div> 中。// ng-controller 指令命名控制器对象。// customersCtrl 函数是一个规范的 JavaScript 对象结构器。// AngularJS 会应用一个 $scope 和 $http 对象来调用 customersCtrl。// $scope 就是该利用对象(利用的变量和函数的所有者)。// $http 是一个用于申请内部数据的 XMLHttpRequest 对象。// $http.get() 从 http://www.w3schools.com/angular/customers.php 读取 JSON 数据。// 如果胜利, 该控制器会依据来自服务器的 JSON 数据,在 $scope 中创立一个属性 (names)。// 向不同的服务器(不同于申请页)申请数据,称作跨站 HTTP 申请。// 跨站申请在网站上很广泛。许多网页会从不同的服务器加载 CSS,图片和脚本。// 在古代浏览器中,基于平安起因,从脚本内进行跨站 HTTP 申请是被禁止的。// 上面的这行代码,已被退出到咱们的 PHP 例子中,以便容许跨站拜访。header("Access-Control-Allow-Origin: *");///////////////////////////////////// AngularJS 表格// 应用 angular 显示表格非常简单:<div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names"> <td>{{ x.Name }}</td> <td>{{ x.Country }}</td> </tr></table></div><script>var app = angular.module('myApp', []);app.controller('customersCtrl', function($scope, $http) { $http.get("http://www.w3schools.com/angular/customers.php") .success(function (response) {$scope.names = response.records;});});</script>// 要排序表格,增加一个 orderBy 过滤器:<table> <tr ng-repeat="x in names | orderBy : 'Country'"> <td>{{ x.Name }}</td> <td>{{ x.Country }}</td> </tr></table>// 要显示表格索引值,增加一个带有 $index 的 <td>:<table> <tr ng-repeat="x in names"> <td>{{ $index + 1 }}</td> <td>{{ x.Name }}</td> <td>{{ x.Country }}</td> </tr></table>// 应用 $even 和 $odd<table> <tr ng-repeat="x in names"> <td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Name }}</td> <td ng-if="$even">{{ x.Name }}</td> <td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Country }}</td> <td ng-if="$even">{{ x.Country }}</td> </tr></table>///////////////////////////////////// AngularJS HTML DOM//AngularJS 有用于将利用的数据绑定到 HTML DOM 元素属性的指令。// ng-disabled 指令将 AngularJS 利用的数据绑定到 HTML 元素的 disabled 属性上。<div ng-app="" ng-init="mySwitch=true"><p><button ng-disabled="mySwitch">Click Me!</button></p><p><input type="checkbox" ng-model="mySwitch">Button</p></div>//利用解析:// ng-disabled 指令将利用的 mySwitch 数据绑定到 HTML 按钮的 disabled 属性上。// ng-model 指令将 HTML checkbox 元素的值绑定到 mySwitch 的值上。// 如果 mySwitch 的值求值为 true,则该按钮将被禁用:<p><button disabled>Click Me!</button></p>// 如果 mySwitch 的值求值为 false,则该按钮将不会被禁用:<p> <button>Click Me!</button></p>// ng-show 指令显示或暗藏一个 HTML 元素。<div ng-app=""><p ng-show="true">I am visible.</p><p ng-show="false">I am not visible.</p></div>// ng-show 指令基于 ng-show 的值显示(或暗藏)一个 HTML 元素。// 你能够应用任何能求值成 true 或 false 的表达式:<div ng-app=""><p ng-show="hour > 12">I am visible.</p></div>///////////////////////////////////// AngularJS 事件// AngularJS 有它本人的 HTML 事件指令。// ng-click 指令定义一个 AngularJS 点击事件。<div ng-app="myApp" ng-controller="myCtrl"><button ng-click="count = count + 1">Click me!</button><p>{{ count }}</p></div><script>var app = angular.module('myApp', []);app.controller('myCtrl', function($scope) { $scope.count = 0;});</script>// ng-hide 指令可用于设置一个利用的局部区域的可见性。// 值 ng-hide="true" 使得一个 HTML 元素不可见。// 值 ng-hide="false" 使得一个 HTML 元素可见。<div ng-app="myApp" ng-controller="personCtrl"><button ng-click="toggle()">Toggle</button><p ng-hide="myVar">First Name: <input type="text" ng-model="firstName"><br>Last Name: <input type="text" ng-model="lastName"><br><br>Full Name: {{firstName + " " + lastName}}</p></div><script>var app = angular.module('myApp', []);app.controller('personCtrl', function($scope) { $scope.firstName = "John", $scope.lastName = "Doe" $scope.myVar = false; $scope.toggle = function() { $scope.myVar = !$scope.myVar; };});</script>//利用解析:// personController 的第一局部和讲述控制器章节中的一样。// 该利用有一个默认属性(一个变量):$scope.myVar = false:// ng-hide 指令根据 myVar 的值(true 或 false),// 设置 <p> 元素的可见性,该元素含有两个输入框。// 函数 toggle() 将 myVar 在 true 和 false 间进行切换。// 值 ng-hide="true" 使得该元素不可见。// ng-show 指令也能用来设置一个利用的某局部的可见性。// 值 ng-show="false" 使得一个 HTML 元素不可见。// 值 ng-show="true" 使得一个 HTML 元素可见。// 这个例子与下面的一样,但用 ng-show 代替了 ng-hide:<div ng-app="myApp" ng-controller="personCtrl"><button ng-click="toggle()">Toggle</button><p ng-show="myVar">First Name: <input type="text" ng-model="firstName"><br>Last Name: <input type="text" ng-model="lastName"><br><br>Full Name: {{firstName + " " + lastName}}</p></div><script>var app = angular.module('myApp', []);app.controller('personCtrl', function($scope) { $scope.firstName = "John", $scope.lastName = "Doe" $scope.myVar = true; $scope.toggle = function() { $scope.myVar = !$scope.myVar; }});</script>///////////////////////////////////// AngularJS 模块// 一个 AngularJS 模块定义一个利用。// 模块是一个利用的不同局部所在的一个容器。// 模块是利用控制器的一个容器。// 控制器总是隶属于一个模块。// 这个利用 ("myApp") 有一个控制器 ("myCtrl"):<!DOCTYPE html><html><script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script><body><div ng-app="myApp" ng-controller="myCtrl">{{ firstName + " " + lastName }}</div><script>var app = angular.module("myApp", []);app.controller("myCtrl", function($scope) { $scope.firstName = "John"; $scope.lastName = "Doe";});</script></body></html>// 在 AngularJS 利用中通常将模块和控制器搁置在 JavaScript 文件中。// 在本例中,"myApp.js" 蕴含了一个利用模块的定义,而 "myCtrl.js" 蕴含了控制器:<!DOCTYPE html><html><script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script><body><div ng-app="myApp" ng-controller="myCtrl">{{ firstName + " " + lastName }}</div><script src="myApp.js"></script><script src="myCtrl.js"></script></body></html>//myApp.jsvar app = angular.module("myApp", []); // 模块定义中的 [] 参数可用来定义依赖的模块。// myCtrl.jsapp.controller("myCtrl", function($scope) { $scope.firstName = "John"; $scope.lastName= "Doe";});// JavaScript 中应该防止应用全局函数。它们会非常容易地被笼罩// 或被其它脚本毁坏。// AngularJS 脚本通过将所有函数保留在模块内,缓解了这种问题。// 尽管 HTML 利用中通常是将脚本搁置在// <body> 元素的开端,但还是举荐你要么在// <head> 中要么在 <body> 的结尾处加载 AngularJS 库。// 这是因为对 angular.module 的调用只有在库被加载后能力被编译。<!DOCTYPE html><html><body><script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script><div ng-app="myApp" ng-controller="myCtrl">{{ firstName + " " + lastName }}</div><script>var app = angular.module("myApp", []);app.controller("myCtrl", function($scope) { $scope.firstName = "John"; $scope.lastName = "Doe";});</script></body></html>///////////////////////////////////// AngularJS 利用// AngularJS 模块定义 AngularJS 利用。// AngularJS 控制器管制 AngularJS 利用。// ng-app 指令定义该利用,ng-controller 定义该控制器。<div ng-app="myApp" ng-controller="myCtrl"> First Name: <input type="text" ng-model="firstName"><br> Last Name: <input type="text" ng-model="lastName"><br> <br> Full Name: {{firstName + " " + lastName}}</div><script> var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { $scope.firstName= "John"; $scope.lastName= "Doe"; });</script>// AngularJS 模块定义利用:var app = angular.module('myApp', []);// AngularJS 控制器管制利用:app.controller('myCtrl', function($scope) { $scope.firstName= "John"; $scope.lastName= "Doe";});起源 & 参考例子 ...

December 15, 2022 · 7 min · jiezi

关于angularjs:web-前端-AngularJS-四大特征

AngularJS 的设计思维与 jquery 齐全不同,前者操作的是变量 $scope $http,后者操作的 DOM。 MVC 模式Angular 遵循软件工程的 M(数据)V(视图)C(控制器)模式,并激励展示,数据,和逻辑组件之间的松耦合.通过依赖注入(dependency injection),Angular 为客户端的 Web 利用带来了传统服务端的服务,例如独立于视图的管制。 因而,后端缩小了许多累赘,产生了更轻的 Web 利用。 Model:数据,其实就是 angular 变量($scope.XX); View: 数据的出现,Html+Directive(指令); Controller:操作数据,就是 function,数据的增删改查; 双向绑定AngularJS 是建设在这样的信念上的:即申明式编程应该用于构建用户界面以及编写软件构建,而指令式编程非常适合来示意业务逻辑。框架采纳并扩大了传统 HTML,通过双向的数据绑定来适应动静内容,双向的数据绑定容许模型和视图之间的主动同步。因而,AngularJS 使得对 DOM 的操作不再重要并晋升了可测试性。这里是区别于 Jquery 的,jq 操作的是 dom 对象,angularJS 操作的是变量。 依赖注入依赖注入(Dependency Injection,简称 DI)是一种设计模式, 指某个对象依赖的其余对象无需手工创立,只须要“吼一嗓子”,则此对象在创立时,其依赖的对象由框架来主动创立并注入进来,其实就是起码常识法令;模块中所有的 service 和 provider 两类对象,都能够依据形参名称实现 DI.控制器就是通过依赖注入的形式实现对服务的调用 模块化设计高内聚低耦合法令 高内聚:每个模块的具体性能具体实现 低耦合:模块之间尽可能的少用关联和依赖 1)官网提供的模块 ng(最外围)、ngRoute(路由)、ngAnimate(动画) 2)用户自定义的模块 angular.module('模块名',[ ]) 想理解更多精彩内容,快来关注尚硅谷! 关键词:前端培训

April 1, 2022 · 1 min · jiezi

关于angularjs:AngularJS-四大特征

AngularJS 的设计思维与 jquery 齐全不同,前者操作的是变量 $scope $http,后者操作的 DOM。 MVC 模式Angular 遵循软件工程的 M(数据)V(视图)C(控制器)模式,并激励展示,数据,和逻辑组件之间的松耦合.通过依赖注入(dependency injection),Angular 为客户端的 Web 利用带来了传统服务端的服务,例如独立于视图的管制。 因而,后端缩小了许多累赘,产生了更轻的 Web 利用。 Model:数据,其实就是 angular 变量($scope.XX); View: 数据的出现,Html+Directive(指令); Controller:操作数据,就是 function,数据的增删改查; 双向绑定AngularJS 是建设在这样的信念上的:即申明式编程应该用于构建用户界面以及编写软件构建,而指令式编程非常适合来示意业务逻辑。框架采纳并扩大了传统 HTML,通过双向的数据绑定来适应动静内容,双向的数据绑定容许模型和视图之间的主动同步。因而,AngularJS 使得对 DOM 的操作不再重要并晋升了可测试性。这里是区别于 Jquery 的,jq 操作的是 dom 对象,angularJS 操作的是变量。 依赖注入依赖注入(Dependency Injection,简称 DI)是一种设计模式, 指某个对象依赖的其余对象无需手工创立,只须要“吼一嗓子”,则此对象在创立时,其依赖的对象由框架来主动创立并注入进来,其实就是起码常识法令;模块中所有的 service 和 provider 两类对象,都能够依据形参名称实现 DI.控制器就是通过依赖注入的形式实现对服务的调用 模块化设计高内聚低耦合法令 高内聚:每个模块的具体性能具体实现 低耦合:模块之间尽可能的少用关联和依赖 1)官网提供的模块 ng(最外围)、ngRoute(路由)、ngAnimate(动画) 2)用户自定义的模块 angular.module('模块名',[ ]) 关键词:前端培训

February 24, 2022 · 1 min · jiezi

关于angularjs:ApacheCN-Angular-译文集-20211114-更新

Angular 专家级编程 零、前言一、架构概述和在 Angular 中构建简略利用二、将 AngularJS 利用迁徙到 Angular 利用三、应用 Angular CLI 生成具备最佳实际的 Angular 利用四、应用组件五、实现 Angular 路由和导航六、创立指令和实现更改检测七、应用可察看对象的异步编程八、模板和数据绑定语法九、Angular 中的高级表单十、Angular 的材质设计十一、实现 Angular 管道十二、实现 Angular 服务十三、利用依赖注入十四、解决 Angular 动画十五、集成 Bootstrap 与 Angular 利用十六、应用 Jasmine 和 Protractor 框架测试 Angular 利用十七、Angular 设计模式Angular 2 组件 零、序言一、Angular 2 组件架构二、应用 Angular CLI 建设开发环境三、TypeScript 入门四、构建根本组件五、构建动静组件六、组件通信七、将所有内容放在一起八、集成第三方组件九、Angular 2 指令Angular 2 示例 零、前言一、开始二、构建咱们的第一个利用——7 分钟训练三、更多 Angular——SPA 和路由四、私人教练五、反对服务器数据长久化六、深度 Angular 指令七、测试私人教练八、一些理论场景Angular 测试驱动开发 零、序言一、测试驱动开发简介二、JavaScript 测试的细节三、Karma 之道四、Protractor 端到端测试五、深刻 Protractor六、第一步七、触发器八、通知世界PrimeNG:Angular UI 开发 零、前言一、开始应用 Angular 和 Priming二、主题化概念和布局三、加强输出和抉择四、按钮和面板组件五、数据迭代组件六、惊人的叠加和音讯七、无尽的菜单变动八、创立图表和地图九、杂项用例和最佳实际十、创立强壮的利用切换到 Angular 2 零、序言一、开始学习 Angular 2二、Angular 2 利用的构建块三、TypeScript 速成课四、Angular 2 组件和指令入门五、Angular 2 中的依赖注入六、应用 Angular 2 路由和表单七、解释管道和与 RESTful 服务的通信八、开发教训及服务器端渲染Angular 和 BootStrap Web 开发 ...

December 8, 2021 · 2 min · jiezi

关于angularjs:Angular-管道

问题在写流动治理详情的时候不晓得怎么用内置的日期管道,本人写了一个管道,同时在管道应用时遇到问题。查看后发现ActivityScalePipe在新建时申明在了Activity模块中,而我想在view模块中应用,就又将管道申明在了view模块中,这样就导致了下面的问题。最开始想到的解决办法时在view不必申明管道,在Activity中export ActivityScalePipe,而后在view模块中引入Activity模块,后果导致产生循环,因为在Activity模块中曾经引入了view模块。起初间接将管道申明在view模块中,而后再export管道,问题解决。但起初想到这样解决不太好,因为如果有其余模块须要用到这个管道,那么这个模块就须要引入整个view模块,显然这种写法并不合理,于是从新批改,将管道申明在pipe模块中,这样无论那个模块想应用管道都只须要引入pipe管道。起初查看其余管道也是应用这种写法。通过这个过程对组件与模块之间关系和模块之间的关系有了更深的了解。组件依赖于模块,存在于模块,组件若想成动运行,则必然是运行于某个模块之中。组件胜利运行的前提,是在模块中被胜利地实例化,模块可能胜利实例化某个组件的前提是模块领有组件想要的所有。留神:管道只能被申明在一个模块中 管道官网文档 简介管道简略来说能够了解为一个对数据转化的货色,数据在管道一端流入,通过管道转化后以另一种模式在另一端流出。管道应用 数据 | 管道名称如: 日期管道名称为‘data’ <p>The hero's birthday is {{ birthday | date }}</p>管道串联 The chained hero's birthday is{{ birthday | date | uppercase}}自定义管道要把类标记为管道并提供配置元数据,请把 @Pipe 装璜器利用到这个类上建设管道命令 ng g p 管道名称管道示例 import { Pipe, PipeTransform } from '@angular/core';@Pipe({name: 'exponentialStrength'})export class ExponentialStrengthPipe implements PipeTransform { transform(value: number, exponent = 1): string { if(valuev === 0){ return '男'; }else { return '女'; }}如果是原始类型的输出值,比方 String 或 Number ,或者是对象援用型的输出值,比方 Date 或 Array ,那么每当 Angular 检测到输出值或援用有变动时,就会执行该输出管道。同样,数组和对象中的值变动时不能检测到。解决办法能够应用新的数组或对象代替原来的对象或数组。 ...

November 20, 2021 · 2 min · jiezi

关于angularjs:Angular管道PIPE介绍

PIPE,翻译为管道。Angular 管道是编写能够在HTML组件中申明的显示值转换的办法。Angular 管道之前在 AngularJS 中被称为过滤器,从 Angular 2开始就被称为管道。管道将数据作为输出并将其转换为所需的输入。 Angular Pipes 将整数、字符串、数组和日期作为输出,用| 分隔,而后依据须要转换成格局,并在浏览器中显示进去。在插值表达式中,能够定义管道并依据状况应用它,在 Angular 应用程序中能够应用许多类型的管道。 内建管道String -> String UpperCasePipeLowerCasePipeTitleCasePipeNumber -> String DecimalPipePercentPipeCurrencyPipeObject -> String JsonPipeDatePipeTools SlicePipeAsyncPipeI18nPluralPipeI18nSelectPipe应用办法大写转换<div> <p ngNonBindable>{{ 'Angular' | uppercase }}</p> <p>{{ 'Angular' | uppercase }}</p> <!-- Output: ANGULAR --></div>日期格式化<div> <p ngNonBindable>{{ today | date: 'shortTime' }}</p> <p>{{ today | date: 'shortTime' }}</p> <!-- Output: 以以后工夫为准,输入格局:10:40 AM --></div>数值格式化<div> <p ngNonBindable>{{ 3.14159265 | number: '1.4-4' }}</p> <p>{{ 3.14159265 | number: '1.4-4' }}</p> <!-- Output: 3.1416 --></div>JavaScript 对象序列化<div> <p ngNonBindable>{{ { name: 'semlinker' } | json }}</p> <p>{{ { name: 'semlinker' } | json }}</p> <!-- Output: { "name": "semlinker" } --></div>管道参数管道能够接管任意数量的参数,应用形式是在管道名称前面增加 : 和参数值。如 number: '1.4-4' ,若须要传递多个参数则参数之间用冒号隔开,具体示例如下: ...

June 20, 2021 · 1 min · jiezi

关于angularjs:Angular模板简介

模板引擎是Web利用中用来生成动静HTML的一个路径, 负责将数据模型与HTML模板联合起来(即模板渲染),生成最终的HTML。 编写HTML模板的语法称为模板语法,模板语法的表达能力和可扩展性决定了模板引擎的易用性。本文将介绍在重构治理控制台中应用到的Angular的模板引擎ng-template。ng-template简介ng-template示意为Angular模板:这意味着此标记的内容将蕴含模板的一部分,而后能够与其余模板一起组合以造成最终的组件模板。 ng-template次要包含:ng-container、ngIf,ngFor、ngClass、ngStyle和ngSwitch。 ng-containerng-container是一个逻辑容器,是 Angular 结构型指令中的一种,用于蕴含管制外部元素的显示与否。 ng-container能够蕴含任何元素,包含文本,但自身不会生成元素标签,也不会影响页面款式和布局。蕴含的内容,如果不通过其余指令管制,会间接渲染到页面中。 根本语法<div> <ng-container> <p>This is paragraph 1.</p> <p>This is paragraph 2.</p> </ng-container></div>渲染后<div> <p>This is paragraph 1.</p> <p>This is paragraph 2.</p></div>ngIfngIf 用于依据表达式的值,在指定地位渲染 then 或 else 模板的内容。 * `then` 模板除非绑定到不同的值,否则默认是 ngIf 指令关联的内联模板。* `else` 模板除非绑定对应的值,否则默认是 null。简略模式<div *ngIf="!isLoggedIn"> Please login, friend.</div><!-- logic && operator --><div *ngIf="isLoggedIn && !isNewUser"> Welcome back, friend.</div><!-- logic OR operator --><div *ngIf="isLoggedIn || isNewUser"> Welcome!</div>应用else块<div *ngIf="isLoggedIn; else loggedOut"> Welcome back, friend.</div><ng-template #loggedOut> Please friend, login.</ng-template>应用then和else块<ng-container *ngIf="isLoggedIn; then loggedIn; else loggedOut"></ng-container><ng-template #loggedIn> <div> Welcome back, friend. </div></ng-template><ng-template #loggedOut> <div> Please friend, login. </div></ng-template>ngForngFor用于应用可迭代的每个项作为模板的上下文来反复模板的一种形式。提供了能够被局部变量别名的其余值: ...

June 20, 2021 · 1 min · jiezi

关于angularjs:浅谈Angular错误处理

错误处理是编写代码常常遇见的并且必须解决的需要,很多时候解决异样的逻辑是为了防止程序的解体,之前《浅谈前端异样监控平台实现计划》介绍过异样跟踪,本文将简略介绍Angular解决异样的形式。 什么是AngularAngualr 是一款来自谷歌的开源的 web 前端框架,诞生于 2009 年,由 Misko Hevery 等人创立,后为 Google 所收买。是一款优良的前端 JS 框架,曾经被用于 Google 的多款产品当中。 AngularJS 是基于申明式编程模式 是用户能够基于业务逻辑进行开发. 该框架基于HTML的内容填充并做了双向数据绑定从而实现了主动数据同步机制. 最初, AngularJS 强化的DOM操作加强了可测试性.try/catch最相熟的的形式,就是在代码中增加try/catch块,在try中产生谬误,就会被捕捉并且让脚本继续执行。然而,随着应用程序规模的扩充,这种形式将变得无奈治理。 ErrorHandlerAngular提供了一个默认的ErrorHandler,能够将谬误音讯打印到控制台,因而能够拦挡这个默认行为来增加自定义的解决逻辑,上面尝试编写错误处理类: import { ErrorHandler, Injectable } from "@angular/core";import { HttpErrorResponse } from "@angular/common/http";@Injectable()export class ErrorsHandler implements ErrorHandler { handleError(error: Error | HttpErrorResponse) { if (!navigator.onLine) { console.error("Browser Offline!"); } else { if (error instanceof HttpErrorResponse) { if (!navigator.onLine) { console.error("Browser Offline!"); } else { // Handle Http Error (4xx, 5xx, ect.) console.error("Http Error!"); } } else { // Handle Client Error (Angular Error, ReferenceError...) console.error("Client Error!"); } console.error(error); } }}通常在app下创立一个共享目录shared,并将此文件放在providers文件夹中当初,须要更改应用程序的默认行为,以应用咱们自定义的类而不是ErrorHandler。批改app.module.ts文件,从@angular/core导入ErrorHandler,并将providers增加到@NgModule模块,代码如下: ...

June 20, 2021 · 3 min · jiezi

关于angularjs:Angular10配置webpack打包-详细教程

对于 Angular 我的项目,举荐应用 angular-cli 创立打包我的项目 Angular 会默认帮咱们配置。 然而有非凡的需要时就显然不是很灵便,比方想宰割一些较大的打包文件、剖析每个打包文件组成,自定义webpack一些参数的时候就发现无从下手。 对许多我的项目的常见依赖项是日期库moment.js 。 这包含应用语言环境的性能,然而,它大大增加了整体捆绑软件的大小。这些都是须要咱们优化的中央。 一、ngx-build-plus 建设额定配置 这里举荐一个工具库ngx-build-plus,不须要改很多货色就能在现有我的项目进行集成。接下来教大家如何应用,具体详情能够去github上找文档。尽管官网文档上只标注到了可用版本为9,然而Angular10也是能够应用的。 应用CLI创立一个新的Angular我的项目从零搭建Angular10我的项目 先决条件在开始之前,请确保你的开发环境曾经蕴含了 Node.js® 和 npm 包管理器。 Node.jsAngular 须要 Node.js 的 8.x 或 10.x 版本。 要想查看你的版本,请在终端/控制台窗口中运行 node -v 命令。2. 增加ngx-build-plus: ng add ngx-build-plusnpm 包管理器Angular、Angular CLI 和 Angular 利用都依赖于某些库所提供的个性和性能,它们都是 npm 包。要下载和装置 npm 包,你必须领有一个 npm 包管理器。 本 “疾速上手” 中应用的是 yarn 客户端命令行界面,治理依赖包 要想查看你是否曾经装置了 yarn 客户端,请在终端/控制台窗口中运行 yarn -v 命令。 第一步:装置 Angular CLI你要应用 Angular CLI 来创立我的项目、创立利用和库代码,并执行多种开发工作,比方测试、打包和公布。 全局装置 Angular CLI。 要想应用 npm 来装置 CLI,请关上终端/控制台窗口,并输出下列命令: ...

January 21, 2021 · 10 min · jiezi

关于angularjs:夏哥事件簿-angularJS-1X-ngmodal-不能输入时间问题解决方案

解决 angularJS ng-modal 和 input type datetime 的问题(输出日期和工夫) JS .directive('formatDate', function ($filter) { return { require: 'ngModel', link: function (scope, elem, attr, ngModelCtrl) { ngModelCtrl.$formatters.push(function (modelValue) { if (modelValue) { return new Date(modelValue); } }); ngModelCtrl.$parsers.push(function (value) { if (value) { return $filter('date')(new Date(value), attr.timetype); } }); } };}); HTML <input type="datetime-local" ng-model="PM.stareTime" format-date timetype="yyyy-MM-dd hh:mm">timetype 须要的写上工夫格局

December 3, 2020 · 1 min · jiezi

关于angularjs:Angluar5ionic3实践四

背景 : 复宏汉霖我的项目的CR做完了.用时五天.超出预计工夫十天.这五天每天都在加班.终于功夫不负有心人.我当初能够开始单独开发Angluar+ionic我的项目了.之前都是用的React+AntDesign.当初算是扩大了新框架.播种满满.这里最初记录下.这两天开发过程中遇到的问题.学到的常识. 学到的常识:肯定会遇到的model弹层比方这样的需要: 进我的项目的时候抉择岗位,在我的页面又要有切换岗位的弹框.这时候就会遇到两个点.一个是model弹层.还有一个就是抽组件. 首先来看下抽组件顾名思义就是把多个用途的同一块货色抽成公共组件.这样在任何页面就能够专用.这也是我的项目必备技能.个别都把抽出来的组件放到src/components外面.来看下我的这个model弹层的公共组件怎么写的:目录构造: 代码内容:choose-job.module.ts 内容:import { NgModule } from '@angular/core';import { IonicPageModule } from 'ionic-angular';import { ChooseJobPage } from './choose-job';@NgModule({ declarations: [ChooseJobPage], imports: [ IonicPageModule.forChild(ChooseJobPage), ],})export class ChooseJobPageModule {}choose-job.ts 内容:import { Component, Input } from '@angular/core';import { NavParams,IonicPage, ViewController } from "ionic-angular";@IonicPage()@Component({ selector: 'choose-job', templateUrl: 'choose-job.html',})export class ChooseJobPage { @Input() jobList: any; // 岗位列表 checkd = { TerritoryID:'' } // 选中的岗位 // 初始化 constructor( public navParams: NavParams, public viewCtrl: ViewController,) { this.jobList = this.navParams.get('jobList'); this.checkd.TerritoryID = this.navParams.get('territoryID') || ''; } // 选中的值 radioSelected =(v)=>{ this.checkd = v } // 点击确认 determine = ()=>{ this.viewCtrl.dismiss({selectedItem: this.checkd, action: 'save'}); }}choose-job.html 内容:<ion-header class="modal-header"> <ion-navbar color="sky" padding-left padding-right> <ion-title text-center="">抉择岗位</ion-title> </ion-navbar></ion-header><ion-content style="height: 300px;"> <ion-item-group class="list"> <ion-item *ngFor="let item of jobList;let i = index" > <ion-label> <ion-row> <h3>{{item.TerritoryName}}</h3> <span class="tr">({{item.isPartTime ? "代岗" : "主岗" }})</span> </ion-row> </ion-label> <ion-radio (click)="radioSelected(item)" [checked]="checkd.TerritoryID === item.TerritoryID" ></ion-radio> </ion-item> <ion-row justify-content-center align-items-center *ngIf="jobList?.length === 0"> <img src="assets/icon/no_data.png" alt="" width="70" style="margin-top: 150px"/> </ion-row> </ion-item-group> <button ion-button class="save" (click)="determine()"> 确定 </button></ion-content>choose-job.scss 内容:approve-list { .list{ max-height: 200px; overflow-x: hidden; } .tr{ color: #32a3e7; font-size: 12px; margin-left: 5px; } .save{ width: 87%; background: #32a3e7; margin: 20px; position: absolute; bottom: 0; }}内容解析:这个公共组件须要的参数只有两个.一个jobList还有一个territoryID.(刚进页面的时候是没有默认值的,在我的外面切换岗位的时候,是须要默认为本人登陆的岗位)须要留神的中央:(1): 调用组件是须要在组件页的类上方定义@IonicPage()的.不然获取不到的.(2): 不加@IonicPage()的组件是被用作标签应用.不须要choose-job.module.ts.在components的公共components.module.ts外面导出就好了. ...

September 18, 2020 · 2 min · jiezi

关于angularjs:Angluar5ionic3实践三

背景: 这几天在做复宏汉霖的报表.用到了echarts.git到了一个新性能.原来没有用过的.哈哈哈.三张报表.动态页面画的画了两天.明天来整顿下学到的知识点. 意识下echartsecharts官网地址外面有许许多多的图例.目前我须要用到的是饼图Pie和柱状图Bar. 实际饼图Pie先看下实现成果:由上图可看出理论覆盖率用的是饼图.这里用到的就是echarts的 Pie. 实现逻辑:html 页面代码: <ion-row justify-content-between > <div no-margin class="font-12 echarts-title" style='border-bottom: none !important;'> <span style='margin:0px 0px 10px 0px;color: #14a3d9;display: block;'>理论笼罩客户数</span> <div style="position: relative; margin-left: 10%;"> <img src="assets/icon/actual.png" alt="" style="width: 80px;margin-left: 40%;"/> <p class='number'>10000人</p> </div> </div> <div no-margin class="font-12 echarts-title" style='border-bottom: none !important;'> <span style='margin:0px 0px 0px -40px;color: #d98a14;display: block;'>理论覆盖率</span> <div #chart1 style='width:100px;height:100px;' ></div> </div> </ion-row>ts 页面代码:export class VisitReportPageNew { @ViewChild("chart1") chart1: ElementRef; ionViewWillEnter() { this.setChart1(); } setChart1() { const ec = echarts as any; const container = this.chart1.nativeElement; const chart = ec.init(container); chart.setOption({ title: { show: true, x: "center", y: "center", text: 85 + "%", itemGap: 0, // 主副标题间距 textStyle: { fontSize: "13", color: "#d98a14", }, subtext: "", subtextStyle: { fontSize: "13", color: "#5B697A", fontWeight: "600", }, }, color: ["#d98a14", "#f1dbbb"], series: { name: "", type: "pie", radius: ["72%", "82%"], avoidLabelOverlap: true, hoverAnimation: false, labelLine: { normal: { show: false, }, }, data: [ { value: 85, name: "" }, { value: 15, name: "" }, ], }, }); }}还有css和module.ts页面的代码关联不大我就不放了.这里次要记住的一些基本参数.series.type : 定义图形形态series.radius : 图形大小,第一个参数是外圈,第二个参数是内圈color : 图形色彩,第一个参数是散布圈次要色彩,第二个参数是残余内容色彩title.text : 图形两头的文字title.textStyle : 图形两头的文字的款式series.data : 占比值实际饼图稍简单版Pie先看下实现成果:上图中的访问次数散布by访问目标的图就是稍简单版的饼图.来看下代码: ...

September 15, 2020 · 3 min · jiezi

Angular8集成Editormd的Markdown编辑器并解析markdown

Angular8集成Editor.md编辑器就不必多说了。这里主要介绍集成editormd后通过他自带的解析markdown方法,解析markdown生成html文章地址:http://www.bowen-tech.top/art...

September 8, 2019 · 1 min · jiezi

基于-Angular-开发的-免费在线纪念日计算器恋爱纪念日计算器结婚纪念日计算器

免费纪念日计算器、恋爱计算器、结婚纪念日计算器,在线纪念日计算器、恋爱计算器、结婚纪念日计算器使用指南只需要选择一个你不能够忘记的日子,点击计算,下面就会显示多少天纪念日以及多少周年纪念日等各种纪念日的具体日期和距离当前的天数。目前支持的提醒纪念日有(从你输入的日期开始的52天、66天、77天、88天、99天、100天、200天、222天、300天、400天、500天、520天、600天、666天、700天、777天、800天、888天、900天、999天、1000天、1314天、5200天、6666天、8888天、9999天、10000天,周年纪念为1-50周年)直达地址:传送门截图欣赏

September 8, 2019 · 1 min · jiezi

实例解说AngularJS在自动化测试中的应用

7月25日晚8点,线上直播,【AI中台——智能聊天机器人平台】,点击了解详情。 一、什么是AngularJS ?1、AngularJS是一组用来开发web页面的框架、模板以及数据绑定和丰富UI的组件; 2、AngularJS提供了一系列健壮的功能,以及将代码隔离成模块的方法; 3、AngularJS于2009年发布第一个版本,由Google进行维护,压缩版94k。 二、AngularJS的核心思想 1、在AngularJS中通过数据视图双向绑定实现视图与业务逻辑解耦,这将提高代码的可测试性; 2、遵循MVC模式开发,鼓励视图、数据、逻辑组件间松耦合; 3、将测试与应用程序编写放在同等重要的位置,在编写模块的同时编写测试。因为各组件的松耦合,使得这种测试得以实现; 4、 应用程序页面端与服务器端解耦。两方只需定义好通信API,即可并行开发。 三、简单的栗子问题:假设我们需要编写一个手机列表,支持对手机信息进行模糊搜索,且按指定字段排序,要怎么实现呢? 如上图所示,几乎没有DOM操作,更专注于业务逻辑! 下面编写HTML 编写控制器Controller PhoneListCtrl 控制器。例子中注入了$scope(数据模型)、$http(封装了ajax的服务)这两个服务都是angularjs内置服务,服务是可以自定义的。 $scope.phones = data; 在这个地方后台返回的数据应用到了数据模型中,这时前台UI会自动响应更新。 四、指令4.1 什么是指令?指令是AngularJS用来扩展浏览器能力的技术之一。在DOM编译期间,和HTML关联着的指令会被检测到,并且被执行。这使得指令可以为DOM指定行为或者改变DOM的结构。例如ng-controller、ng-src、ng-model等。 4.2 AngularJS的编译 4.3 简单的AngularJS指令写法自定义指令的一般格式: angular.application(‘myApp’, []).directive(‘myDirective’,function(){//一个指令定义对象return{ }; //通过设置项来定义指令,在这里进行覆写});下面我们来看一个简单的自定义指令的例子: module:这个方法将新建一个模块。AngularJS以模块管理代码。directive:在模块中新建指令,指定的方法在编译步骤会被执行,执行后返回一个自定义的链接函数,这个链接函数在完成双向绑定后执行。Restrict:它告诉AngularJS这个指令在DOM中可以何种形式被声明。E(元素), A(属性,默认值), C(类名)。scope :可以被设置为true或一个对象。默认值是false。当scope设置为true时,会从父作用域继承并创建一个新的作用域对象。有三种绑定策略@ = &。Template:一段HTML文本,或一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符串。4.4 使用指令 ng-app="MyModule":在angularjs启动时指定初始化的模块(module)。当前指定的是自定义的模块。drink water="{{pureWater}}":调用自定义的drink指令,将$scope中的pureWater属性赋值给指令中的water属性。drink可以是一个属性,也可以是一个标签。五、模块和服务在AngularJS中,模块负责组织、启动、实例化应用。 模块的两个部分,一个是配置块,另一个是运行块。 配置块:在实例工厂(provider)注册和配置阶段运行。只有工厂、常量才可以注入到配置块中(常量的配置要放在前面); 运行块:注入器(injector)被创建后执行,被用来启动应用。实例和常量、变量等都能被注入。 AngularJS应用中的服务是一些用依赖注入捆绑在一起的、可替换的对象。这些对象可以提供一些封装好的逻辑操作,以供调用。 AngularJS内置了很多有用的服务,例如前面提到的$timeout、$http等,我们可以通过使用内置服务完成大部分业务逻辑。但很多时候我们还需要自定义服务: 服务的使用 上图的代码中定义了一个服务notify,它依赖另外一个服务$window。$window中封装了window对象的方法,定义了一个控制器myController,并为这个控制器注入了notify服务,同时在控制器的scope中定义了一个方法callNotify来调用服务。$inject是依赖注入的一种方式,请参看下文依赖注入章节。 六、依赖注入我们可以将需要的服务比作一件工具,比如一把锤子,那我们要怎么获得锤子呢? 第一种方法:自己打造一把锤子。如果锤子的工艺改变了,我们就需要重新制造。相当于我们在程序中new了一个服务,服务的实现改变时,只能修改代码,这将产生风险。 第二种方法:我们找到一间工厂,告诉工厂锤子的型号,然后工厂为我们制造。这时候就不需要关系锤子是怎么做的,我们只管使用。但是这种方式还是很麻烦,我们需要知道工厂在哪。类似于在代码中通过工厂方法获取我们想要的服务。这种方会对工厂产生依赖。 第三种方法:我们在门前贴张单子,声明我们需要一把什么型号的锤子,第二天就有人默默地送来了一把锤子。这在现实生活中简直是痴心妄想,但这种方式确实很轻松,不需要考虑任何东西,我们只关注使用锤子。这就是程序里的依赖注入。只要声明了需要什么,在使用的时候就可以得到什么。 6.1 AngularJS中的依赖注入第一种方式:通过方法参数名声明依赖。这种方式不推荐使用,因为js文件压缩后方法参数名会改变。 第二种方式:声明一个数组,依赖列表放数组的前部,注入目标放数组最后一个元素。推荐使用这个方法。 第三种方式:通过$inject属性来声明依赖列表。 ...

July 16, 2019 · 1 min · jiezi

SPA项目开箱即用的SEO解决方案

前言本文章写于 2019-07-05 请注意时效性。 有关 SPA 项目的 SEO 友好的解决方案其实不多, 常见的解决手段如下: 将 SPA 项目改为 SSR 渲染使用预渲染前者非常稳定但是对于已有的 SPA 项目进行改造需要注意的问题有很多而且耗时长与重写一个没有太大区别,后者只能对于那些"无论是哪种用户访问返回的结果都一样"的页面合适不免十分被动。 总的来说都是十分的繁琐,不过依然有可以避开修改原有代码的解决方案, 例如下面的这个: https://www.cnblogs.com/lipte...这些方案的基本原理就是,使用代理服务器区分搜索引擎的爬虫和普通用户从而实现针对性内容响应,普通用户响应原有的 SPA 项目也就是“纯粹的 index.html 页面”,而对于爬虫响应对应路由下的渲染好的HTML页面。既然各位智慧无穷的网友开出了药方,看来我们需要手动熬制了。不过先慢着看看 github 有什么现成的药没有:没错 github 上已经有了现成的解决方案。 Rendora 简介Rendora 本身是一个代理服务器使用 GO 语言编写专门被设计与解决 SPA 项目的 SEO 处理,支持配置文件以及对外接口。 使用 Rendora 可以相较于其他方案有如下的优势: 无需修改原有项目无需修改构建配置支持任意路由页面的渲染不受限于前端框架与所使用到的技术搜索引擎爬虫和普通用户获取到的数据一致它的基本原理就是请求经过 Rendora 的时候它会根据请求头 user-agent 来判断请求是属于爬虫还是普通用户, 普通用户直接代理到原有的Web服务器, 而爬虫的请求会经过无头浏览器(head-less browser) 处理生成一张页面返回给爬虫,而这个页面的内容可以理解为是运行时的 DOM 快照。 明白了基本原理后我们不难想到只要是异步加载数据然后再利用数据渲染内容的页面都适用。而且爬虫和普通用户两者最终获取到的数据可以高度一致。 安装Rendora 官方文档中已经给出了安装方式,我就在这里直接照搬了,不过 Rendora 本身是由 GO 语言编写,而且依赖了无头浏览器还是有许多小坑要踩的。 在本文中我使用的系统是 Ubuntu18.04桌面版 ,但是其他的系统用户 windows 和 macos 都是可以安装以及使用的,安装 Rendora 方式稍有不同但是基本概念都是一致的。 ...

July 9, 2019 · 2 min · jiezi

前后端分离mock-server方案-Moco

虽然前后端分离已经流行很多年了,仍有很多团队不能够充分的利用前后端分离的优势。主要体现在前端过分依赖服务环境, 将高效的约定分工合作模式理解很浅。 在这里推荐一种mock server的解决方案。 mock server的方案有很多: 1. Java API 2. JUnit @Rule 3. Node.js (npm) module 4. Grunt plugin 5. Docker container等这里推荐较为灵活简单的解决方案【Moco】 源项目github地址: https://github.com/dreamhead/... 搭建步骤 1 下载Moco Download Standalone Moco Runnerhttp://central.maven.org/mave...2 启动Java Moco进程 java -jar moco-runner-<version>-standalone.jar http -p 12306 -g main.json3 配置main.json 最佳实践: 配置main.json 引入其他的json [ { "include" : "foo.json" }, { "include" : "mock.json" } ]4 编写配置文件: 1 mock.json [ { "request": { "uri": "/mock" }, "response": { "text": "Hello, MOTO" } } ] 2 foo.json [ { "request": { "uri": "/foo", "queries": { "f": { "match": ".*" // anyNumber anyString } } }, "response": { "json": { // 此处为json body } } } ] 5 修改的配置文件及时生效 ...

June 27, 2019 · 1 min · jiezi

整理了一下angularJs的webpack模板

github地址:https://github.com/qianxiaoni... 欢迎大家star或者fork呀~目录结构src/ components/ 组件 config/ dict.js 一个全局变量的run方法 router.js 路由表 validation.js 'angular-validation'的config配置 controllers/ 控制器 data/ 数据mock文件 directives/ 指令 filters/ 过滤器 image/ 图片 pages/ 页面 services/ 服务(公共方法) app.js 入口函数 common.less 公共样式文件 index.html html模板文件

June 23, 2019 · 1 min · jiezi

Fundebug前端JavaScript插件更新至182修复2个小BUG

摘要: 修复2个BUG,请大家及时更新。 Fundebug前端异常监控服务Fundebug是专业的程序异常监控平台,我们JavaScript插件可以提供全方位的异常监控,可以帮助开发者第一时间定位各种前端异常,包括: JavaScript执行错误资源加载错误HTTP请求错误unhandledrejectionWebSockect连接错误并且,我们可以记录用户行为、“录制”用户操作视频,帮助开发者快速复现BUG,提高Debug效率。 Fundebug前端异常监控插件更新至1.8.2,修复了2个小BUG: 修复用户行为中重复记录HTTP请求的BUG修复Websocket的onerror为undefined报错的BUG这2个BUG都不会影响Fundebug功能,不过为了避免造成困扰,请大家及时更新插件。 修复用户行为中重复记录HTTP请求的BUG根据用户反馈,Fundebug插件有时会在用户行为中某些HTTP请求: 这个问题的根本原因应该是浏览器的BUG导致的,我们通过对插件代码进行修改规避了这个问题。 这个BUG不会影响Fundebug的功能,不过为了避免造成困扰,请大家及时更新插件。 修复Websocket的onerror为undefined报错的BUG根据用户反馈,当我们将Websocket的onerror设为undefined时,会导致Fundebug插件报错: var ws = new WebSocket("wss://ap.fundebug.com/api/events/count");ws.onerror = undefined;报错信息为:”TypeError: null is not an object (evaluating 'n.apply')“。我们优化了监控WebSocket连接错误的代码,可以避免这个报错。 这个BUG不会影响Fundebug的功能,不过为了避免造成困扰,请大家及时更新插件。 最后,感谢Fundebug用户暗元素的反馈与协助。 参考Fundebug前端JavaScript插件更新至1.2.0,支持监控WebSocket连接错误没有Fundebug不能复现的BUGFundebug录屏插件更新至0.4.0,修复BUG,优化性能Fundebug文档 - JavaScript插件版本关于FundebugFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多知名企业。欢迎大家免费试用! 版权声明转载时请注明作者Fundebug以及本文地址:https://blog.fundebug.com/2019/06/18/fundebug-javascript-1-8-2/

June 18, 2019 · 1 min · jiezi

萌新与大型angularjs项目 三周目——程序猿与改需求

概述本周学习内容不多,就简单介绍一下我在公司的日常吧1.测试2.改需求3.再改总之就是先让我去测试一下bug,然后再给我定下一个需求,让我试着去改,改完之后再挑挑毛病,然后再改。。。改需求果然是杀死程序员的最简单方法目前给我的任务还好,只是对这一个界面改改样式,但是领导跟我讲的未来前景让我有想死的冲动。目标:使用自定义的css样式,不再使用原先的css模板,最终达到自定义所有样式的目标。我只想说,前景很远大,愿望很美好,我听了想死。。总结最近这两周由于身体原因,没有认真学习,很惭愧,下周我会调整过来,努力补回学习进度。

April 20, 2019 · 1 min · jiezi

Angular项目中使用Swiper和Ueditor

这一阵子开始学习angular,张dalao给我一个阉割了很多功能的初级后台项目作为练手,虽然初级,但是有些东西对于初学者来说真的很难,就比如说现在就要讲的引入Swiper和Ueditor这两个插件。难点在于这两个东西都挺老的了,Swiper还好,而Ueditor已经很久没有更新了,在网上查了很久,磕磕绊绊,终于顺利的把这两个东西引入到了项目里。废话不多说,上图上代码先讲Ueditor吧,下图是引入以后的Ueditor富文本编辑器这个是Ueditor的GitHub地址按照GitHub的教程来先把文件download下来然后在项目里安装npm install ngx-ueditor –save然后在AppModel.ts里面引入下面的代码import { BrowserModule } from ‘@angular/platform-browser’;import { FormsModule } from ‘@angular/forms’;import { UEditorModule } from ’ngx-ueditor’;@NgModule({ imports: [ BrowserModule, FormsModule, UEditorModule.forRoot({ js: [ ./assets/ueditor/ueditor.config.js, ./assets/ueditor/ueditor.all.min.js, ], // 默认前端配置项 options: { UEDITOR_HOME_URL: ‘./assets/ueditor/’ } }) ], declarations: [AppComponent], bootstrap: [AppComponent]})export class AppModule { }可以看到js:[]里面引入了两个js文件,这两个文件就在刚刚下下来的压缩包里路径有点复杂ngx-ueditor-master\src\assets把这个ueditor文件夹整个解压到项目里的assets目录下,这样准备工作就做完了接下来就是在组件里面用了下面的是html代码<ueditor [(ngModel)]=“html” //双向绑定编辑器的内容[config]=“editorConfig” //配置[loadingTip]="‘加载中……’" //加载时的文字(onReady)="_ready($event)" //加载完成时的回调(onDestroy)="_destroy()" //销毁时的回调(ngModelChange)="_change($event)" //内容改变时的回调></ueditor>接下来是ts代码 html = ``; //编辑器内容 editorConfig={ wordCount: true, // 文字计数 initialFrameHeight: 300, // 设置高度 initialFrameWidth: ‘100%’, // 设置宽度 } _ready($event){ } _destroy(){ } _change($event){ }具体的API可以在文档里面的查到,就不多说这样就可以在Angular里面使用Ueditor了下班咯,明天再更新一下Swiper的使用 ...

April 12, 2019 · 1 min · jiezi

angular2 NgModel` 模块

angular2 NgModel 模块在Angular2中一个Module指的是使用@NgModule修饰的class。@NgModule利用一个元数据对象来告诉Angular如何去编译和运行代码。一个模块内部可以包含组件、指令、管道,并且可以将它们的访问权限声明为公有,以使外部模块的组件可以访问和使用到它们。我们也可以通过定义子模块来扩展我们应用的功能。NgModule 的APIinterface NgModule { // providers: 这个选项是一个数组,需要我们列出我们这个模块的一些需要共用的服务 // 然后我们就可以在这个模块的各个组件中通过依赖注入使用了. providers : Provider[] // declarations: 数组类型的选项, 用来声明属于这个模块的指令,管道等等. // 然后我们就可以在这个模块中使用它们了. declarations : Array<Type<any>|any[]> // imports: 数组类型的选项,我们的模块需要依赖的一些其他的模块,这样做的目的使我们这个模块 // 可以直接使用别的模块提供的一些指令,组件等等. imports : Array<Type<any>|ModuleWithProviders|any[]> // exports: 数组类型的选项,我们这个模块需要导出的一些组件,指令,模块等; // 如果别的模块导入了我们这个模块, // 那么别的模块就可以直接使用我们在这里导出的组件,指令模块等. exports : Array<Type<any>|any[]> // entryComponents: 数组类型的选项,指定一系列的组件,这些组件将会在这个模块定义的时候进行编译 // Angular会为每一个组件创建一个ComponentFactory然后把它存储在ComponentFactoryResolver entryComponents : Array<Type<any>|any[]> // bootstrap: 数组类型选项, 指定了这个模块启动的时候应该启动的组件.当然这些组件会被自动的加入到entryComponents中去 bootstrap : Array<Type<any>|any[]> // schemas: 不属于Angular的组件或者指令的元素或者属性都需要在这里进行声明. schemas : Array<SchemaMetadata|any[]> // id: 字符串类型的选项,模块的隐藏ID,它可以是一个名字或者一个路径;用来在getModuleFactory区别模块,如果这个属性是undefined // 那么这个模块将不会被注册. id : string }常用API简介NgModule的主要属性如下:declarations:模块内部Components/Directives/Pipes的列表,声明一下这个模块内部成员,声明之后才能使用对应的组件等。providers:指定应用程序的根级别需要使用的service。(Angular2中没有模块级别的service,所有在NgModule中声明的Provider都是注册在根级别的Dependency Injector中)imports:导入其他module,其它module暴露的出的Components、Directives、Pipes等可以在本module的组件中被使用。比如导入CommonModule后就可以使用NgIf、NgFor等指令。exports:用来控制将哪些内部成员暴露给外部使用。导入一个module并不意味着会自动导入这个module内部导入的module所暴露出的公共成员。除非导入的这个module把它内部导入的module写到exports中。bootstrap:通常是app启动的根组件,一般只有一个component。bootstrap中的组件会自动被放入到entryComponents中。entryCompoenents: 不会再模板中被引用到的组件。这个属性一般情况下只有ng自己使用,一般是bootstrap组件或者路由组件,ng会自动把bootstrap、路由组件放入其中。 除非不通过路由动态将component加入到dom中,否则不会用到这个属性。子模块 随着程序的壮大,单一的根模块已不能清晰的划分职责,这时候便可以引入Feature Module。Feature Module与根模块的创建方式一样,所有的模块共享一个运行期上下文和依赖注入器。 功能模块与根模块的职责区别主要有以下两点:根模块的目的在于启动app,功能模块的目的在于扩展app功能模块可以根据需要暴露或隐藏具体的实现Angular2提供的另一个与模块有关的技术就是延迟加载了。默认情况下Angular2将所有的代码打包成一个文件,目的是为了提高应用的流畅性,但是如果是运行在mobile中的app,加载一个大文件可能会过慢,所以rc5提供了一种延迟加载方式。import { ModuleWithProviders } from ‘@angular/core’;import { Routes, RouterModule } from ‘@angular/router’;export const routes: Routes = [ { path: ‘’, redirectTo: ‘contact’, pathMatch: ‘full’}, { path: ‘crisis’, loadChildren: ‘app/crisis/crisis.module#CrisisModule’ }, // 可以延迟加载子模块,子模块的结构和父模块一样,它会去加载子模块中的Routes配置,并跳转对应的组件中去。 { path: ‘heroes’, loadChildren: ‘app/hero/hero.module#HeroModule’ }];export const routing: ModuleWithProviders = RouterModule.forRoot(routes);其中,path指明路径,loadChildren指明使用延迟加载,‘app/crisis/crisis.module#CrisisModule’指明了模块的路径,和模块的名称。 ...

April 10, 2019 · 1 min · jiezi

表格组件 GridManager Angular 1.x

GridManager Angular 1.x基于 Angular 1.x 的 GridManager 封装, 用于便捷的在 Angular 中使用GridManager.列表项目实现功能宽度调整: 表格的列宽度可进行拖拽式调整位置更换: 表格的列位置进行拖拽式调整配置列: 可通过配置对列进行显示隐藏转换表头吸顶: 在表存在可视区域的情况下,表头将一直存在于顶部排序: 表格单项排序或组合排序分页: 表格ajax分页,包含选择每页显示总条数和跳转至指定页功能用户偏好记忆: 记住用户行为,含用户调整的列宽、列顺序、列可视状态及每页显示条数序号: 自动生成序号列全选: 自动生成全选列导出: 当前页数据下载,和仅针对已选中的表格下载右键菜单: 常用功能在菜单中可进行快捷操作过滤: 通过对列进行过滤达到快速搜索效果API该文档为原生GridManager的文档,angular-1.x版本除了在columnData.text columnData.template topFullColumn.template中可以使用angular模版外,其它使用方式相同。APIDemo该示例为原生GridManager的示例,angular-1.x版本除了在columnData.text columnData.template topFullColumn.template中可以使用angular模版外,其它使用方式相同。简单的示例复杂的示例Core codeGridManagerjToolENVES2015 + webpack + angular 1.x + GridManager安装npm install gridmanager-angular.1.x –save项目中引用es2015引入方式import gridManager from ‘gridmanager-angular-1.x’;通过script标签引入<link rel=“stylesheet” href="../node_modules/gridmanager-angular-1.x/css/gm-angular.css"><script src="../node_modules/gridmanager-angular-1.x/js/gm-angular.js"></script>示例<html> <head> <link rel=“stylesheet” href=“https://unpkg.com/gridmanager-angular-1.x/css/gm-angular.css"> <script src=“https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script> <script src=“https://unpkg.com/gridmanager-angular-1.x/js/gm-angular.js"></script> </head> <body ng-app=“myApp” ng-controller=“AppController as vm”> <grid-manager option=“option” callback=“callback(query)"></grid-manager> </body></html>function AppController($window, $rootScope, $scope, $element, $gridManager){ $scope.testClick = (row) => { console.log(‘click’, row); }; // 常量: 搜索条件 $scope.TYPE_MAP = { ‘1’: ‘HTML/CSS’, ‘2’: ’nodeJS’, ‘3’: ‘javaScript’, ‘4’: ‘前端鸡汤’, ‘5’: ‘PM Coffee’, ‘6’: ‘前端框架’, ‘7’: ‘前端相关’ }; $scope.searchForm = { title: ‘’, info: ’’ }; /** * 搜索事件 / $scope.onSearch = () => { console.log(‘onSearch’); $gridManager.setQuery(’testAngular’, $scope.searchForm); }; $scope.onReset = () => { $scope.searchForm = { title: ‘’, info: ’’ }; }; // 表格渲染回调函数 // query为gmOptions中配置的query $scope.callback = function(query) { console.log(‘callback => ‘, query); }; $scope.option = { gridManagerName: ’testAngular’, width: ‘100%’, height: ‘100%’, supportAjaxPage:true, isCombSorting: true, disableCache: false, ajax_data: function () { return ‘https://www.lovejavascript.com/blogManager/getBlogList'; }, ajax_type: ‘POST’, columnData: [ { key: ‘pic’, remind: ’the pic’, width: ‘110px’, align: ‘center’, text: ‘缩略图’, // ng template template: &lt;a target="_blank" style="display:block; height:58.5px;" ng-href="https://www.lovejavascript.com/#!zone/blog/content.html?id={{row.id}}" title="点击阅读[{{row.title}}]"&gt; &lt;img style="width:90px;margin:0 auto;" ng-src="https://www.lovejavascript.com/{{row.pic}}" alt="{{row.title}}"&gt; &lt;/a&gt; },{ key: ’title’, remind: ’the title’, align: ’left’, text: ‘标题’, sorting: ‘’, // 使用函数返回 ng template template: function() { return ‘<a class=“plugin-action” target="_blank” ng-href=“https://www.lovejavascript.com/#!zone/blog/content.html?id={{row.id}}” title=“点击阅读[{{row.title}}]">{{row.title}}</a>’; } },{ key: ’type’, remind: ’the type’, text: ‘博文分类’, align: ‘center’, width: ‘150px’, sorting: ‘’, // 表头筛选条件, 该值由用户操作后会将选中的值以{key: value}的形式覆盖至query参数内。非必设项 filter: { // 筛选条件列表, 数组对象。格式: [{value: ‘1’, text: ‘HTML/CSS’}],在使用filter时该参数为必设项。 option: [ {value: ‘1’, text: ‘HTML/CSS’}, {value: ‘2’, text: ’nodeJS’}, {value: ‘3’, text: ‘javaScript’}, {value: ‘4’, text: ‘前端鸡汤’}, {value: ‘5’, text: ‘PM Coffee’}, {value: ‘6’, text: ‘前端框架’}, {value: ‘7’, text: ‘前端相关’} ], // 筛选选中项,字符串, 默认为’’。 非必设项,选中的过滤条件将会覆盖query selected: ‘3’, // 否为多选, 布尔值, 默认为false。非必设项 isMultiple: true }, // isShow: false, template: &lt;button type="button" ng-click="testClick(row)" ng-bind="TYPE_MAP[row.type]"&gt;&lt;/button&gt; },{ key: ‘info’, remind: ’the info’, width: ‘300px’, text: ‘简介’ },{ key: ‘username’, remind: ’the username’, align: ‘center’, width: ‘100px’, text: ‘作者’, // 使用函数返回 dom string template: &lt;a class="plugin-action" href="https://github.com/baukh789" target="_blank" title="去看看{{row.username}}的github"&gt;{{row.username}}&lt;/a&gt; },{ key: ‘createDate’, width: ‘130px’, text: ‘创建时间’, sorting: ‘DESC’, // 使用函数返回 htmlString template: function(createDate, rowObject){ return new Date(createDate).toLocaleDateString(); } },{ key: ’lastDate’, width: ‘130px’, text: ‘最后修改时间’, sorting: ‘’, // 使用函数返回 htmlString template: function(lastDate, rowObject){ return new Date(lastDate).toLocaleDateString(); } },{ key: ‘action’, remind: ’the action’, width: ‘100px’, align: ‘center’, text: ‘<span style=“color: red”>操作</span>’, // 直接返回 htmlString template: ‘<span class=“plugin-action” ng-click=“delectRowData(row, index)">删除</span>’ } ] }; /* * 模拟删除 * @param row * @param index */ $scope.delectRowData = function(row, index) { if(window.confirm(确认要删除当前页第[${index}]条的['${row.title}]?)){ console.log(’—-删除操作开始—-’); $gridManager.refreshGrid(’testAngular’); // $element[0].querySelector(’table[grid-manager=“testAngular”]’).GM(‘refreshGrid’); console.log(‘数据没变是正常的, 因为这只是个示例,并不会真实删除数据.’); console.log(’—-删除操作完成—-’); } };}AppController.inject = [’$window’, ‘$rootScope’, ‘$scope’, ‘$element’, ‘$gridManager’];angular .module(‘myApp’, [‘gridManager’]) .controller(‘AppController’, AppController);调用公开方法以下方法需要在已经存在gridManager实例的Angular环境下使用。// 刷新$gridManager.refreshGrid(’testAngular’);// 更新查询条件$gridManager.setQuery(’testAngular’, {name: ‘baukh’});// …其它更多请直接访问API查看当前版本import gridManager from ‘gridmanager-angular-1.x’;console.log(‘GridManager’, angular.module(‘gridManager’).version); ...

April 6, 2019 · 3 min · jiezi

Angular动态创建组件之Portals

本文主要介绍使用Angular api 和 CDK Portals两种方式实现动态创建组件,另外还会讲一些跟它相关的内容。如:Angular多级依赖注入、ViewContainerRef,Portals可以翻译为 门户 ,我觉得放到这里叫 入口 更好,可以理解为动态创建组件的入口,类似于小程序或者Vue中的Slot.cdk全名Component Development Kit 组件开发包,是Angular官方在开发基于Material Design的组件库时抽象出来单独的一个开发包,里面封装了一些开发组件时的公共逻辑并且跟Material Design 设计无关,可以用来封装自己的组件库或者直接在业务开发中使用,里面代码抽象程度非常高,非常值得学习,现在我用到的有Portals、Overlay(打开浮层相关)、SelectionModel、Drag and Drop等.官方:https://material.angular.io/中文翻译:https://material.angular.cn动态创建组件想想应用的路由,一般配置路由地址的时候都会给这个地址配置一个入口组件,当匹配到这个路由地址的时候就在指定的地方渲染这个组件,动态创建组件类似,在最页面未接收到用户行为的时候,我不知道页面中这块区域应该渲染那个组件,当页面加载时根据数据库设置或者用户的操作行为才能确定最终要渲染的组件,这时候就要用代码动态创建组件把目标组件渲染到正确的地方。示例截图 使用Angular API动态创建组件该路由的入口组件是PortalsEntryConponent组件,如上面截图所示右侧有一块虚线边框的区域,里面具体的渲染组件不确定。第一步先在视图模板中定义一个占位的区域,动态组件就要渲染在这个位置,起一个名称#virtualContainer文件portals-entry.component.html<div class=“portals-outlet” > <ng-container #virtualContainer> </ng-container></div>第二步通过ViewChild取到这个container对应的逻辑容器文件portals-entry.component.ts @ViewChild(‘virtualContainer’, { read: ViewContainerRef }) virtualContainer: ViewContainerRef;第三步处理单击事件,单击按钮时动态创建一个组件,portals-entry.component.ts完整逻辑import { TaskDetailComponent } from ‘../task/task-detail/task-detail.component’;@Component({ selector: ‘app-portals-entry’, templateUrl: ‘./portals-entry.component.html’, styleUrls: [’./portals-entry.component.scss’], providers: [ ]})export class PortalsEntryComponent implements OnInit { @ViewChild(‘virtualContainer’, { read: ViewContainerRef }) virtualContainer: ViewContainerRef; constructor( private dynamicComponentService: DynamicComponentService, private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector, ) { } ngOnInit() { } openTask() { const task = new TaskEntity(); task.id = ‘1000’; task.name = ‘写一篇关于Portals的文章’; const componentFactory = this.componentFactoryResolver.resolveComponentFactory(TaskDetailComponent); const componentRef = this.virtualContainer.createComponent<TaskDetailComponent>( componentFactory, null, this.virtualContainer.injector ); (componentRef.instance as TaskDetailComponent).task = task; // 传递参数 }} 代码说明openTask()方法绑定到模板中按钮的单击事件导入要动态创建的组件TaskDetailComponentconstructor注入injector、componentFactoryResolver 动态创建组件需要的对象,只有在组件上下文中才可以拿到这些实例对象使用api创建组件,现根据组件类型创建一个ComponentFactory对象,然后调用viewContainer的createComponent创建组件使用componentRef.instance获取创建的组件实例,这里用来设置组件的task属性值其它ViewContainerRef除了createComponent方法外还有一个createEmbeddedView方法,用于创建模板@ViewChild(‘customTemplate’)customTemplate: TemplateRef<any>;this.virtualContainer.createEmbeddedView(this.customTemplate, { name: ‘pubuzhixing’ }); createEmbeddedView方法的第二个参数,用于指定模板的上下文参数,看下模板定义及如何使用参数<ng-template #customTemplate let-name=“name”> <p>自定义模板,传入参数name:{{name}}</p></ng-template> 此外还可以通过ngTemplateOutlet直接插入内嵌视图模板,通过ngTemplateOutletContext指定模板的上下文参数<ng-container [ngTemplateOutlet]=“customTemplate” [ngTemplateOutletContext]="{ name:‘pubuzhixing’ }"></ng-container>小结分析下Angular动态创建组件/内嵌视图的API,动态创建组件首先需要一个被创建的组件定义或模板声明,另外需要Angular上下文的环境来提供这个组件渲染在那里以及这个组件的依赖从那获取,viewContainerRef是动态组件的插入位置并且提供组件的逻辑范围,此外还需要单独传入依赖注入器injector,示例直接使用逻辑容器的injector,是不是很好理解。示例仓储:https://github.com/pubuzhixin…CDK Portal 官方文档介绍这里先对Portal相关的内容做一个简单的说明,后面会有两个使用示例,本来这块内容准备放到最后的,最终还是决定放在前面,可以先对Portals有一个简单的了解,如果其中有翻译不准确请见谅。地址:https://material.angular.io/c…——– 文档开始portals 提供渲染动态内容到应用的可伸缩的实现,其实就是封装了Angular动态创建组件的过程Portals这个Portal指是能动态渲染一个指定位置的 UI块 到页面中的一个 open slot 。UI块 指需要被动态渲染的内容,可以是一个组件或者是一个模板,而 open slot 是一个叫做PortalOutlet的开放的占位区域。Portals和PortalOutlets是其它概念中的低级的构造块,像overlays就是在它基础上构建的 Portal<T> 包括动态组件的抽象类,可以是TemplatePortal(模板)或者ComponentPortal(组件)方法描述attach(PortalOutlet): T把当前Portal附加到宿主上detach(): void把Portal从宿主上拆离isAttached: boolean当前Portal是否已经附加到宿主上 PortalOutlet 动态组件的宿主方法描述attach(Portal): any附加指定Portaldetach(): any拆离当前附加Portaldispose(): void永久释放宿主资源hasAttached: boolean当前是否已经装在Portal代码片段说明CdkPortal <ng-template cdkPortal> <p>The content of this template is captured by the portal.</p></ng-template><!– OR –><!– 通过下面的结构指令语法可以得到同样的结果 –><p cdkPortal> The content of this template is captured by the portal.</p> 可以通过ViewChild、ViewChildren获取到该Portal,类型应该是CdkPortal,如下所示: // 模板中的Portal@ViewChild(CdkPortal) templateCDKPortal: TemplatePortal<any>; ComponentPortal 组件类型的Portal,需要当前组件在NgModule的entryComponents中配置才能动态创建该组件。 this.userSettingsPortal = new ComponentPortal(UserSettingsComponent); CdkPortalOutlet使用指令可以把portal outlet添加到一个ng-template,cdkPortalOutlet把当前元素指定为PortalOutlet,下面代码把userSettingsPortal绑到此portal-outlet上 <!– Attaches the userSettingsPortal from the previous example. –><ng-template [cdkPortalOutlet]=“userSettingsPortal”></ng-template> —– 文档完毕 Portals使用示例这里首先使用新的api完成和最上面示例一样的需求,在同样的位置动态渲染TaskDetailComponent组件。第一步同样是设置一个宿主元素用于渲染动态组件,可以使用指令cdkPortalOutlet挂载一个PortalOutlet在这个ng-container元素上<div class=“portals-outlet”> <ng-container #virtualContainer cdkPortalOutlet> </ng-container></div>第二步与 使用Angular API动态创建组件 一节使用同一个逻辑元素作为宿主,只不过这里的获取容器的类型是CdkPortalOutlet,代码如下@ViewChild(‘virtualContainer’, { read: CdkPortalOutlet })virtualPotalOutlet: CdkPortalOutlet;第三步创建一个ComponentPortal类型的Portal,并且将它附加上面获取的宿主virtualPotalOutlet上,代码如下 portalOpenTask() { this.virtualPotalOutlet.detach(); const taskDetailCompoentPortal = new ComponentPortal<TaskDetailComponent>( TaskDetailComponent ); const ref = this.virtualPotalOutlet.attach(taskDetailCompoentPortal); // 此处同样可以 通过ref.instance传递task参数 }小结这里是使用ComponentPortal的示例实现动态创建组件,Portal还有一个子类TemplatePortal是针对模板实现的,上节 CDK Portal 官方文档介绍 中有介绍,这里就不在赘述了。总之使用Portals可以很大程度上简化代码逻辑。示例仓储:https://github.com/pubuzhixin…Portals 源码分析上面只是使用Portal的最简单用法,下面讨论下它的源码实现,以便更好的理解ComponentPortal首先我们先看一下ComponentPortal类的创建,上面的例子只是指定了一个组件类型作为参数,其实它还有别的参数可以配置,先看下ComponentPortal的构造函数定义export class ComponentPortal<T> extends Portal<ComponentRef<T>> { constructor( component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null, componentFactoryResolver?: ComponentFactoryResolver | null) { super(); this.component = component; this.viewContainerRef = viewContainerRef; this.injector = injector; this.componentFactoryResolver = componentFactoryResolver; } }ComponentPortal构造函数的另外两个参数 viewContainerRef 和 injector viewContainerRef 参数非必填默认附到PortalOutlet上,如果传入viewContainerRef参数,那么ComponentPortal就会附到该viewContaierRef上,而不是当前PortalOutlet所在的元素上。injector 参数非必填,默认使用PortalOutlet所在的逻辑容器的injector,如果传入injector,那么动态创建的组件就使用传入的injector作为注入器。 BasePortalOutletBasePortalOutlet提供了附加ComponentPortal和TemplatePortal的部分实现,我们看下attach方法的部分代码(仅仅展示部分逻辑) /* Attaches a portal. / attach(portal: Portal<any>): any { if (!portal) { throwNullPortalError(); } if (portal instanceof ComponentPortal) { this._attachedPortal = portal; return this.attachComponentPortal(portal); } else if (portal instanceof TemplatePortal) { this._attachedPortal = portal; return this.attachTemplatePortal(portal); } throwUnknownPortalTypeError(); } attach处理前先根据Portal的类型是确实是组件还是模板,然后再进行相应的处理,其实最终还是调用了ViewContainerRef的createComponent或者createEmbeddedView方法,对这块感兴趣看查看源代码文件portal-directives.ts。 DomPortalOutletDomPortalOutlet可以把一个Portal<T>插入到一个Angular应用上下文之外的DOM中,想想我们前面的例子,无论自己实现还是使用CdkPortalOutlet都是把一个模板或者组件插入到一个Angular上下文中的宿主ViewContainerRef中,而DomPortalOutlet就是 脱离Angular上下文 的宿主,可以把Portal<T>渲染到任意dom中,我们常常有这种需求,比如弹出的模态框、Select浮层。在cdk中Overlay用到了DomPortalOutlet,然后material ui的MatMenu也用到了DomPortalOutlet,MatMenu比较容易理解,简单看下它是如何创建和使用的DomPortalOutle(查看全部)if (!this._outlet) { this._outlet = new DomPortalOutlet(this._document.createElement(‘div’), this._componentFactoryResolver, this._appRef, this._injector);}const element: HTMLElement = this._template.elementRef.nativeElement;element.parentNode!.insertBefore(this._outlet.outletElement, element);this._portal.attach(this._outlet, context);上面的代码先创建了DomPortalOutlet类型的对象_outlet,DomPortalOutlet是一个DOM宿主它不在Angular的任何一个ViewContainerRef中,现在看下它的四个构造函数参数参数名类型说明outletElementElement创建的document元素_componentFactoryResolverComponentFactoryResolver刚开始一直不理解这个实例对象是干什么的,后来查了资料,它大概的作用是对要创建的组件或者模板进行编译_appRefApplicationRef当前Angular应用的一个关联对象_defaultInjectorInjector注入器对象说明:这节讲的 脱离Angular上下文 是不太准确定,任何模板或者组件都不能脱离Angular的运行环境,这里应该是脱离了实际渲染的Component Tree,单独渲染到指定dom中。复杂示例为ComponentPortal传入PortalInjector对象,PortalInjector实例对象配置一个其它业务组件的injector并且配置tokens,下面简单说明下逻辑结构,有兴趣的可看完整示例。业务组件TaskListComponent文件task-list.component.ts@Component({, selector: ‘app-task-list’, templateUrl: ‘./task-list.component.html’, styleUrls: [’./task-list.component.scss’], providers: [TaskListService]})export class TaskListComponent implements OnInit { constructor(public taskListService: TaskListService) {}} 组件级提供商配置了TaskListService定义TaskListService用于获取任务列表数据,并保存在属性tasks中TaskListComponent模板在模板中直接绑定taskListService.tasks属性数据修改父组件PortalsEntryComponent因为PortalOutlet是在父组件中,所以单击任务列表创建动态组件的逻辑是从父组件响应的portals-entry.component.ts @ViewChild(’taskListContainer’, { read: TaskListComponent }) taskListComponent: TaskListComponent; ngOnInit() { this.taskListComponent.openTask = task => { this.portalCreatTaskModel(task); }; }portalCreatTaskModel(task: TaskEntity) { this.virtualPotalOutlet.detach(); const customerTokens = new WeakMap(); customerTokens.set(TaskEntity, task); const portalInjector = new PortalInjector( this.taskListViewContainerRef.injector, customerTokens ); const taskModelCompoentPortal = new ComponentPortal<TaskModelComponent>( TaskModelComponent, null, portalInjector ); this.virtualPotalOutlet.attach(taskModelCompoentPortal); }给ComponentPortal的构造函数传递了PortalInjector类型的参数portalInjector,PortalInjector继承自InjectorPortalInjector构造函数的两个参数第一个参数是提供一个基础的注入器injector,这里使用了taskListViewContainerRef.injector,taskListViewContainerRef就是业务TaskListComponent组件的viewContainerRef @ViewChild(’taskListContainer’, { read: ViewContainerRef }) taskListViewContainerRef: ViewContainerRef; 也就是新的组件的注入器来自于TaskListComponent第二个参数是提供一个tokens,类型是WeakMap,其实就是key/value的键值对,只不过它的key只能是引用类型的对象,这里把类型TaskEntity作为key,当前选中的实例对象作为value,就可以实现对象的传入,使用set方法customerTokens.set(TaskEntity, task);。新的任务详情组件TaskModelComponenttask-model.component.ts constructor( public task: TaskEntity, private taskListService: TaskListService ) {}没错,是通过注入器注入的方式获取TaskEntity实例和服务TaskListService的实例taskListService。 小结这个例子相对复杂,只是想说明可以给动态创建的组件传入特定的injector。总结想写Portals的使用主要是看了我们组件库中模态框ThyDialog的实现,觉得这些用法比较巧妙,所以想分享出来。示例仓储:https://github.com/pubuzhixin…组件库仓储:https://github.com/worktile/n…拓展ViewContainerRefangula.cn解释:表示可以将一个或多个视图附着到组件中的容器,可以包含宿主视图(当用 createComponent() 方法实例化组件时创建)和内嵌视图(当用 createEmbeddedView() 方法实例化 TemplateRef 时创建)。我这里的理解ViewContainerRef是Angular中的一个逻辑单元,简单理解它与组件或者页面中的html元素一一对应只是逻辑形态不同,它也有层级只是层级与组件树的层级不是一一对应,这点个人感觉有些难理解,就拿Portals里面ComponentPortal的实现来说,构造函数里面可以传入一个viewContainerRef,代码片段/* * A ComponentPortal is a portal that instantiates some Component upon attachment. /export class ComponentPortal<T> extends Portal<ComponentRef<T>> { /* * [Optional] Where the attached component should live in Angular’s logical component tree. * 可选参数 关联的组件应该寄宿的逻辑组件树的位置 * This is different from where the component renders, which is determined by the PortalOutlet. * 这跟组件真正渲染的位置是不同的,真正的位置由PortalOutlet决定 * The origin is necessary when the host is outside of the Angular application context. * 当宿主是在Angular上下文环境之外这个参数是必填项 */ viewContainerRef?: ViewContainerRef | null; constructor( component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null, componentFactoryResolver?: ComponentFactoryResolver | null) { // … }}对其中viewContainerRef的注释进行了简单的翻译,但还是不知道它是怎么实现逻辑组件树与真实渲染组件树设置不同层级,经过自己的尝试当设置viewContainerRef后,组件就渲染在了传入的viewContainerRef里面。属性 element 和 injector element 的类型是ElementRef,用来标识本容器在父容器中的位置与html中的元素一一对应 injector 的类型是Injector,它是容器的一个依赖注入器对象,我们在组件的constructor中注入的服务以及获取关联的对象都要通过它来查找,在ViewContainer的逻辑树中注入器对象有一个 注入器冒泡 机制,当一个组件申请获得一个依赖时,Angular 先尝试用该组件容器自己的注入器来满足它,在该组件的容器中找不到实例并且也没有配置注入器提供商(providers),他就会在把这个申请转给它父组件的注入器来处理。所以在动态创建组件的时候可以单独配置这个injector可以子组件传递数据、共享实例对象。WeakMap最初因为不了解WeakMap而对这个实现疑惑不解,查了WeakMap的相关资料WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键名必须是对象,而值可以是任意的。键名是对象的弱引用,当对象被回收后,WeakMap自动移除对应的键值对,WeakMap结构有助于防止内存泄漏。 可以与Map对比理解,Map中key可以是各种类型,而WeakMap必须是对象。这样WeakMap就可以用来在不修改原引用类型对象的基础上,而扩充该对象的属性值,并且不影响引用类型对象的垃圾回收,随该对象的消失,扩充属性随之消失。本文作者:杨振兴文章来源:Worktile技术博客欢迎访问交流更多关于技术及协作的问题。文章转载请注明出处。 ...

March 19, 2019 · 3 min · jiezi

萌新与大型angularjs项目 一周目

前言本周,公司让我从已有项目入手,学习angularjs。需要最终能找到一个页面做一个一样的出来,并实现相关功能。下面我就简单讲述一下我的学习过程。初识大项目这是我第一次接触到一个别人写的完整的大项目,一开始还是很茫然的。第一步:跑项目先把项目跑起来,进行一个整体的了解。我这次需要仿写的是计量平台-強检器具备案管理-综合查询页面第二步:查看源代码之后,要找到别人写的代码在哪里才行。得益于老师讲的前后台分离的概念,前台代码和后台是分开的。打开webapp的文件夹需要找的就是controllers(控制器/C层),service(服务/M层)和views(视图/V层)但是当我打开C层打算看看代码的时候,一下子傻了这么一堆文件,要怎么才能找到我想要的页面呢还好,这时候我想到了angularjs的路由配置首先,我们截取这一部分网址然后进行以下步骤1.打开scripts文件下面的route.js文件,2.用ctrl+F调出查询框,将刚才的网址粘贴进来,并将中间的“/”改为“.”3.然后就可以找到相关的V层文件位置了4.下面这个controller的名字虽然很长,但是可以用ctrl+P进行全文件查找这样就很轻松的找到了C层文件。第三步:看注释我个人的观点是:V层是写给用户看的,C层是写给V层的,只有注释是写给程序员的。在此要特别感谢潘老师对于写注释重要性的一次次强调,让萌新能够清晰的了解各个模块的功能。仿?仿!仿。。。还是抄吧。。前面准备的文件全都找到了,也该开始试着写代码了满怀着悲愤的心情,抱着一去不复返的态度,决定与这一堆代码奋战到底。当然,基本流程还是比较简单的。首先是先建立一个自己的test页面view以及controller和service然后开始用$http.get从后台抓数据,用$scope和ng-model进行V层和C层的双向绑定,然后数据就在V层显示了在然后。。。突然发现就没别的了?!打开service文件,发现虽然很多行代码,但是通篇就讲一件事,按照我的要求给我数据,我找后台,后台给你全解决了,你就等着结果就行了。所以整个页面写下来,除了方法名自己起一个,剩下的完全就是抄下来的,也没有什么可改动的地方。这时候,我突然就明白了什么是接口,以及前后台分离的概念。不明白的问题1.关于导航栏新增栏目2.V层的自定义标签<yunzhi- >不知道怎么看属性总结这周看得还是比较简单的内容,个人感觉大项目与自己写的练习还是有区别的,更加注重规范。

March 15, 2019 · 1 min · jiezi

每周总结 3月9日

本周学习了教学视频的第三章的内容,其中大部分内容和第二章的都差不多,也没遇到什么太复杂的问题。遇到的问题问题1:这个页面的问题是点击保存按钮没有反应,下面控制台也没有报错首先我去查看了一下V层,发现ng-submit()也写了,应该不是V层的问题然后又去重新看了一遍视频,发现老师改过一次$scope.submit,我没注意正确写法问题2这周用idea的时候,打开突然发现运行按钮不能点了这边点开之后是这么一个弹框之后我换了一个电脑,然后查看了一下两个电脑的区别发现是少了一个叫spring boot的东西点击加号然后系统就自动找到Application这个文件了创建之后就可以把程序跑起来了。总结本周的学习时间还算比较多,但是每天回到家之后就会比较懒,躺在床上就总是不想起来,遇到了问题也只能自己解决,学习效率明显降低了。

March 9, 2019 · 1 min · jiezi

【CuteJavaScript】Angular6入门项目(4.改造组件和添加HTTP服务)

本文目录一、项目起步二、编写路由组件三、编写页面组件1.编写单一组件2.模拟数据3.编写主从组件四、编写服务1.为什么需要服务2.编写服务五、引入RxJS1.关于RxJS2.引入RxJS3.改造数据获取方式六、改造组件1.添加历史记录组件2.添加和删除历史记录七、HTTP改造1.引入HTTP2.通过HTTP请求数据3.通过HTTP修改数据4.通过HTTP增加数据5.通过HTTP删除数据6.通过HTTP查找数据本项目源码放在github六、改造组件从这里开始,我们要使用RxJS来改造组件和添加新功能了,让整个项目更加完善。1.添加历史记录组件创建HistoryComponent组件ng g component hostory然后在app.component.html文件夹中添加组件:<!– app.component.html –><app-history></app-history>2.添加增删改查功能这里我们要开始做书本的增删改查功能,需要先创建一个HistoryService服务,方便我们实现这几个功能:创建HistoryService服务ng g service history然后在生成的ts文件中,增加add和clear方法,add方法用来添加历史记录到history数组中,clear方法则是清空history数组:// history.service.tsexport class HistoryService { history: string[] = []; add(history: string){ this.history.push(history); } clear(){ this.history = []; }}使用HistoryService服务在将这个服务,注入到BooksService中,并改造getBooks方法:// books.service.tsimport { HistoryService } from ‘./history.service’;constructor( private historyservice: HistoryService) { }getBooks(): void{ this.historyservice.add(‘请求书本数据’) this.booksservice.getBookList() .subscribe(books => this.books = books);}也可以用相同方法,在IndexComponent中添加访问首页书本列表的记录。// index.component.tsimport { HistoryService } from ‘../history.service’;constructor( private booksservice: BooksService, private historyservice: HistoryService) { }getBooks(): void{ this.historyservice.add(‘访问首页书本列表’); this.booksservice.getBookList() .subscribe(books => this.books = books);}接下来,将我们的HistoryService注入到HistoryComponent中,然后才能将历史数据显示到页面上:// history.component.tsimport { HistoryService } from ‘../history.service’;export class HistoryComponent implements OnInit { constructor(private historyservice: HistoryService) { } ngOnInit() {}}<!– history.component.html –><div *ngIf=“historyservice.history.length”> <h2>操作历史:</h2> <div> <button class=“clear” (click)=“historyservice.clear()” >清除</button> <div *ngFor=“let item of historyservice.history”>{{item}}</div> </div></div>代码解释: *ngIf=“historyservice.history.length”,是为了防止还没有拿到历史数据,导致后面的报错。 (click)=“historyservice.clear()”, 绑定我们服务中的clear事件,实现清除缓存。 *ngFor=“let item of historyservice.history”,将我们的历史数据渲染到页面上。 到了这一步,就能看到历史数据了,每次也换到首页,都会增加一条。 接下来,我们要在书本详情页也加上历史记录的统计,导入文件,注入服务,然后改造getBooks方法,实现历史记录的统计:// detail.component.tsimport { HistoryService } from ‘../history.service’;export class DetailComponent implements OnInit { constructor( private route: ActivatedRoute, private location: Location, private booksservice: BooksService, private historyservice: HistoryService ) { } //… getBooks(id: number): void { this.books = this.booksservice.getBook(id); this.historyservice.add(查看书本${this.books.title},id为${this.books.id}); console.log(this.books) }}这时候就可以在历史记录中,看到这些操作的记录了,并且清除按钮也正常使用。七、HTTP改造原本我只想写到上一章,但是想到,我们实际开发中,哪有什么本地数据,基本上数据都是要从服务端去请求,所以这边也有必要引入这一张,模拟实际的HTTP请求。1.引入HTTP在这一章,我们使用Angular提供的 HttpClient 来添加一些数据持久化特性。 然后实现对书本数据进行获取,增加,修改,删除和查找功能。 HttpClient是Angular通过 HTTP 与远程服务器通讯的机制。 这里我们为了让HttpClient在整个应用全局使用,所以将HttpClient导入到根模块app.module.ts中,然后把它加入 @NgModule.imports 数组:import { HttpClientModule } from ‘@angular/common/http’;@NgModule({ //… imports: [ BrowserModule, AppRoutingModule, HttpClientModule ], //…})这边我们使用 内存 Web API(In-memory Web API) 模拟出的远程数据服务器通讯。 注意: 这个内存 Web API 模块与 Angular 中的 HTTP 模块无关。通过下面命令来安装:npm install angular-in-memory-web-api –save然后在app.module.ts中导入 HttpClientInMemoryWebApiModule 和 InMemoryDataService 类(后面创建):// app.module.tsimport { HttpClientInMemoryWebApiModule } from ‘angular-in-memory-web-api’;import { InMemoryDataService } from ‘./in-memory-data.service’;@NgModule({ // … imports: [ // … HttpClientInMemoryWebApiModule.forRoot( InMemoryDataService, {dataEncapsulation:false} ) ], // …})export class AppModule { }知识点: forRoot() 配置方法接受一个 InMemoryDataService 类(初期的内存数据库)作为参数。 然后我们要创建InMemoryDataService类:ng g service InMemoryData并将生成的in-memory-data.service.ts修改为:// in-memory-data.service.tsimport { Injectable } from ‘@angular/core’;import { InMemoryDbService } from ‘angular-in-memory-web-api’;import { Books } from ‘./books’;@Injectable({ providedIn: ‘root’})export class InMemoryDataService implements InMemoryDbService { createDb(){ const books = [ { id: 1, url: ‘https://img3.doubanio.com/view/subject/m/public/s29988481.jpg’, title: ‘像火焰像灰烬’, author: ‘程姬’, }, // 省略其他9条数据 ]; return {books}; } constructor() { }}这里先总结InMemoryDbService所提供的RESTful API,后面都要用到: 例如如果url是api/books,那么查询所有成员:以GET方法访问api/books查询某个成员:以GET方法访问api/books/id,比如id是1,那么访问api/books/1更新某个成员:以PUT方法访问api/books/id删除某个成员:以DELETE方法访问api/books/id增加一个成员:以POST方法访问api/books2.通过HTTP请求数据现在要为接下来的网络请求做一些准备,先在books.service.ts中引入HTTP符号,然后注入HttpClient并改造:// books.service.tsimport { HttpClient, HttpHeaders} from ‘@angular/common/http’;// …export class BooksService { constructor( private historyservice: HistoryService, private http: HttpClient ) { } private log(histories: string){ this.historyservice.add(正在执行:${histories}) } private booksUrl = ‘api/books’; // 提供一个API供调用 // …}这里我们还新增一个私有方法log和一个私有变量booksUrl。 接下来我们要开始发起http请求数据,开始改造getBookList方法:// books.service.ts// …getBookList(): Observable<Books[]> { this.historyservice.add(‘请求书本数据’) return this.http.get<Books[]>(this.booksUrl);}// …这里我们使用 http.get 替换了 of,其它没修改,但是应用仍然在正常工作,这是因为这两个函数都返回了 Observable<Hero[]>。 实际开发中,我们还需要考虑到请求的错误处理,要捕获错误,我们就要使用 RxJS 的 catchError() 操作符来建立对 Observable 结果的处理管道(pipe)。 我们引入catchError 并改造原本getBookList方法:// books.service.tsgetBookList(): Observable<Books[]> { this.historyservice.add(‘请求书本数据’) return this.http.get<Books[]>(this.booksUrl).pipe( catchError(this.handleError<Books[]>(‘getHeroes’, [])) );}private handleError<T> (operation = ‘operation’, result?: T) { return (error: any): Observable<T> => { this.log(${operation} 失败: ${error.message}); // 发出错误通知 return of(result as T); // 返回空结果避免程序出错 };}知识点: .pipe() 方法用来扩展 Observable 的结果。 catchError() 操作符会拦截失败的 Observable。并把错误对象传给错误处理器,错误处理器会处理这个错误。 handleError() 错误处理函数做了两件事,发出错误通知和返回空结果避免程序出错。 这里还需要使用tap操作符改造getBookList方法,来窥探Observable数据流,它会查看Observable的值,然后我们使用log方法,记录一条历史记录。 tap 回调不会改变这些值本身。// books.service.tsgetBookList(): Observable<Books[]> { return this.http.get<Books[]>(this.booksUrl) .pipe( tap( _ => this.log(‘请求书本数据’)), catchError(this.handleError<Books[]>(‘getHeroes’, [])) );}3.通过HTTP修改数据这里我们需要在原来DetailComponent上面,添加一个输入框、保存按钮和返回按钮,就像这样:<!– detail.component.html –><!– 前面代码省略 –><div> <h2>修改信息:</h2> <label>新标题: <input [(ngModel)]=“books.title” placeholder=“请输入新标题”> </label> <button (click)=“save()">保存</button> <button (click)=“goBack()">返回</button></div>这边切记一点,一定要在app.module.ts中引入 FormsModule模块,并在@NgModule的imports中引入,不然要报错了。// app.module.ts// …import { FormsModule } from ‘@angular/forms’; @NgModule({ // … imports: [ // … FormsModule ], // …})input框绑定书本的标题books.title,而保存按钮绑定一个save()方法,这里还要实现这个方法:// detail.component.tssave(): void { this.historyservice.updateBooks(this.books) .subscribe(() => this.goBack());}goBack(): void { this.location.back();}这里通过调用BooksService的updateBooks方法,将当前修改后的书本信息修改到源数据中,这里我们需要去books.service.ts中添加updateBooks方法:// books.service.ts// …updateBooks(books: Books): Observable<any>{ return this.http.put(this.booksUrl, books, httpOptions).pipe( tap(_ => this.log(修改书本的id是${books.id})), catchError(this.handleError<Books>(getBooks请求是id为${books.id})) )}// …知识点: HttpClient.put() 方法接受三个参数:URL 地址、要修改的数据和其他选项。 httpOptions 常量需要定义在@Injectable修饰器之前。 现在,我们点击首页,选择一本书进入详情,修改标题然后保存,会发现,首页上这本书的名称也会跟着改变呢。这算是好了。4.通过HTTP增加数据我们可以新增一个页面,并添加上路由和按钮:ng g component add添加路由:// app-routing.module.ts// …import { AddComponent } from ‘./add/add.component’;const routes: Routes = [ { path: ‘’, redirectTo:’/index’, pathMatch:‘full’ }, { path: ‘index’, component: IndexComponent}, { path: ‘detail/:id’, component: DetailComponent}, { path: ‘add’, component: AddComponent},]添加路由入口:<!– app.component.html –><!– 省略一些代码 –><a routerLink="/add”>添加书本</a>编辑添加书本的页面:<!– add.component.html –><div class=“add”> <h2>添加书本:</h2> <label>标题: <input [(ngModel)]=“books.title” placeholder=“请输入标题”> </label> <label>作者: <input [(ngModel)]=“books.author” placeholder=“请输入作者”> </label> <label>书本id: <input [(ngModel)]=“books.id” placeholder=“请输入书本id”> </label> <label>封面地址: <input [(ngModel)]=“books.url” placeholder=“请输入封面地址”> </label> <div><button (click)=“add(books)">添加</button></div></div>初始化添加书本的数据:// add.component.ts// …import { Books } from ‘../books’;import { BooksService } from ‘../books.service’;import { HistoryService } from ‘../history.service’;import { Location } from ‘@angular/common’;export class AddComponent implements OnInit { books: Books = { id: 0, url: ‘’, title: ‘’, author: ’’ } constructor( private location: Location, private booksservice: BooksService, private historyservice: HistoryService ) { } ngOnInit() {} add(books: Books): void{ books.title = books.title.trim(); books.author = books.author.trim(); this.booksservice.addBooks(books) .subscribe( book => { this.historyservice.add(新增书本${books.title},id为${books.id}); this.location.back(); }); }}然后在books.service.ts中添加addBooks方法,来添加一本书本的数据:// books.service.tsaddBooks(books: Books): Observable<Books>{ return this.http.post<Books>(this.booksUrl, books, httpOptions).pipe( tap((newBook: Books) => this.log(新增书本的id为${newBook.id})), catchError(this.handleError<Books>(‘添加新书’)) );}现在就可以正常添加书本啦。 5.通过HTTP删除数据这里我们先为每个书本后面添加一个删除按钮,并绑定删除事件delete:<!– books.component.html –><!– 省略一些代码 –><span class=“delete” (click)=“delete(list)">X</span>// books.component.tsimport { BooksService } from ‘../books.service’;export class BooksComponent implements OnInit { @Input() list: Books; constructor( private booksservice: BooksService ) { } // … delete(books: Books): void { this.booksservice.deleteBooks(books) .subscribe(); }}然后还要再books.service.ts中添加deleteBooks方法来删除:// books.service.tsdeleteBooks(books: Books): Observable<Books>{ const id = books.id; const url = ${this.booksUrl}/${id}; return this.http.delete<Books>(url, httpOptions).pipe( tap(_ => this.log(删除书本${books.title},id为${books.id})), catchError(this.handleError<Books>(‘删除书本’)) );}这里需要在删除书本结束后,通知IndexComponent将数据列表中的这条数据删除,这里还需要再了解一下Angular 父子组件数据通信。 然后我们在父组件IndexComponent上添加change事件监听,并传入本地的funChange:<!– index.component.html –><app-books *ngFor=“let item of books” [list]=“item” (change) = “funChange(item, $event)"></app-books>在对应的index.component.ts中添加funChange方法:// index.component.tsfunChange(books, $event){ this.books = this.books.filter(h => h.id !== books.id);}再来,我们在子组件BooksComponent上多导入Output和EventEmitter,并添加@Output()修饰器和调用emit:import { Component, OnInit, Input, Output, EventEmitter } from ‘@angular/core’;export class BooksComponent implements OnInit { // … @Output() change = new EventEmitter() // … delete(books: Books): void { this.booksservice.deleteBooks(books) .subscribe(()=>{ this.change.emit(books); }); }}这样就实现了我们父子组件之间的事件传递啦,现在我们的页面还是正常运行,并且删除一条数据后,页面数据会更新。6.通过HTTP查找数据还是在books.service.ts,我们添加一个方法getBooks,来实现通过ID来查找指定书本,因为我们是通过ID查找,所以返回的是单个数据,这里就是Observable<Books>类型:// books.service.tsgetBooks(id: number): Observable<Books>{ const url = ${this.booksUrl}/${id}; return this.http.get<Books>(url).pipe( tap( _ => this.log(请求书本的id为${id})), catchError(this.handleError<Books>(getBooks请求是id为${id})) )}注意,这里 getBooks 会返回 Observable<Books>,是一个可观察的单个对象,而不是一个可观察的对象数组。八、结语这个项目其实很简单,但是我还是一步一步的写下来,一方面让自己更熟悉Angular,另一方面也是希望能帮助到更多朋友哈~最终效果: 本部分内容到这结束Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…JS小册js.pingan8787.com微信公众号前端自习课 ...

February 23, 2019 · 4 min · jiezi

【CuteJavaScript】Angular6入门项目(2.构建项目页面和组件)

本文目录一、项目起步二、编写路由组件三、编写页面组件1.编写单一组件2.模拟数据3.编写主从组件四、编写服务1.为什么需要服务2.编写服务五、引入RxJS1.关于RxJS2.引入RxJS3.改造数据获取方式六、改造组件1.添加历史记录组件2.添加和删除历史记录七、HTTP改造1.引入HTTP2.通过HTTP请求数据3.通过HTTP修改数据4.通过HTTP增加数据5.通过HTTP删除数据6.通过HTTP查找数据本项目源码放在github三、编写页面组件接下来开始编写页面组件,这里我们挑重点来写,一些布局的样式,后面可以看源码。1.编写单一组件我们首先写一个书本信息的组件,代码如下:<!– index.component.html –><div class=“content”> <div class=“books_box”> <!– 单个课本 –> <div class=“books_item” *ngFor=“let item of [1,2,3,4,5,6,7,8,9,10]"> <img class=“cover” src=“https://img3.doubanio.com/view/subject/m/public/s29988481.jpg"> <div class=“title”><a>像火焰像灰烬</a></div> <div class=“author”>程姬</div> </div> </div></div>知识点: ngFor 是一个 Angular 的复写器(repeater)指令,就像angular1中的ng-for和vuejs中的v-for。 它会为列表中的每项数据复写它的宿主元素。 这时候可以看到页面变成下面这个样子: 接下来我们要把写死在HTML上面的数据,抽到JS中: 现在先新建一个books.ts文件来定义一个Book类,并添加id,url,title和author四个属性:// src/app/books.tsexport class Book { id: number; url: string; title: string; author: string;}然后回到index.component.ts文件去引入它,并定义一个books属性,使用导入进来的Book类作为类型:// index.component.tsimport { Book } from ‘../books’;export class IndexComponent implements OnInit { books: Book = { id: 1, url: ‘https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', title: ‘像火焰像灰烬’, author: ‘程姬’, }}然后再改造前面的组件文件index.component.html:<!– index.component.html –><div class=“books_item” ngFor=“let item of [1,2,3,4,5,6,7,8,9,10]"> <img class=“cover” src=”{{books.url}}” alt=”{{books.id}}"> <div class=“title”> <a>{{books.title}}</a> </div> <div class=“author”>{{books.author}}</div></div>接着,我们再为每个课本添加一个点击事件,来实现点击封面图能查看大图的效果,现在index.component.ts中定义一个getDetailImage方法,并在index.component.html中绑定该方法:// index.component.tsexport class IndexComponent implements OnInit { getDetailImage(books){ alert(正在查看id为${books.id}的大图!); }}这边方法的具体实现,不写,不是本文重点。下面是增加点击事件的绑定:<!– index.component.html –><img class=“cover” src="{{books.url}}" alt="{{books.id}}" (click)=“getDetailImage(books)">知识点: (click)是Angular用来绑定事件,它会让 Angular 监听这个 <img> 元素的 click 事件。 当用户点击 <img> 时,Angular 就会执行表达式 getDetailImage(books)。 再来,我们引入前面学到的路由链接指令来改造HTML:<!– index.component.html –><a routerLink="/detail/{{books.id}}">{{books.title}}</a>这时候,我们在点击书本的标题,发现页面跳转到URL地址为http://localhost:4200/detail/1的页面,这就说明,我们页面的路由跳转也成功了~ 改造完成后,可以看到,页面显示的还是一样,接下来我们先这样放着,因为我们后面会进行数据模拟,和模拟服务器请求。 我们就这样写好第一个单一组件,并且数据是从JS中读取的。2.模拟数据这时候为了方便后面数据渲染,我们这里需要模拟一些本地数据,我们创建一个本地 mock-books.ts文件来存放模拟的数据:// app/mock-books.tsimport { Books } from ‘./books’;export const BookList: Books[] = [ { id: 1, url: ‘https://img3.doubanio.com/view/subject/m/public/s29988481.jpg’, title: ‘像火焰像灰烬’, author: ‘程姬’, }, // 省略其他9条]然后在index.component.ts中导入模拟的数据,并将原有的books值修改成导入的模拟数据BookList:// index.component.tsimport { BookList } from ‘../mock-books’;books = BookList;并将原本的ngFor中修改成这样,绑定真正的数据:<!– index.component.html –><div class=“books_item” ngFor=“let item of books”> <img class=“cover” src=”{{item.url}}" alt="{{item.id}}"> <div class=“title”> <a>{{item.title}}</a> </div> <div class=“author”>{{item.author}}</div></div>3.编写主从组件当我们写完一个单一组件后,我们会发现,如果我们把每个组件都写到同一个HTML文件中,这是很糟糕的事情,这样做有缺点:代码复用性差;(导致每次相同功能要重新写)代码难维护;(因为一个文件会非常长)影响性能;(打开每个页面都要重复加载很多)为了解决这个问题,我们这里就要开始使用真正的组件化思维,将通用常用组件抽离出来,通过参数传递来控制组件的不同业务形态。 这便是我们接下来要写的主从组件。 思考一下,我们这里现在能抽成组件作为公共代码的,就是这个单个书本的内容,因为每个书本的内容都一致,只是里面数据的差异,于是我们再新建一个组件:ng g component books并将前面index.component.html中关于课本的代码剪切到books.component.html中来,然后删除掉ngFor的内容,并将原本本地的变量books替换成list,这个变量我们等会会取到:<!– books.component.html –><div class=“books_item”> <img class=“cover” src="{{list.url}}" alt="{{list.id}}" (click)=“getDetailImage(list)"> <div class=“title”> <a routerLink="/detail/{{list.id}}">{{list.title}}</a> </div> <div class=“author”>{{list.author}}</div></div>再将这个组件,引用到它的父组件中,这里是要引用到index.component.html的组件中,并将前面的ngFor再次传入<app-books>:<div class=“content”> <div class=“books_box”> <app-books *ngFor=“let item of books”></app-books> </div></div>接下来要做的就是获取到list变量的值,显然这个值是要从外面组件传进来的,我们需要在books.component.ts引入前面定义的 Books类 和 @Input() 装饰器,还要添加一个带有 @Input() 装饰器的 list 属性,另外还要记得将getDetailImage方法也剪切过来:// books.component.tsimport { Component, OnInit, Input } from ‘@angular/core’;import { Books } from ‘../books’;export class BooksComponent implements OnInit { @Input() list: Books; constructor() { } ngOnInit() {} getDetailImage(books){ alert(正在查看id为${books.id}的大图!); }}@Input() 装饰器介绍具体可以查看 手册 我们要获取的 list 属性必须是一个带有 @Input() 装饰器的输入属性,因为外部的 IndexComponent 组件将会绑定到它。就像这样:<app-books *ngFor=“let list of books” [list]=“item”></app-books>知识点: [list]=“item” 是 Angular 的属性绑定语法。这是一种单向数据绑定。从 IndexComponent 的 item 属性绑定到目标元素的 list 属性,并映射到了 BooksComponent 的 list 属性。做到这里,我们已经将BooksComponent作为IndexComponent的子组件来引用了,在实际开发过程中,这样的父子组件关系,会用的非常多。 写到这里,看看我们项目,还是一样正常在运行,只是现在项目中组件分工更加明确了。 现在的效果图: 本部分内容到这结束Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…JS小册js.pingan8787.com微信公众号前端自习课 ...

February 23, 2019 · 2 min · jiezi

【CuteJavaScript】Angular6入门项目(3.编写服务和引入RxJS)

本文目录一、项目起步二、编写路由组件三、编写页面组件1.编写单一组件2.模拟数据3.编写主从组件四、编写服务1.为什么需要服务2.编写服务五、引入RxJS1.关于RxJS2.引入RxJS3.改造数据获取方式六、改造组件1.添加历史记录组件2.添加和删除历史记录七、HTTP改造1.引入HTTP2.通过HTTP请求数据3.通过HTTP修改数据4.通过HTTP增加数据5.通过HTTP删除数据6.通过HTTP查找数据本项目源码放在github四、编写服务截止到这部分,我们的BooksComponent组件获取和显示的都是本地模拟的数据。 接下来我们要开始对这些进行重构,让聚焦于为它的视图提供支持,这也让它更容易使用模拟服务进行单元测试。1.为什么需要服务我们不应该让组件来直接获取或保存数据,它们应该聚焦于展示数据,而数据访问的工作交给其他服务来做。 这里我们需要创建一个名为BooksService的服务,让我们应用中所有的类都使用它来获取书本列表的数据,使用的时候,只需要将它通过Angular的依赖注入机制注入到需要用的组件的构造函数中。 知识点: 服务可以实现多个不同组件之间信息共享,后面我们还会将它注入到两个地方: BooksService中,使用该服务发送消息。 IndexService中,使用该服务来展示消息。 接下来我们使用命令行,创建BooksService :ng g service books在生成的books.service.ts文件中:// books.service.tsimport { Injectable } from ‘@angular/core’;@Injectable({ providedIn: ‘root’})新导入了@Injectable装饰器,是为了让BooksService提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖,简单理解就是如果你的服务需要依赖,那么你就需要导入它。 并且它接收该服务的元数据对象。2.编写服务接下来我们开始编写books.service.ts服务。导入服务所需组件这里我们导入Books和BookList,并添加一个getBooks方法来返回所有书本的数据,并且还需要添加一个getBooks方法来返回指定id的书本信息:// index.component.tsimport { Books } from ‘./books’;import { BookList } from ‘./mock-books’;@Injectable({ providedIn: ‘root’})export class BooksService { constructor() { } getBookList(): Books[] { return BookList; } getBook(id: number): Books{ return BookList.find(book => book.id === id) }}在我们使用这个服务之前,需要先注册该服务,因为我们在使用ng g service books命令创建服务时,CLI已经默认为我们添加了注册了,这是方法就是上面代码中的:providedIn: ‘root’表示将我们的服务注册在根注入器上,这样我们就可以把这个服务注入到任何享用的类上了。修改IndexComponent先删除BookList的引入,并修改books属性的定义:// index.component.tsimport { BooksService } from ‘../books.service’;export class IndexComponent implements OnInit { books : Books[]; ngOnInit() {}}然后注入我们的BooksService服务,需要先往构造函数中添加一个私有的booksservice,使用注入的BooksService作为类型,理解成一个注入点:// index.component.tsconstructor(private booksservice: BooksService) { }之后我们需要添加一个getBooks方法来获取这些书本数据,并在生命周期函数ngOnInit中调用:export class IndexComponent implements OnInit { ngOnInit() { this.getBooks(); } getBooks(): void{ this.books = this.booksservice.getBookList(); }}修改DetailComponent我们先改造书本详情页的HTML结构:<!– detail.component.html –><div *ngIf=“books” class=“detail”> <h3>《{{books.title}}》介绍</h3> <div> <img src="{{books.url}}"> </div> <p>书本标题: {{books.title}}</p> <p>书本作者: {{books.author}}</p> <p>书本id: {{books.id}}</p></div><div ngIf="!books" class=“detail”> <h3>暂无信息</h3></div>知识点: 这里使用了ngIf指令,当条件为true则显示其HTML内容。// detail.component.tsimport { Books } from ‘../books’;import { BooksService } from ‘../books.service’;export class DetailComponent implements OnInit { constructor( private route: ActivatedRoute, private location: Location, private booksservice: BooksService // 引入BooksService服务 ) { } books: Books; // 定义books类型 ngOnInit() { this.getDetail() } getDetail(): void{ const id = +this.route.snapshot.paramMap.get(‘id’); this.getBooks(id); } getBooks(id: number): void { this.books = this.booksservice.getBook(id); }}这段代码,主要定义了getBooks方法,当刚进入页面时,将书本id传入getBooks方法,去BooksService去获取对应id的书本信息,并复制给变量books,然后展示到页面。 改造之后,我们的页面显示依旧正常。 但是我们要知道,这背后的逻辑已经改变了。五、引入RxJS改造项目1.关于RxJS这里简单介绍关键概念,具体可以查看 RxJS 官网,也可以参考 浅析Angular之RxJS。什么是RxJSRxJS全称Reactive Extensions for JavaScript,中文意思: JavaScript的响应式扩展。 RxJS主要是提供一种更加强大和优雅的方式,来利用响应式编程的模式,实现JavaScript的异步编程。RxJS优点纯净性;流动性;RxJS核心概念RxJS 是基于观察者模式和迭代器模式以函数式编程思维来实现的。RxJS 中含有两个基本概念:Observables 与 Observer。 Observables 作为被观察者,是一个值或事件的流集合;而 Observer 则作为观察者,根据 Observables 进行处理。它们之间的订阅发布关系(观察者模式) 如下: 订阅:Observer 通过 Observable 提供的 subscribe() 方法订阅 Observable。 发布:Observable 通过回调 next 方法向 Observer 发布事件。 ———— 来源Angular修仙之路 RxJS Observable 另外这里列出来一些核心,具体还是看官网咯,并且下面使用到的时候会具体介绍。Observable (可观察对象): 表示一个概念,这个概念是一个可调用的未来值或事件的集合。Observer(观察者): 一个回调函数的集合,它知道如何去监听由 Observable 提供的值。Subscription (订阅): 表示 Observable 的执行,主要用于取消 Observable 的执行。Operators (操作符): 采用函数式编程风格的纯函数 (pure function),使用像 map、filter、concat、flatMap 等这样的操作符来处理集合。Subject (主体): 相当于 EventEmitter,并且是将值或事件多路推送给多个 Observer 的唯一方式。Schedulers (调度器): 用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout 或 requestAnimationFrame 或其他。2.引入RxJS在我们的真实应用中,我们必须要等到服务器响应后,我们才能获取到数据,因此这天生就需要用异步思维来操作。 由于Angular中已经自带RxJS,所以我们只要在需要使用的时候,引入即可使用:3.改造数据获取方式了解完RxJS的一些概念后,我们开始改造下这些书本的数据获取方式。改造BooksService首先我们从 RxJS 中导入 Observable 和 of 符号:// books.service.tsimport { Observable, of } from ‘rxjs’;知识点: Observable: 观察者模式中的观察者,具体可以参考 Angular修仙之路 RxJS Observable of: 用来获取观察者拿到的数据,通常是一个Observable。 然后修改getBookList方法// books.service.tsgetBookList(): Observable<Books[]> { return of(BookList);}这里 of(BookList) 返回一个 Observable<Books[]>,它会发出单个值,这个值就是这些模拟书本的数组。改造IndexComponent这里也要修改getBooks方法,使用subscribe去订阅服务返回回来的值:// index.component.tsgetBooks(): void{ this.booksservice.getBookList() .subscribe(books => this.books = books);}由于原本直接赋值数据,在实际场景中是不可能这样同步的,所以这里subscribe函数,会在Observable发出数据以后,再把书本列表传到里面的回调函数,再复制给books属性。 使用这种异步方式,当 BooksService 从远端服务器获取英雄数据时,不用担心还没拿到数据就执行后面。 下一步,我们就要改造一下项目了。 本部分内容到这结束Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…JS小册js.pingan8787.com微信公众号前端自习课 ...

February 23, 2019 · 2 min · jiezi

【CuteJavaScript】Angular6入门项目(1.构建项目和创建路由)

本文目录一、项目起步二、编写路由组件三、编写页面组件1.编写单一组件2.模拟数据3.编写主从组件四、编写服务1.为什么需要服务2.编写服务五、引入RxJS1.关于RxJS2.引入RxJS3.改造数据获取方式六、改造组件1.添加历史记录组件2.添加和删除历史记录七、HTTP改造1.引入HTTP2.通过HTTP请求数据3.通过HTTP修改数据4.通过HTTP增加数据5.通过HTTP删除数据6.通过HTTP查找数据八、结语这个入门项目是我学习完Angular 英雄指南教程后,自己手写的一个练习项目,一步一步来,最终的项目源码可以这里查看,大佬们请指点啦。 推荐两个Angular学习网站:Angular 中文网Angular 修仙之路还有呢,我没怎么关注到样式,所以样式会有点丑,主要都放在核心逻辑中了。 最终实现:首页书本列表数据展示各个页面静态/动态路由跳转本地模拟数据服务书本数据的增删改查父子组件通信常用指令使用和介绍后面我将把这个系列的文章,收录到我的【CuteJavaScript】中,里面有整理了ES6/7/8/9知识点和重温JS基础系列文章。 那么,快跟我一步步来完成这个入门项目吧。零、Angular安装Angular 需要 Node.js 的 8.x 或 10.x 版本。 检查你的Node.js版本,请在终端/控制台窗口中运行 node -v 命令。 要想安装 Node.js,请访问 nodejs.org。安装Angular CLInpm install -g @angular/cli常用命令后续用到会详细介绍这些命令。启动服务,并打开新窗口ng serve –open# –open 可简写 -o创建新组件ng generate component books# generate 可简写 g创建新服务ng generate service books创建路由模块ng generate module app-routing –flat –module=app其他另外Angular CLI还有很多的命令提供,详细可以查阅官方文档 Angular CLI 命令。最后搭建完是这样: 一、项目起步创建项目ng new bookscd books创建所需的两个页面组件ng g component indexng g component detailg是generate的简写。二、编写路由组件这里为了项目结构先起来,所以先简单配置一下路由,后面路由会调整,如果遇到什么不懂,可以查看Angular 路由与导航。安装路由模块ng g module app-routing –flat –module=app知识点: –flat 把这个文件放进了 src/app 中,而不是单独的目录中。 –module=app 告诉 CLI 把它注册到 AppModule 的 imports 数组中。引入路由模块// app-routing.module.ts import { RouterModule, Routes } from ‘@angular/router’;导出路由模块的指令这里需要添加一个 @NgModule.exports 数组,并传入RouterModule,导出 RouterModule 让路由器的相关指令可以在 AppModule 中的组件中使用。// app-routing.module.ts @NgModule({ imports: [CommonModule], declarations: [], exports: [RouterModule]})添加定义路由这里添加路由的时候,记得将所需要指向的组件也引入进来,这里我们需要引入两个页面的组件:// app-routing.module.ts import { IndexComponent } from ‘./index/index.component’;import { DetailComponent } from ‘./detail/detail.component’;然后将我们所需要的路由定义在routes变量中,类型是我们引入的Routes:// app-routing.module.ts const routes: Routes = [ { path: ‘’, redirectTo:’/index’, pathMatch:‘full’ }, // 1 { path: ‘index’, component: IndexComponent}, // 2 { path: ‘detail/:id’, component: DetailComponent}, // 3 ]知识点: angular的路由接收两个参数:path:用于匹配浏览器地址栏中 URL 的字符串。component:当导航到此路由时,路由器展示的组件名称。第1行代码: 作为路由系统的默认路由,当所有路由都不匹配的话,就会重定向到这个路由,并展示对应的组件。 第2行代码: 正常情况下的路由配置。 第3行代码: 配置的是携带参数的路由,在路由/后,用 : 拼接参数名来实现,获取这个参数的值的方法后面会介绍。另外,我们还可以这么传递参数,直接将数据通过路由传入,后面还会介绍:{ path: ‘pathname’, component: DemoComponent, data: { title: ‘pingan8787’ } },添加路由监视配置好路由还不能使用,需要一个监视路由变化的工具,这时候需要把RouterModule添加到 @NgModule.imports 数组中,并用 routes 来配置它。 这里只需要调用 imports 数组中的 RouterModule.forRoot() 函数就行了,就像这样:// app-routing.module.ts imports: [ RouterModule.forRoot(routes) ],添加路由出口所谓的路由出口,就是路由所对应的组件展示的地方,接下来我们在app.component.html内容中,添加<router-outlet></router-outlet>:<!– app.component.html –><div> <h1> 欢迎来到我的个人书屋! </h1> <router-outlet></router-outlet></div>这里的<router-outlet></router-outlet>就是我们路由输出的地方,也是组件展示的地方,简单理解就是,它会告诉路由器要在哪里显示路由的视图。添加路由链接所谓的路由链接,就是出发路由跳转事件的地方,比如一个按钮,一张图片等,我们还是在app.component.html中,使用<a routerLink="/path"></a>添加3个按钮:<!– app.component.html –><div> <h1> 欢迎来到我的个人书屋! </h1> <a routerLink="">重定向</a> | <a routerLink="/index">打开首页</a> | <a routerLink="/detail/1">打开书本详情</a> <router-outlet></router-outlet></div>这边3个按钮的路由,我们将上面定义的3种路由,传入到routerLink参数中,现在就项目就可以实现页面跳转了。 另外,这里还可以传入一个可选参数routerLinkActive=“className”,表示当这个<a>标签激活的时候显示的样式,值是一个字符串,为样式的类名:<a routerLink="/index" routerLinkActive=“activeClass”>打开首页</a> | 获取带参数路由的参数在第7步中,我们点击 打开书本详情 按钮中,在路由中带了参数,这时候我们需要这么来获取这个参数:先导出模块ActivatedRoute和Location:// detail.component.tsimport { ActivatedRoute } from ‘@angular/router’;import { Location } from ‘@angular/common’;再注入到构造函数中,并将值作为私有变量:// detail.component.tsexport class DetailComponent implements OnInit { constructor( private route: ActivatedRoute, private location: Location ) { } ngOnInit() {}}知识点: ActivatedRoute 保存该 DetailComponent 实例的路由信息。可以从这个组件获取URL中的路由参数和其他数据。 Location 是一个 Angular 的服务,用来与浏览器打交道。后续会使用它来导航回上一个视图。提取路由参数:这里声明getDetail方法,提取路由参数,并ngOnInit生命周期钩子方法在中执行。// detail.component.tsngOnInit() { this.getDetail()}getDetail(): void{ const id = +this.route.snapshot.paramMap.get(‘id’); console.log(此课本的id是${id})}知识点: route.snapshot 是一个路由信息的静态快照,抓取自组件刚刚创建完毕之后。 paramMap 是一个URL中路由所携带的参数值的对象。“id"对应的值就是要获取的书本的 id。 注意: 路由参数总会是字符串。这里我们使用 (+) 操作符,将字符串转换成数字。 现在在浏览器上刷新下页面,再点击 打开书本详情 按钮,可以看到控制台输出了 此课本的id是1 的结果。 到这一步,我们算是把路由配置完成了,接下来可以开始做页面的逻辑了。 本部分内容到这结束Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…JS小册js.pingan8787.com微信公众号前端自习课 ...

February 23, 2019 · 2 min · jiezi

每周总结 2月22日

ngRoute与ui.router由于放假之前看得视频教程,这次回来有点记不清楚了,就又重新跟着做了一下,结果发现了一个以前没注意到的问题。遇到的问题:这个main页面本来应该有一个table的,但是无论我怎么改,都是什么都没有,下面控制台也不报错,也不知道该怎么找。解决后来问了组长,组长说是我V层的ng-view应该改成ui-view这是因为我在配置路由的文件中改用了ui.router,而ng-view适用的是ngRoute,所以不显示了。延伸之后组长又给我讲了一下ui.router和ngRoute的区别,说是ui.router可以嵌套,而另一个不行。当时听完了比较迷糊,ng开头的我知道是angular,但是不明白ui开头的是从哪来的,然后就查了差。实验之后,又做了一个实验测试一下这两个用嵌套有什么区别。首先是ngRoute在main里面再嵌套一个,然后网页就卡死了。然后是ui.router就没出现卡死的情况。总结ui.router比ngRoute的功能要更好用。

February 22, 2019 · 1 min · jiezi

angularjs面试总结

ng-if 跟 ng-show/hide 的区别ng-if 在后面表达式为 true 的时候才创建这个 dom 节点,ng-show 是初始时就创建了,用 display:block 和 display:none 来控制显示和不显示。ng-if 会(隐式地)产生新作用域,ng-switch 、 ng-include 等会动态创建一块界面的也是如此。angular 的数据绑定脏检查机制。双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新,显然,这需要一个监控。原理就是,Angular 在 scope 模型上设置了一个监听队列,用来监听数据变化并更新 view 。每次绑定一个东西到 view 上时 AngularJS 就会往 $watch 队列里插入一条 $watch,用来检测它监视的 model 里是否有变化的东西。当浏览器接收到可以被 angular context 处理的事件时,$digest 循环就会触发,遍历所有的 $watch,最后更新 dom。例如,click 时会产生一次更新的操作(至少触发两次 $digest 循环)<button ng-click=“val=val+1”>increase 1</button>按下按钮浏览器接收到一个事件,进入到 angular context$digest 循环开始执行,查询每个 $watch 是否变化由于监视 $scope.val 的 $watch 报告了变化,因此强制再执行一次 $digest 循环新的 $digest 循环未检测到变化浏览器拿回控制器,更新 $scope.val 新值对应的 dom$digest 循环的上限是 10 次(超过 10次后抛出一个异常,防止无限循环)。两个平级界面块 a 和 b,如果 a 中触发一个事件,有哪些方式能让b 知道共用服务$rootScope广播ngRoute 和 ui.router区别ngRoute 模块是 Angular 自带的路由模块,而 ui.router 模块是基于 ngRoute模块开发的第三方模块。ui.router 是基于 state (状态)的, ngRoute 是基于 url 的,ui.router模块具有更强大的功能,主要体现在视图的嵌套方面。使用 ui.router 能够定义有明确父子关系的路由,并通过 ui-view 指令将子路由模版插入到父路由模板的 <div ui-view></div> 中去,从而实现视图嵌套。而在 ngRoute 中不能这样定义,如果同时在父子视图中 使用了 <div ng-view></div> 会陷入死循环。 ...

January 29, 2019 · 1 min · jiezi

软件设计与编程实践总结

问题描述大三了,一年一度的软件设计与编程实践到来了。继今年的软件工程实验之后第二个大实验;要求类似,多用户登录的复杂系统,软件工程实验要求五个下午,本实验要求八个上午。感谢Spring Data JPA,此框架真的是实验利器,大大提高了开发效率。感谢团队,感谢潘老师。要不我可能也要和我的同学一样一起学Tomcat,写Servlet,生连JDBC,手写SELECT,最后把实验写黄。需求描述三个角色:货主、司机、管理员。管理员负责维护基础信息,就是基础模块的增删改查,就是实体有点多。主要的就是业务流程:货主在平台发起订单,司机能综合查询相关订单。司机进行抢单,一个单可以被多个司机抢,然后货主选择我这批货由哪个司机进行承运,并进行付款。司机能更改运输状态,当货物到站时,货主确认后,平台将经过抽成的钱转给司机。问题Spring Data JPA@RepositoryRestResource(path = “Tax”)public interface TaxRepository extends JpaRepository<Tax, Long> { Tax findByMinPriceLessThanAndMaxPriceGreaterThanEqual(BigDecimal priceMin, BigDecimal priceMax);}直接在仓库接口上加注解,然后就在当前的path上生成了增删改查、分页、排序等接口。这个框架用是很好用,就是有一个问题,查询数据时后台不序列化id。id: 1name: zhangsanage: 18假如数据表中由这样一个实体,如果调用getAll接口的话,返回的却是这样的数据。[{ name: zhangsan, age: 18}]没有id在首页显示是没问题的,但是如果编辑的时候怎么办呢?查找官方文档,实现RepositoryRestConfigurer接口,在configureRepositoryRestConfiguration中配置为哪个实体暴露id。@Configurationpublic class CustomRestConfiguration implements RepositoryRestConfigurer { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.exposeIdsFor(GoodCategory.class) .exposeIdsFor(OrderDetail.class) .exposeIdsFor(Orders.class) .exposeIdsFor(Payment.class) .exposeIdsFor(Price.class) .exposeIdsFor(Tax.class) .exposeIdsFor(User.class) .exposeIdsFor(Vehicle.class); }}地图我们设计的是货主发起订单时的出发地与目的地都是在百度地图上选的。所以,需要解决三个问题:定位当前位置,点开地图时默认是这个位置。用户选择了位置,我怎么知道选的是哪个地方?计算出发地与目的地之间的距离,用于计算总价。设计和之前写定时任务不知道本初子午线一样,解决地图问题的时候,又暴露了我地理没学好的缺陷。一直想怎么存储位置呢?后来学习了一下百度地图的开发文档发现,经纬度是最好的解决方法。经纬度唯一地标识一个位置,我们可以根据经纬度再去百度查这个经纬度是一个什么地点。最后就是这么设计的,先选出经纬度,然后前台去根据当前选中的经纬度去百度要数据,这个经纬度对应的是哪个市哪个区那条街?同时根据起点与终点调百度的接口查询距离是多少。private String startPlace; // 起点private String endPlace; // 终点private Float startLongitude; // 起点经度private Float startLatitude; // 起点纬度private Float endLongitude; // 终点经度private Float endLatitude; // 终点纬度private Float distance; // 运输距离定位百度地图开放平台上的DEMO特别详细,有四种定位方式确定当前用户在哪,我们采用的是根据ip的定位方式,虽然距离不太精确,但对于实验来说足够了。算距离发现百度地图的api和我想象中的有差距,点开获取两点间的距离,没想到竟然是勾股定理。综合查询司机查订单的时候,用到了团队的核心库。想把这个YunzhiService的实例放到我的应用上下文中。@Configurationpublic class BeanConfig { @Bean public YunzhiService yunzhiService() { return new YunzhiServiceImpl(); }}总结弃用虽然不太愿意承认,但是当我帮同学装环境的时候,npm提示grunt不推荐使用。新技术将至。README文档对于一个项目的参考价值来说十分重要,我之前在Github上看到过不少开源的项目,我看过以后觉得毫无参考价值,一个文档都不写,这个项目开源对他人来说有何意义吗?物流运输平台 - Github深觉文档的价值,以后开源的每个项目,不管代码如何,在至少保证文档齐全。至少保证我这个项目开源,对他人有意义! ...

January 13, 2019 · 1 min · jiezi

Angular系列之目录

angular4笔记系列之内置指令angular4笔记系列之搭建环境angular4笔记系列之模板与数据绑定

January 10, 2019 · 1 min · jiezi

Angular2 模块简介

Angular2 模块简介NgModule 简介Angular 应用是模块化的, 而NgModule我们可以把它当做一个容器,用于存放一些内聚的代码块,它接收一个元数据对象并通过该对象告诉 Angular 如何编译和运行模块代码。它标记出该模块拥有的组件、指令和管道, 并把它们的一部分公开出去,以便外部组件使用它们。 它可以向应用的依赖注入器中添加服务提供商。每个 Angular 应用都至少有一个 NgModule 类,也就是根模块,它习惯上命名为 AppModule,并位于一个名叫 app.module.ts 的文件中。引导这个根模块就可以启动你的应用。NgModule 元数据NgModule 是一个带有 @NgModule() 装饰器的类。@NgModule() 装饰器是一个函数,它接受一个元数据对象,该对象的属性用来描述这个模块。其中最重要的属性如下。declarations(可声明对象表) —— 那些属于本 NgModule 的组件、指令、管道。exports(导出表) —— 那些能在其它模块的组件模板中使用的可声明对象的子集。imports(导入表) —— 那些导出了本模块中的组件模板所需的类的其它模块。providers —— 本模块向全局服务中贡献的那些服务的创建器。 这些服务能被本应用中的任何部分使用。(你也可以在组件级别指定服务提供商,这通常是首选方式。)bootstrap —— 应用的主视图,称为根组件。它是应用中所有其它视图的宿主。只有根模块才应该设置这个 bootstrap 属性。通俗一点讲即:声明哪些组件、指令、管道属于该模块;公开某些类,以便其它的组件模板可以使用它们;导入其它模块,从其它模块中获得本模块所需的组件、指令和管道;在应用程序级提供服务,以便应用中的任何组件都能使用它。如:import { BrowserModule } from ‘@angular/platform-browser’;import { BrowserAnimationsModule } from ‘@angular/platform-browser/animations’;import { NgModule } from ‘@angular/core’;import { HttpModule, Http } from ‘@angular/http’;import { RouterModule } from ‘@angular/router’;import { appRoutes } from ‘./app.routes’;import { AppcontentModule } from ‘./appcontent/appcontent.module’;import { AppComponent } from ‘./app.component’;import { CommonService } from ‘./shared/common.service’;@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule, HttpModule, RouterModule.forRoot(appRoutes), AppcontentModule ], providers: [CommonService], bootstrap: [AppComponent]})export class AppModule { }特性模块我们一般把自定义的其他模块叫特性模块。随着应用的扩大,所有的事情都在一个模块中完成难免会变乱,我们就会想着把统分为多个模块,每个模块都只做各自的事情而互不干扰,用根模块来引导程序并管理所有子模块即通过路由定向以及为它们提供全局配置与服务实例。实现方式如下:根模块负责全局的路由。核心模块负责全局服务,也可以定义一些只在根模块中使用的组件等,并只能由根模块引入一次,不再导出。共享模块不做服务的提供,而是定义全局共享的组件等,以及帮助子模块导入系统模块,让子模块只需要导入此共享模块就够了。子模块内部可以细分自己的子路由到具体的子组件,以及提供自己的服务等。除了页面入口模块(即除了根模块外的具体业务模块)之外的其他子模块均考虑写成惰性加载的模块,以提升页面引导的速度减少性能浪费。当需要一个比较通用的全局服务时,可以将其加入CoreModule,也可以再创建一个仅被根模块引入的特性模块。进一步的,甚至可以将此模块发布到npm,这就需要更强的编码能力和技术积累了。Angular2模块的目录和目录结构一般如下:每个模块一个应该单独的文件夹模块内功能相关或相近每个模块最好都有单独的路由定义 ...

January 7, 2019 · 1 min · jiezi

【angularjs】如何使用$sce控制代码安全

最近在做关于angularjs的项目,后续可能会分享一些关于angularjs的一些小知识或者常见问题!今天分享的是关于angularjs如何使用$sce控制代码安全。问题场景:需求要求,点击按钮,要通过一个http网址打开页面,但是这个页面要使用弹框而不能新开一个窗口,这时候我就想到了iframe,大致代码如下:在controller里面写到:var url = ‘http://www.baidu.com’;$scope.myUrl = url;html如下:<iframe ng-src="{{myUrl}}" height=“500” width=“800” scrolling=“yes”></iframe>这时候问题来了,打开页面是空白的,然后控制台打印了“Error:[$interpolate:interr]”的类型错误:获取页面元素也发现iframe里面的src值没有获取到:在js里面打印url是有值的,在html里面用其他标签获取url也是有值的,所以最后把问题定位在了iframe上面,最后发现,angularjs是没办法直接传url给iframe的src里面的,需要经过一定的安全过滤。有两种办法可以解决:1、最直接最简单的方法,就是直接在url值外层套一个$sce.trustAsResourceUrl(),在controller里面的js改写为:var url = ‘http://www.baidu.com’;$scope.myUrl = $sce.trustAsResourceUrl(url);2、使用过滤器:新增过滤器angular.module(‘app’, []).filter(‘iframeSrc’, [’$sce’, function($sce) { return function(val) { return $sce.trustAsResourceUrl(val); };}])在html里面使用过滤器:<iframe ng-src="{{myUrl | iframeSrc}}" height=“500” width=“800” scrolling=“yes”></iframe>以上两种方法都可以解决src的取值问题,关键点就在于$sce.trustAsResourceUrl()上面。$sce.trustAsResourceUrl()是angularjs中防止用户注入url的一个安全检查方法。································································································································由此就引申出关于$sce的一些知识:(1)在angularJs中为了避免安全漏洞,一些ng-src或者ng-include都会进行安全校验,因此常常会遇到一个iframe中的ng-src无法使用的问题(2)什么是SCE?SCE,即strict contextual escaping,字面上我理解为严格的上下文访问之类的意思,不知道是否正确,但是从SCE的工作机制上看,他做的就是一种安全检查,将相关语境中存在的注入或者跨站攻击等风险干掉,从而将一些特定的内容(包括html,url,css,js,resourceUrl)标记过信任内容再提供给页面使用。这些默认都是不被angularjs信任的,若是使用过程中有像我这样的需求的话,就可以使用$sce来进行授权信任。(3)常用的$sce方法和使用场景:$sce.trustAs(type,name); //type包括:$sce.HTML,$sce.CSS,$sce.URL,$sce.RESOURCE_URL,$sce.JS,比如trsutAsUrl其实调用的是trsutAs($sce.URL,“xxxx”);$sce.trustAsHtml(value);$sce.trustAsUrl(value); //a标签中的href , img标签中的src$sce.trustAsResourceUrl(value); //ng-include,src或者ngSrc,比如iframe或者Object$sce.trustAsJs(value);后面奉上官网的例子ng-bind-html:<!DOCTYPE html><html><head> <title></title> <script src=“http://apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script></head><body ng-app=“mySceApp”> <div ng-controller=“AppController”> <i ng-bind-html=“explicitlyTrustedHtml” id=“explicitlyTrustedHtml”></i> </div> <script type=“text/javascript”> angular.module(‘mySceApp’,[]) .controller(‘AppController’, [’$scope’, ‘$sce’, function($scope, $sce) { $scope.explicitlyTrustedHtml = $sce.trustAsHtml( ‘<span onmouseover=“this.textContent=&quot;Explicitly trusted HTML bypasses ’ + ‘sanitization.&quot;">Hover over this text.</span>’); }]); </script></body></html> ...

December 28, 2018 · 1 min · jiezi

angular2 控制视图的封装模式

angular2 控制视图的封装模式为什么我想要分享控制视图的封装模式呢?主要是我们angular的项目大多数都会去引入一个UI组件,但往往因为需求和关系我们会去修改UI组件的样式。这时,因为有些人不是很了解View encapsulation里面的属性,往往会直接在全局的style.js里面添加全局样式,等项目越来越大,就会出现一些不知名的bug和维护起来变得困难。如果你运用好视图的封装模式,会帮你解决好很多的问题。 一般来说组件的 CSS 样式被封装进了自己的视图中,而不会影响到应用程序的其它部分。通过在组件的元数据上设置视图封装模式,你可以分别控制每个组件的封装模式。Angular2有三种样式封装模式:ViewEncapsulation.Native - 使用原生的Shadow Dom。ViewEncapsulation.Emulated - angular2的默认值,通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,在标签上增加标识,来固定样式的作用域,以达到把 CSS 样式局限在组件视图中的目的。ViewEncapsulation.None - 没有Shadow Dom,样式没有封装, Angular 会把 CSS 添加到全局样式中。而不会应用上前面讨论过的那些作用域规则、隔离和保护等。 从本质上来说,这跟把组件的样式直接放进 HTML 是一样的。在 ViewEncapsulation.Emulated下 的 Angular 应用的 DOM 树中,每个 DOM 元素都被加上了一些额外的属性。<hero-details _nghost-pmm-5> <h2 _ngcontent-pmm-5>Mister Fantastic</h2> <hero-team _ngcontent-pmm-5 _nghost-pmm-6> <h3 _ngcontent-pmm-6>Team</h3> </hero-team></hero-detail>生成出的属性分为两种:1、一个元素在原生封装方式下可能是 Shadow DOM 的宿主,在这里被自动添加上一个 _nghost 属性。 这是组件宿主元素的典型情况。2、组件视图中的每一个元素,都有一个 _ngcontent 属性,它会标记出该元素是哪个宿主的模拟 Shadow DOM。用法如下:import { Component, OnInit, ViewEncapsulation } from ‘@angular/core’;@Component({ selector: ‘app-factor_analysi’, templateUrl: ‘./factor_analysis.component.html’, styleUrls: [’./factor_analysis.component.scss’], providers: [factor_analysis_api], encapsulation: ViewEncapsulation.None}) ...

December 27, 2018 · 1 min · jiezi

ionic3 toastController使用封装

说明toastController是ionic官方提供的消息提示框组件,用于给用户操作后结果反馈和提示。官网地址:https://ionicframework.com/do…如下是默认的样式,项目中使用则需要去改变很多样式则需要讲解一些入参。2. 使用控制台运行命令,创建服务ionic g provicer ToastService编写程序import { Injectable } from ‘@angular/core’;/导入ionic消息提示框模块ToastController/import { ToastController } from “ionic-angular”;/ Generated class for the ToastServiceProvider provider. See https://angular.io/guide/dependency-injection for more info on providers and Angular DI.*/@Injectable()export class ToastServiceProvider {//自己定义的三种消息框样式 errorCss=‘errorToast’ generalCss=‘generalToast’ successCss=‘successToast’/构造函数引入/ constructor(public toast:ToastController) { console.log(‘Hello ToastServiceProvider Provider’); } /* * 错误信息提示框 * @param message 消息 / errorToast(message:any){ this.presentToast(message,this.errorCss); } /* * 普通信息提示框 * @param message 消息 / generalToast(message:any){ this.presentToast(message,this.generalCss); } /* * 成功信息提示框 * @param message / successToast(message:any){ this.presentToast(message,this.successCss); } /* * * @param message需要展示的信息 * @param css 自定义的背景颜色 */ presentToast(message:any,css:string) { let toast = this.toast.create({ message: message,//提示消息内容 duration: 3000,//显示时长,单位毫秒 position: ‘bottom’,//消息框出现的位置,bottom就是底端的意思,自然就有top和中间了 showCloseButton:true,//是否有关闭按钮,true就是有 cssClass:css,//自己给消息框定义的样式,css样式名称 closeButtonText:‘关闭’//关闭按钮上的文字 }); toast.onDidDismiss(() => { console.log(‘Dismissed toast’); }); toast.present();//出发消息提示框 }}对应的css文件.errorToast{ //.toast-message{ // color: #a94442; //} .toast-wrapper { //background: #eba6ac; background: #f53d3d; }}.generalToast{ .toast-wrapper { background: #488aff; }}.successToast{ .toast-wrapper { background: #32db64; }}3. 导入app.module.ts中声明服务,那个页面需要使用,引用即可。4.效果成功提示消息:失败提示:

December 21, 2018 · 1 min · jiezi

angularjs http与后台交互

1.描述无论是使用angularjs做前端或是结合ionic混合开发移动端开发app都需要与后台进行交互,而angular给我提供了httpModule模块供我们使用。今天就展现一个http的封装和使用的一个具体流程。2. HttpModule引入找到app.module.ts文件import { NgModule, ErrorHandler } from ‘@angular/core’;import { BrowserModule } from ‘@angular/platform-browser’;import { IonicApp, IonicModule, IonicErrorHandler } from ‘ionic-angular’;import { MyApp } from ‘./app.component’;import { LoginPage } from “../pages/login/login”;/引入HttpClientModule模块/import { HttpClientModule } from “@angular/common/http”;import { RequestServiceProvider } from “../providers/request-service/request-service”;import { StatusBar } from ‘@ionic-native/status-bar’;import { SplashScreen } from ‘@ionic-native/splash-screen’;@NgModule({ declarations: [ MyApp, LoginPage, ], imports: [ BrowserModule, /* 导入模块 / HttpClientModule, IonicModule.forRoot(MyApp,{ tabsHideOnSubPages:’true’, backButtonText:’’ }) ], bootstrap: [IonicApp], entryComponents: [ MyApp, LoginPage, ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, RequestServiceProvider, ]})export class AppModule {}按照自己的项目导入HttpClientModule模块即可,我导入其他组件,不用考虑。3.创建服务ionic g provider RequestService执行完成后则会出现如下文件4.封装服务/导入http相关/import { HttpClient,HttpHeaders } from ‘@angular/common/http’;import { Injectable } from ‘@angular/core’;import {Observable} from “rxjs”;/ Generated class for the RequestServiceProvider provider. See https://angular.io/guide/dependency-injection for more info on providers and Angular DI./@Injectable()export class RequestServiceProvider { /** 讲基础路径提取说出来,配置ip和端口时只需要在这修改 / //basePath:string=‘http://10.4.0.205:8081’ reserveBasePath:string=‘http://10.6.254.110:8081’ basePath=this.reserveBasePath; /* 封装固定的消息头相关 / private headers = new HttpHeaders({‘Content-Type’: ‘application/json’}) // private headers = new HttpHeaders({‘Access-Control-Allow-Origin’:’’});/初始化http变量/ constructor(public http: HttpClient) { console.log(‘Hello RequestServiceProvider Provider’); } /* 给外界提供了四个基础的方法只需要传入uri和data即可 / get(req:any):Observable<any> { return this.http.get(this.basePath+req.uri,{headers:this.headers}); } post(req:any):Observable<any>{ return this.http.post(this.basePath+req.uri,req.data,{headers:this.headers}); } put(req:any):Observable<any>{ return this.http.put(this.basePath+req.uri,req.data,{headers:this.headers}); } delete(req:any):Observable<any>{ return this.http.delete(this.basePath+req.uri,{headers:this.headers}); }}5.导入声明封装服务找到app.module.ts文件和第一部类似import { NgModule, ErrorHandler } from ‘@angular/core’;import { BrowserModule } from ‘@angular/platform-browser’;import { IonicApp, IonicModule, IonicErrorHandler } from ‘ionic-angular’;import { MyApp } from ‘./app.component’;import { LoginPage } from “../pages/login/login”;/引入HttpClientModule模块/import { HttpClientModule } from “@angular/common/http”;/导入自定的服务/import { RequestServiceProvider } from “../providers/request-service/request-service”;import { StatusBar } from ‘@ionic-native/status-bar’;import { SplashScreen } from ‘@ionic-native/splash-screen’;@NgModule({ declarations: [ MyApp, LoginPage, ], imports: [ BrowserModule, / 导入模块 / HttpClientModule, IonicModule.forRoot(MyApp,{ tabsHideOnSubPages:’true’, backButtonText:’’ }) ], bootstrap: [IonicApp], entryComponents: [ MyApp, LoginPage, ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, / 声明服务 / RequestServiceProvider, ]})export class AppModule {}6.使用服务找到自己的页面所对应的ts文件如下面代码一样import { Component } from ‘@angular/core’;import { IonicPage, NavController, NavParams } from ‘ionic-angular’;/导入声明/import {RequestServiceProvider} from “../../providers/request-service/request-service”;/ * Generated class for the LoginPage page. * * See https://ionicframework.com/docs/components/#navigation for more info on * Ionic pages and navigation. /@IonicPage()@Component({ selector: ‘page-login’, templateUrl: ’login.html’,})export class LoginPage { title:string = ‘登录’ promptMessage:string = ’’ user={ username:’’, password:’’ } req={ login:{ uri:’/user/login’ } } constructor(public navCtrl: NavController, public navParams: NavParams, /* 初始化服务对象 / private requestService:RequestServiceProvider) { } ionViewDidLoad() { console.log(‘ionViewDidLoad LoginPage’); } login(){ /* 调用post方法,subscribe()方法可以出发请求,调用一次发送一次,调用多次发多次 */ this.requestService.post({uri:this.req.login.uri,data:user}).subscribe((res:any)=>{ console.log(res); if (res.code == 0){ this.promptMessage = res.message; } else { this.promptMessage = res.message; } }, error1 => { alert(JSON.stringify(error1)) }); } } ...

December 20, 2018 · 2 min · jiezi

解析Angularjs的$http异步删除数据及实例

这篇文章主要介绍了Angularjs的$http异步删除数据详解及实例的相关资料,这里提供实现思路及实现具体的方法,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。Angularjs的$http异步删除数据详解及实例有人会说删除这东西有什么可讲的,写个删除的service,controller调用一下不就完了。嗯…看起来是这样,但是具体实现起来真的有这么简单吗?首先有以下几个坑怎么确定数据是否删除成功?怎么同步视图的数据库的内容?1.思路1.实现方式一删除数据库中对应的内容,然后将$scope中的对应的内容splice2.实现方式二删除数据库中对应的内容,然后再reload一下数据(也就是再调用一次查询方法,这种消耗可想而知,并且还要保证先删除数据再查询)2.具体实现方式删除数据的service:用异步,返回promiseservice(‘deleteBlogService’,//删除博客 [’$rootScope’, ‘$http’, ‘$q’, function ($rootScope, $http, $q) { var result = {}; result.operate = function (blogId) { var deferred = $q.defer(); $http({ headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ },//欢迎加入前端全栈开发交流圈一起学习交流:864305860 url: $rootScope.$baseUrl + “/admin/blog/deleteBlogById”, method: ‘GET’, dataType: ‘json’, params: { id: blogId } }) .success(function (data) { deferred.resolve(data); console.log(“删除成功!”); }) .error(function () { deferred.reject(); alert(“删除失败!”) }); return deferred.promise; }; return result; }])//欢迎加入前端全栈开发交流圈一起学习交流:864305860controller里面注意事项要特别注意执行顺序:确保己经删除完成之后再去reload数据,不然会出来视图不更新/** * 删除博客 */ $scope.deleteBlog = function (blogId) { var deletePromise = deleteBlogService.operate(blogId); deletePromise.then(function (data) { if (data.status == 200) { var promise = getBlogListService.operate($scope.currentPage); promise.then(function (data) { $scope.blogs = data.blogs; $scope.pageCount = $scope.blogs.totalPages; });//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860 }//面向1-3年前端人员 });//帮助突破技术瓶颈,提升思维能力 };结语感谢您的观看,如有不足之处,欢迎批评指正。 ...

December 17, 2018 · 1 min · jiezi

避免并发的重复请求 for Angular

在项目的实际开发中偶然遇到了相同的GET请求被连续触发的问题,典型用例如CMS系统首页打开时导航栏需要加载栏目数据,页面中的栏目列表也同样请求该数据。当然,理想状态下可以要求导航栏先加载并缓存,然后其它组件从缓存中获取,然而实际上这些功能可能由不同的开发者编写,那么协调起来就麻烦一些了。而且越复杂的系统就更容易的出现这个问题,所以不得不解决一下了。最初遇到这个问题是在一个AngularJS(AngularJS1.6测试通过)项目中,所以先丢这个出来:/** * 这只是一个简单的例子,请自行扩展。 * 返回的值总是一个promise,这样就默默的拦截了重复的请求 * 注意:这里使用了本地缓存,这可能造成数据无法更新, * 而下一个例子则仅仅是过滤掉一个请求周期之内重复的请求 /function get(url) { var defer = $q.defer(); if (localStage.getItem(‘cachedRequest-’ + url) !== null) { if (localStage.getItem(‘cachedRequest-’ + url).then) { //then方法不是undefined那么这就是个promise对象,扔回去 return localStage.getItem(‘cachedRequest-’ + url); } else { //数据已经本地缓存了那就放到defer里面返回 defer.resolve(JSON.parse(localStage.getItem(‘cachedRequest-’ + url))); } } else { //不好解释,要打太多字…明白就好 var promise = $http.get(url).then(function(res){ localStage.setItem(‘cachedRequest-’ + url, JSON.stringify(res)); return defer.resolve(res); }); defer.resolve(promise); } return defer.promise();}Angular版本 (Angular6测试通过)/* * 这是最简代码,错误处理等是使用拦截器实现的 /import { Injectable } from ‘@angular/core’;import { throwError, Subject } from ‘rxjs’;import { HttpClient, HttpHeaders } from ‘@angular/common/http’;import { map } from ‘rxjs/operators’;//如果配置文件中设置了代理那么可以丢掉这个const BEServer = “http://localhost”;@Injectable({ providedIn: ‘root’})export class ApiRequestService { private apiSubjects = {}; constructor( private http: HttpClient ) { } private extractData(res: Response) { let body = res; return body || { }; } private buildUrl(url, params) { / 此处略去N行代码和迭代方法 / return url; } private getHttpOptions(type) { / 各种略 */ return {headers:{}}; } exec(type, url, data = null) { url = BEServer + url; let method = type.toLowerCase(); if (([‘get’, ‘post’, ‘put’, ‘delete’, ‘file’]).indexOf(method) < 0) { return throwError(‘Request method is invalid.’); } let httpOptions = this.getHttpOptions(method); if (method == ‘get’) { if (data) { url = this.buildUrl(url, data); } } if (method == ‘get’) { if (! this.apiSubjects[url]) { this.apiSubjects[url] = { subscribe: this.http[method](url, httpOptions).pipe(map(this.extractData)).subscribe(data => { this.apiSubjects[url].subject.next(data); //这个delete的处理感觉不顺,但是实测也找不到更好的办法 delete(this.apiSubjects[url]); }), subject: new Subject<Object>() }; } return this.apiSubjects[url].subject; } else if (method == ‘delete’) { return this.http[method](url, httpOptions).pipe(map(this.extractData)); } else { return this.http[method](url, data, httpOptions).pipe(map(this.extractData)); } }}//调用测试,不必要的代码全略掉instanceOfApiRequestService.exec(‘GET’, ‘/api/dashboard’).subscribe(data => { console.log(data);});instanceOfApiRequestService.exec(‘GET’, ‘/api/dashboard’).subscribe(data => { console.log(data);});instanceOfApiRequestService.exec(‘GET’, ‘/api/dashboard’).subscribe(data => { console.log(data);});//三次log都被触发,但是只有一次http请求。刚接触Angular6不久,不管是我这个想法本身有错误还是解决的方式有问题都请拍砖不要客气,只求大侠的砖头上绘制一下示例代码,不胜感激。 ...

November 5, 2018 · 2 min · jiezi