- 使用 Angular CLI 创建一个名为 heroes 的新组件。
ng generate component hero-detail
这个命令会做这些事:
创建目录 src/app/hero-detail。
在这个目录中会生成四个文件:
- 作为组件样式的 CSS 文件。
- 作为组件模板的 HTML 文件。
- 存放组件类 HeroDetailComponent 的 TypeScript 文件。
- HeroDetailComponent 类的测试文件。
该命令还会把 HeroDetailComponent 添加到 src/app/app.module.ts 文件中 @NgModule 的 declarations 列表中。
- CLI 自动生成了三个元数据属性:
selector— 组件的选择器(CSS 元素选择器)
templateUrl— 组件模板文件的位置。
styleUrls— 组件私有 CSS 样式表文件的位置。
ngOnInit 是一个生命周期钩子,Angular 在创建完组件后很快就会调用 ngOnInit。这里是放置初始化逻辑的好地方 - *ngFor
*ngFor 是一个 Angular 的复写器(repeater)指令。 它会为列表中的每项数据复写它的宿主元素。
<li *ngFor="let hero of heroes">
- 添加 click 事件绑定
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
click 外面的圆括号会让 Angular 监听这个元素的 click 事件。
添加 click 事件处理器
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
-
使用 *ngIf 隐藏空白的详情
-
使用ngModel
[(ngModel)] 是 Angular 的双向数据绑定语法。
虽然 ngModel 是一个有效的 Angular 指令,不过它在默认情况下是不可用的。
它属于一个可选模块 FormsModule,你必须自行添加此模块才能使用该指令。
有些元数据位于 @Component 装饰器中,你会把它加到组件类上。 另一些关键性的元数据位于 @NgModule 装饰器中。
最重要的 @NgModule 装饰器位于顶级类 AppModule 上。
Angular CLI 在创建项目的时候就在 src/app/app.module.ts 中生成了一个 AppModule 类。 这里也就是你要添加 FormsModule 的地方。
导入 FormsModule
打开 AppModule (app.module.ts) 并从 @angular/forms 库中导入 FormsModule 符号。
import { FormsModule } from '@angular/forms';
app.module.ts文件
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms'//插入的FormModule
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
@NgModule({
declarations: [
AppComponent,
HeroesComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
- 使用 Angular CLI 创建一个名叫 hero 的服务。
ng generate service hero
- 添加 AppRoutingModule
在 Angular 中,最好在一个独立的顶级模块中加载和配置路由器,它专注于路由功能,然后由根模块 AppModule 导入它。
按照惯例,这个模块类的名字叫做 AppRoutingModule,并且位于 src/app 下的 app-routing.module.ts 文件中。
ng generate module app-routing --flat --module=app
–flat 把这个文件放进了 src/app 中,而不是单独的目录中。
–module=app 告诉 CLI 把它注册到 AppModule 的 imports 数组中。
- 内存 Web API
npm install angular-in-memory-web-api --save
导入 HttpClientInMemoryWebApiModule 和 InMemoryDataService 类,在/app.module.ts
- angular 4 中@Input()、@Output()父子组件之间的传值
@Input()、@Output()父子组件之间的传值
1、child组件内有一个Output customClick的事件,事件的数据类型是number;
2、child组件内有一个onClicked方法,这个是应用在html中button控件的click事件中,通过(click)="onClicked()"进行方法绑定;
3、parent组件内有一个public的属性showMsg,Angular的ts类默认不写关键字就是public;
4、parent组件内有一个onCustomClicked方法,这个也是要用在html中的,是和child组件内的output标记的customClick事件进行绑定的;
5、步骤为child的html的button按钮被点击->onClicked方法被调用->emit(99)触发customClick->Angular通过Output数据流识别出发生变化并通知parent的html中(customClick)->onCustomClicked(event)被调用, event)被调用,event)被调用,event为数据99->改变了showMsg属性值->影响到了parent的html中的显示由1变为99
————————————————
版权声明:本文为CSDN博主「FlyWine」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wf19930209/article/details/79349164 - angular 4 中共享服务在多个组件中数据通信的示例
不同组件中操作统一组数据,不论哪个组件对数据进行了操作,其他组件中立马看到效果https://www.jb51.net/article/137456.htm
如下图:
- Angular 响应式Form 表单
①表单验证
目前 Angular 支持的内建 validators 如下:
1.required - 设置表单控件值是非空的。
2.email - 设置表单控件值的格式是 email。
3.minlength - 设置表单控件值的最小长度。
4.maxlength - 设置表单控件值的最大长度。
5.pattern - 设置表单控件的值需匹配 pattern 对应的模式。
如:
this.itFormGroup = this.fb.group({
lxnameCtr: ["", [Validators.required]],
remarkCtr: [""]
});
createForm() {
this.heroForm = this.fb. group({
name: ['', Validators.required ],
street: '',
city: '',
state: '',
zip: '',
power: '',
sidekick: ''
});
}
FormBuilder.group 是一个用来创建 FormGroup 的工厂方法,它接受一个对象,对象的键和值分别是
FormControl 的名字和它的定义
更多的可以参考网址:https://segmentfault.com/a/1190000010064866
②响应式表单的更新值 setValue、patchValue
创建响应式表单是需要在app.module.ts中导入ReactiveFormsModule模块
- patchValue
借助 patchValue() ,你可以通过提供一个只包含要更新的控件的键值对象来把值赋给 FormGroup 中的指定
控件。patchValue() 不会检查缺失的控件值,并且不会抛出有用的错误信息。
this.heroForm.patchValue({
name: this.hero.name
});
- setValue
跟 patchValue 有一点不一样,当我们提供一个 FromGroup 中并不存在的字段时,会抛出一个错误;setValue() 方法会在赋值给任何表单控件之前先检查数据对象的值。
this.heroForm.setValue({
name: this.hero.name,
address: this.hero.addresses[0] || new Address()
});
- navigator.userAgent.toLowerCase()
toLowerCase()方法:用于把字符串转化为小写;语法:stringObject.toLowerCase();
toUpperCase() 方法:用于把字符串转换为大写;语法:stringObject.toUpperCase();
navigator对象:包含有关浏览器的信息,所有浏览器都支持该对象
navigator属性:
Navigator 对象方法:
我们这里所用的userAgent属性是一个只读属性,声明了浏览器用于HTTP请求的用户代理头的值
所有主要浏览器都支持 userAgent 属性
-
判断移动端设备,区分android,iphone,ipad和其它
var ua = navigator.userAgent.toLowerCase();
if(ua.match(/android/i)) == "android") {
alert("android");
}
if(ua.match(/iPhone/i)) == "iPhone") {
alert("iPhone");
}
if(ua.match(/iPad/i)) == "iPad") {
alert("iPad");
}
- 判断移动端用的是不是特定类型的浏览器,比如新浪weibo客户端内置浏览器,qq客户端内置浏览器(而非qq浏览器),微信内置浏览器(并且区分版本是否大于等于6.0.2)(特定类型浏览器可能会存在,无法下载,无法跳转和自己的客户端app的特定协议等等,所以需要区分)(由于微信在6.0.2的时候做了新的策略,使得微信的分享功能在新版本变得不一样,为了兼容新旧版本,这里做了区分操作)
新浪weibo客户端返回1,qq客户端返回2,微信小于6.0.2版本返回3,微信大于等于6.0.2版本返回4,其它返回0
var ua = navigator.userAgent.toLowerCase();
if(ua.match(/weibo/i) == "weibo") {
return 1;
} else if(ua.indexOf('qq/') != -1) {
return 2;
} else if(ua.match(/MicroMessenger/i) == "micromessenger") {
var v_weixin = ua.split('micromessenger')[1];
v_weixin = v_weixin.substring(1, 6);
v_weixin = v_weixin.split(' ')[0];
if(v_weixin.split('.').length == 2) {
v_weixin = v_weixin + '.0';
}
if(v_weixin < '6.0.2') {
return 3;
} else {
return 4;
}
} else {
return 0;
}
- 把他们统一起来判断登陆端是pc还是手机
var sUserAgent = navigator.userAgent.toLowerCase();
var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
var bIsMidp = sUserAgent.match(/midp/i) == "midp";
var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
var bIsAndroid = sUserAgent.match(/android/i) == "android";
var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
if(bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
alert("您是手机登录");
} else {
alert("您是电脑登录");
}
- 判断是否是ie浏览器
isIE(): boolean {
return !(/(msie\s|trident.*rv:)([\w.]+)/.exec(navigator.userAgent.toLowerCase()) == null);
}
或者:
var isIE = /mise|trident/g.test(window.navigator.userAgent.toLowerCase());
网上更全的:
function isIE(){
var userAgent = navigator.userAgent,
rMsie = /(msie\s|trident.*rv:)([\w.]+)/,
rFirefox = /(firefox)\/([\w.]+)/,
rOpera = /(opera).+version\/([\w.]+)/,
rChrome = /(chrome)\/([\w.]+)/,
rSafari = /version\/([\w.]+).*(safari)/;
var browser;
var version;
var ua = userAgent.toLowerCase();
var match = rMsie.exec(ua);
if (match != null) {
ieVersion = { browser : "IE", version : match[2] || "0" };
return true;
}
var match = rFirefox.exec(ua);
if (match != null) {
var ffVersion = { browser : match[1] || "", version : match[2] || "0" };
return false;
}
var match = rOpera.exec(ua);
if (match != null) {
var opVersion = { browser : match[1] || "", version : match[2] || "0" };
return false;
}
var match = rChrome.exec(ua);
if (match != null) {
var chVersion = { browser : match[1] || "", version : match[2] || "0" };
return false;
}
var match = rSafari.exec(ua);
if (match != null) {
var sfVersion = { browser : match[2] || "", version : match[1] || "0" };
return false;
}
if (match != null) {
var ohterVersion = { browser : "", version : "0" };
return false;
}
}
- 创建动态组件并使用
先创建一个弹框组件ggMsg.component
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { MyhttpService } from "../myhttp.service";
declare var $: any;
@Component({
template: `
<div id="toast-container" aria-live="polite" role="alert">
<div class="toast toast-info" >
<button class="toast-close-button" role="button" (click)="clsoe()">×</button>
<div class="toast-title">提示信息</div>
<div class="toast-message" id="toast-message">
<span style="line-height: 50px;">未读公告:</span><span style="line-height: 50px;">{{wdcount}}</span>
<span style="line-height: 50px;">已读公告:</span><span style="line-height: 50px;">{{ydcount}}</span>
</div>
</div>
</div>
`,
styles: [
".toast-title{font-weight:bold;font-size:16px;}",
".toast-message{-ms-word-wrap:break-word;word-wrap:break-word}",
".toast-message a,.toast-message label{color:#fff}",
".toast-message a:hover{color:#ccc;text-decoration:none}",
".toast-close-button{position:relative;right:-0.3em;top:-0.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}",
"#toast-container{position:fixed;z-index:999999;right:12px;bottom:-150px;}",
"#toast-container>div{margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}",
],
providers: [MyhttpService]
})
export class GgmsgComponent implements OnInit, OnDestroy {
@Input() data: any
@Output() outer: EventEmitter<any> = new EventEmitter<any>();
ngOnInit() {
}
ngOnDestroy() {
}
clsoe(): void {
this.outer.emit({
type: "closeggmsg" //约定一个,这里告诉父组件要关闭了
});
}
}
@Input():父组件将数据传过来,子组件接收
@Output():
将创建的动态组件放到存放所有动态组件文件中
我这里是dynamaner.component.ts
import {GgmsgComponent} from "./ggMsg.component";
const CompMap = {
'ggmsg':GgmsgComponent,
}
const DynaCompArr = [
GgmsgComponent
]
export {
CompMap,
DynaCompArr,
}
创建组件容器
在 Angular 中放置组件的地方称为 container 容器。这里我放置在app.component组件中,为了方便以后每个页面都显示
import {Component, ViewContainerRef, ViewChild, ComponentFactoryResolver} from '@angular/core';
import {NavigationEnd, NavigationError, NavigationStart, Router} from "@angular/router";
import {CompMap} from "./dynacomponent/dynamaner.component"; //将存放动态组件的文件引进来
<div class="pubMsg">
<ng-container #pubMsgRoom></ng-container>
</div>
export class AppComponent {
@ViewChild("pubMsgRoom", {read: ViewContainerRef}) rRoom: ViewContainerRef;
}
this.rRoom.remove(0); //清空组件(有没有都先清空)每次我们需要创建组件时,我们需要删除之前的视图,否则组件容器中会出现多个视图
const ggmsgcomponent : ComponentFactory= this.cfr.resolveComponentFactory(CompMap['ggmsg']); //**resolveComponentFactory()** 方法接受一个组件并返回如何创建组件的 **ComponentFactory** 实例。
const ggmsgcomp : ComponentRef = this.rRoom.createComponent(ggmsgcomponent); //我们调用容器的 **createComponent()** 方法,该方法内部将调用 **ComponentFactory** 实例的 **create()** 方法创建对应的组件,并将组件添加到我们的容器
ggmsgcomp.instance.data = {}; //我们已经能获取新组件的引用,即可以我们可以设置组件的输入类型
ggmsgcomp.instance.outer.subscribe((resp) => {} //同样我们也可以订阅组件的输出属性
完整示例
ggMsg.component.ts
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { MyhttpService } from "../myhttp.service";
declare var $: any;
@Component({
template: `
<div id="toast-container" aria-live="polite" role="alert">
<div class="toast toast-info" >
<button class="toast-close-button" role="button" (click)="clsoe()">×</button>
<div class="toast-title">提示信息</div>
<div class="toast-message" id="toast-message">
<span style="line-height: 50px;">未读公告:</span><span style="line-height: 50px;">{{wdcount}}</span>
<span style="line-height: 50px;">已读公告:</span><span style="line-height: 50px;">{{ydcount}}</span>
</div>
</div>
</div>
`,
styles: [
".toast-title{font-weight:bold;font-size:16px;}",
".toast-message{-ms-word-wrap:break-word;word-wrap:break-word}",
".toast-message a,.toast-message label{color:#fff}",
".toast-message a:hover{color:#ccc;text-decoration:none}",
".toast-close-button{position:relative;right:-0.3em;top:-0.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}",
"#toast-container{position:fixed;z-index:999999;right:12px;bottom:-150px;}",
"#toast-container>div{margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}",
],
providers: [MyhttpService]
})
export class GgmsgComponent implements OnInit, OnDestroy {
loginuseruuid: string = "";
//已读未读
wdcount: string = "--";
ydcount: string = "--";
tmid: any;
@Input() data: any
@Output() outer: EventEmitter<any> = new EventEmitter<any>();
constructor(private itservice: MyhttpService) {
}
ngOnInit() {
clearTimeout(this.tmid);
this.getLogmsg();
}
ngOnDestroy() {
clearTimeout(this.tmid);
}
clsoe(): void {
this.outer.emit({
type: "closeggmsg"
});
}
getLogmsg(): void {
this.itservice.requstUrl("security_client/auth/getLoginUser", null).subscribe(resp => {
const retflag = resp.result;
if (retflag == "success") {
this.loginuseruuid = resp.rows[0].user_uuid;
this.getMsg();
}
});
}
getMsg() {
this.itservice.requstUrl("owner_client/owner/OaGwtz/JsrUuidByGwtzZtCount", {
jsruuid: this.loginuseruuid,
randomv: Math.random(),
}).subscribe(resp => {
const retflag = resp.result;
if (retflag == "success") {
$("#toast-container").stop();
$("#toast-container").animate({
bottom: '10px'
}, "slow");
const rowobj = resp.rows;
this.wdcount = rowobj.wck;
this.ydcount = rowobj.yck;
clearTimeout(this.tmid)
this.tmid = setTimeout(function () {
$("#toast-container").fadeOut()
}, 5000);
}
});
}
}
app.component.ts
import {Component, ViewContainerRef, ViewChild, ComponentFactoryResolver} from '@angular/core';
import {NavigationEnd, NavigationError, NavigationStart, Router} from "@angular/router";
import {LocalStorageService} from "./local-storage-service";
import {CommService} from "./comm-service";
import {CompMap} from "./dynacomponent/dynamaner.component";
declare var $: any;
@Component({
selector: 'app-root',
template: `
<!--Metrnic提供的三个点的动画-->
<div [class.page-spinner-bar]="hasanima">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<div class="main_out">
<router-outlet></router-outlet>
<div id="myMask"></div>
<div class="pubMsg">
<ng-container #pubMsgRoom></ng-container>
</div>
</div>
`,
styles: [
".page-spinner-bar>div{margin: 0 5px;width: 18px;height: 18px;background: orange;border-radius: 100% !important;display: inline-block;-webkit-animation: bounceDelay 1.4s infinite ease-in-out;animation: bounceDelay 1.4s infinite ease-in-out;-webkit-animation-fill-mode: both;animation-fill-mode: both;}",
".page-spinner-bar .bounce1{-webkit-animation-delay: -0.32s;animation-delay: -0.32s;}",
".page-spinner-bar .bounce2{-webkit-animation-delay: -0.16s;animation-delay: -0.16s;}",
".main_out{position: relative}",
'#myMask{display: none;position:absolute;left: 0px;top: 0px;z-index: 1000;background-color: grey;opacity: .1}',
".pubMsg{position:absolute;right:5px;bottom:5px;width:auto;height:auto}",
],
})
export class AppComponent {
hasanima: boolean = false;
setTm: any;
@ViewChild("pubMsgRoom", {read: ViewContainerRef}) rRoom: ViewContainerRef;
constructor(private router: Router,
private localstorage: LocalStorageService,
private commservice: CommService,
private cfr: ComponentFactoryResolver) {
this.router.events.subscribe(event => {
//路由开始切换
if (event instanceof NavigationStart) {
this.hasanima = true;
//显示三个点
const token = this.commservice.getToken();
const localtoken = this.localstorage.get("token");
const url = event.url;
if (!token) { //刷新操作 或者 第一次进入系统登入页面
if (url == "/" || url == "/login") {
//第一次进入系统登入页面不处理
} else {
if (!localtoken) {
//清空缓存后刷新操作
this.router.navigate(["login"]);
} else {
//简单的刷新操作
this.commservice.setToken(localtoken);
clearInterval(this.setTm);
this.setTm = setTimeout(() => {
this.createComponent(url);
}, 2000)
}
}
} else {
this.createComponent(url);
}
}
//路由切换结束
if (event instanceof NavigationEnd) {
//隐藏三个点
this.hasanima = false;
}
//路由切换出错
if (event instanceof NavigationError) {
this.hasanima = false;
console.log('路由切换出错');
}
});
}
createComponent(url: any) {
// 公告的提示信息
if (url == "/taskpanelM/workcenter/mygg") {
this.rRoom.remove(0);
const ggmsgcomponent = this.cfr.resolveComponentFactory(CompMap['ggmsg']);
const ggmsgcomp = this.rRoom.createComponent(ggmsgcomponent);
ggmsgcomp.instance.data = {};
ggmsgcomp.instance.outer.subscribe((resp) => {
const typev = resp.type;
if (typev == "closeggmsg") {
this.rRoom.remove(0);
}
});
} else {
this.rRoom.remove(0);
}
}
}