angular 4版本小知識點----方便自己看
- 使用 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);
}
}
}
相關文章
- 前端小知識點前端
- java小知識點Java
- 小知識點1
- js小知識點JS
- mongo 小知識點Go
- 浮點數小知識點
- ElasticSearch知識點小記Elasticsearch
- VEEAM的小知識點
- makefile 知識點小結
- 【JAVA】- 知識點小結Java
- promise知識點小結Promise
- 《 Angular高階程式設計(第4版)》之“Angular 基礎知識”Angular程式設計
- 自己整理的php面試知識點PHP面試
- react-native基本知識點(4/4)React
- Python小知識點隨筆Python
- 前端小知識10點(2019.5.2)前端
- 前端小知識10點(2019.4.14)前端
- node知識點小結(一)
- 前端(js html)小知識點前端JSHTML
- 前端小知識點彙總前端
- 記錄的小知識點
- 前端小知識--從Javascript閉包看let前端JavaScript
- Laravel 小知識點之 HtmlString 類LaravelHTML
- 雜湊表知識點小結
- 2021/06/02知識點小結
- 總結的小知識點(一)
- Qt小知識4.QWindow和QWidgetQT
- IdentityServer4系列 | 初識基礎知識點IDEServer
- Python 中不易懂的小知識點Python
- 前端知識點小結--node、express、mongodb前端ExpressMongoDB
- 彙編必知小知識點及常用debug命令
- 【Emmet 的使用手冊(知識點超全版本)】
- kubebuilder實戰之八:知識點小記UI
- 知識點
- 爬知識星球,製作自己的知識倉庫
- react篇lesson4(react-router)知識點React
- baota小知識
- 知識小匯