日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

投影元素直接隔离_Angular ngcontent 内容投影

發(fā)布時間:2024/1/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 投影元素直接隔离_Angular ngcontent 内容投影 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

內(nèi)容投影和ng-content是可以讓我們最大程度構(gòu)建可重用組件的Angular功能之一。我們來構(gòu)造一個小組件,一個Font Awesomne輸入框。我們設(shè)計這個組件的目標(biāo)是為了構(gòu)造一個帶有圖標(biāo)的文本框。

最終的樣子如圖所示:

ng-content

不使用ng-content的話會遇到什么問題?先來嘗試下不用內(nèi)容投影的話,我們的組件會遇到什么問題。

首先看模板:

class="fa"?[ngClass]="classes">
????#input
????(focus)="inputFocus?=?true"
????(blur)="inputFocus?=?false"
????(keyup)="value.emit(input.value)"
/>
用classes對象來控制展示的圖標(biāo),然后用inputFocus獲得焦點進入input,通過組件的HostBinding來給組件應(yīng)用外邊框。import?{Component,?EventEmitter,?HostBinding,?Input,?OnInit,?Output}?from?'@angular/core';

({
????selector:?'app-fa-input',
????templateUrl:?'./fa-input.component.html',
????styleUrls:?['./fa-input.component.scss']
})
export?class?FaInputComponent?implements?OnInit?{
????()?icon:?string;
????()?value?=?new?EventEmitter<string>();
????inputFocus:?boolean?=?false;
????get?classes()?{
????????const?cssClasses?=?{
????????????fa:?true,
????????}
????????cssClasses['fa'?+?this.icon]?=?true;
????????return?cssClasses;
????}
????('class.focus')
????get?focus()?{
????????console.log(this.inputFocus);
????????return?this.inputFocus;
????}
}

// css部分:
:host{
????border:?1px?solid?grey;
}
input{
????border:?none;
????outline:?none;
}
:host(.focus)?{
????border:?1px?solid?blue;
}
看樣式文件可以知道,組件內(nèi)部的input元素被移除了自帶的樣式。但我們給宿主元素加上了邊框,讓組件看起來像原生的html input元素。當(dāng)input獲取到焦點的時候,通過將.focus類添加到宿主元素來模擬輸入框獲得焦點。然后看看如何使用這個組件:<div>
????<h1>FA?Inputh1>
????<i?class="fa?fa-heart">i>
????<app-fa-input?icon="envelope"?(value)="onNewValue($event)">app-fa-input>
div>
使用的時候,我們只需要向組件傳遞一個圖標(biāo)的名稱和接收input輸入值的函數(shù)即可。讓我們回顧下我們是如何設(shè)計這個組件的:
  • 作為組件公共api的一部分,我們有一個圖標(biāo)的屬性,該屬性定義了需要顯示的圖標(biāo)。

  • 組件有個名為value的自定義輸出事件,該事件在input元素輸入值發(fā)生變化時發(fā)出新的值。

  • 為了實現(xiàn)焦點功能,我們在組件內(nèi)部的input元素上綁定了blur和focus事件,通過@HostBinding在宿主元素上增加或刪除focuscss 類。

  • 這個組件可以滿足我們的需求。但是假設(shè)我們的需求發(fā)生了變更,我們馬上會陷入到新的麻煩中。

問題1:如何支持所有的input屬性?

我們的組件目前只是預(yù)定義了blur和focus屬性,那我們需要增加其他屬性,比如type,autocomplete、placeholder等,咋辦?那我們只能被迫去修改組件,使其可以支持這樣調(diào)用:input?icon="envelope"?type="text"?placeholder="email"?autocomplete="off"?(value)="onNewValue($event)">input>
在組件類中需要接收這些屬性:export?class?FaInputComponent?implements?OnInit?{
????//?...
????()?icon:?string;
????()?placeholder:?string;
????()?type:?string;
????()?autocomplete:?string;
????()?value?=?new?EventEmitter<string>();
????//?...
}
在模板中應(yīng)用:class="fa"?[ngClass]="classes">
????#input
????[placeholder]="placeholder"
????[type]="type"
????[autocomplete]="autocomplete"
????(focus)="inputFocus?=?true"
????(blur)="inputFocus?=?false"
????(keyup)="value.emit(input.value)"
/>
總而言之,我們要將需要處理的屬性,從消費處一直傳遞到組件內(nèi)部,然后在組件內(nèi)部從組件類到組件的模板。雖然是很麻煩,但這樣是可行的。但是,還有其他更棘手的問題。

問題2:如何和Angular Form 集成?

我們的組件是個帶圖標(biāo)的輸入框,那么它的作用不僅僅是展示,它的重點功能是表單的一個輸入元素,那么我們很可能需要和Angular Form集成,那么我們咋辦?還是如上面一樣,我們需要將表單的所有屬性,比如formControlName全部轉(zhuǎn)發(fā)到組件內(nèi)部。

問題3:檢測普通瀏覽器事件

我們想在組件上檢測到標(biāo)準(zhǔn)瀏覽器的dom事件怎么辦?比如keydown事件?也還是和上面一樣,我們需要通過組件內(nèi)部檢測然后在消費的地方去提供處理方法。也是可行的,但是好像我們的這個設(shè)計變得很不好,這樣慢慢的會很臃腫。這種設(shè)計不是個很好的解決方法。

問題4:自定義屬性

在構(gòu)建表單時,第三方系統(tǒng)可能希望填寫某些自定義的html數(shù)據(jù)屬性,比如類似于:data-之類的屬性用于其他作用。這會變得非常難辦,因為我們無法預(yù)知這些屬性的名字。那么,到目前為止,我們這種設(shè)計的關(guān)鍵問題是什么?關(guān)鍵問題是,我們將input元素隱藏到了組件模板中。在需要調(diào)用這個組件的地方和組件內(nèi)部形成了一個屏障。我們可以用內(nèi)容投影來重構(gòu)組件,以解決上面的問題。

使用ng-content內(nèi)容投影來重構(gòu)組件

讓我們重新設(shè)計組件Api,與其將輸入元素隱藏在組件內(nèi)部,不如將其提供為組件本身的內(nèi)容元素(content element)。那么我們在調(diào)用的地方應(yīng)該是這樣的:input?icon="envelope">
??????<input?type="text"?placeholder="email"/>input>
需要注意的是,我們這里的input元素不是存在于組件內(nèi)部,而是作為組件的html標(biāo)簽的一部分“內(nèi)容”。實際上,這種api在html標(biāo)準(zhǔn)元素中非常常見,比如選擇框:<select>
???<option?value=1>oneoption>
???<option?value=2>twooption>
select>
Angular Core 確實允許我們做同樣的事情。我們可以使用@ContentChild和@ContentChildren裝飾器來查詢組件HTML內(nèi)容的所標(biāo)記的內(nèi)容。并將其在內(nèi)部模板用作配置API。如果有必要,我們還可以將區(qū)域中的內(nèi)容直接用作組件的內(nèi)容。我們需要改造fa-input組件:<mat-icon>{{icon}}mat-icon>
<ng-content>ng-content>
為省事我這里使用了Angular Material的圖標(biāo)。import?{Component,?Input,?OnInit}?from?'@angular/core';
({
????selector:?'app-fa-input',
????templateUrl:?'./fa-input.component.html',
????styleUrls:?['./fa-input.component.scss']
})
export?class?FaInputComponent?{
????()?icon:?string;
????constructor()?{
????}
}
然后在其他組件中使用這個組件:input?icon="mail_outline">
????<input?type="text"?name="email"/>input>
頁面需要的元素都是OK的,我們這里的input也作為投影的內(nèi)容顯示在了組件的內(nèi)部。但是好像css沒有應(yīng)用上啊,那投影的元素的樣式如何處理?給投影的元素應(yīng)用css樣式;目前的樣式是定義在組件的樣式fa-input.component.scss之中:input{
????border:?none;
????outline:?none;
}
為啥不起作用?因為這些樣式位于鏈接到組件的樣式文件內(nèi),所以它們會被賦予一個運行時的屬性,這個屬性是該組件模板中所有html元素獨有的屬性。目前元素沒應(yīng)用上,我們可以給mat-icon寫個樣式來觀察下:.mat-icon{
????color:?red;
}

//?然后查看運行后的頁面,我們查看控制板板里面的css有:
.mat-icon[_ngcontent-hhd-c122]?{
????color:?red;
}

對應(yīng)的html有:

<app-fa-input?_ngcontent-hhd-c144?icon="mail_outline"?_nghost-hhd-c122?ng-reflect-icon="mail_outline">
????<mat-icon?_ngcontent-hhd-c122?role="img"?class="mat-icon">mail_outlinemat-icon>
????<input?_ngcontent-hhd-c144?type="text"?name="email">
app-fa-input>
我們可以看到,組件內(nèi)部的元素是擁有一個特定的屬性_ngcontent-hhd-c122,組件內(nèi)部鏈接的樣式也是有一個屬性_ngcontent-hhd-c122,這可以讓組件內(nèi)部的樣式不去干涉外部的元素。這是非常有用的。而input元素是外部投影進來的,所以它的屬性是_ngcontent-hhd-144,組件內(nèi)部的樣式是應(yīng)用不上去的,這就是為啥我們樣式不起作用的原因。我們需要加上::ng-deep來使樣式穿透。::ng-deep?input{
????border:?none;
????outline:?none;
}
這樣看起來是好的,但是有個隱患,我們在外層使用組件的地方加上一個input:

input?icon="mail_outline">
????<input?type="text"?name="email"/>input>
<input?type="text"?name="email"/>
好嘛,兩個input都被應(yīng)用上了樣式。甚至于我們?nèi)e的組件,不是父子組件,只是在這個頁面組件樹的其他組件中加上input,發(fā)現(xiàn)都應(yīng)用上了,看來這個樣式使用::ng-deep之后就變成了全局的css了。這樣會造成一些不可控的問題。如何解決呢?我們只需要在樣式前面加上:host來限定下即可::host?::ng-deep?input{
????border:?none;
????outline:?none;
}
這樣,發(fā)現(xiàn)只在投影到組件內(nèi)部的元素才會應(yīng)用這個樣式。所以,我們的需求是樣式既要應(yīng)用在當(dāng)前組件,也需要應(yīng)用到投影進來的元素。我們使用:host ::ng-deep就可以完美解決。再在控制臺下查看下樣式:[_nghost-unf-c122]?input?{
????border:?none;
????outline:?none;
}
正如我們所看到的,這個樣式的作用域依舊是當(dāng)前的組件內(nèi)容,但是他們也會穿透到投影到當(dāng)前組件的元素。如何與投影內(nèi)容交互?前面我們嘗試了將組件內(nèi)的樣式應(yīng)用到投影的元素中,現(xiàn)在我們嘗試下和投影的內(nèi)容進行交互。我們無法在ng-content標(biāo)簽上創(chuàng)建交互,也沒法在其上綁定事件監(jiān)聽。相對的,與投影內(nèi)容做交互做好的方法是以單獨的指令去操作。這里為了示例,我就不創(chuàng)建新的指令了,而是使用Angular Material的matInput指令。首先在將matInput掛到input元素上:input?icon="mail_outline">
????<input?matInput?type="text"?name="email"/>input>
然后在指令中通過@ContentChild修飾符獲取到投影進來的input元素:export?class?FaInputComponent?implements?OnInit?{
????@Input()?icon:?string;
????@ContentChild(MatInput)
????input:MatInput;
}
然后通過這個指令去模擬input獲取到焦點的過程:@HostBinding('class.focus')
get?focus()?{
????console.log('input',?this.input.focused);
????return?this.input???this.input.focused?:?false;
}

//?相對應(yīng)的css樣式:
.fa-input{
????padding:?3px?8px;
????display:?flex;
????justify-content:?flex-start;
????align-items:?center;
}
:host?::ng-deep?input{
????border:?none;
????outline:?none;
}
:host(.focus){
????border:?1px?solid?blue;
}

最后的效果:

多插槽(Multi-Slot)內(nèi)容投影

到目前為止,我們基本是一個ng-content將內(nèi)容投影進來,但是假如我們想投影一部分或者幾個部分呢?前面是在fa-input組件內(nèi)部定義了icon,然后將input從外部投影到了組件內(nèi)部。那么我希望可以將兩個都投影進來。fa-input組件只是提供一個空殼子。可以通過ng-content的select屬性來獲取到組件tag標(biāo)記中的內(nèi)容進行部分投影。我們可以修改fa-input組件的模板內(nèi)容:<div?class="fa-input">
????圖標(biāo):
????<ng-content?select="mat-icon">ng-content>
????輸入框:
????<ng-content?select="input">ng-content>
????
????<ng-content>ng-content>
div>
然后在使用的地方:<app-fa-input?icon="mail_outline">
????<mat-icon>mail_outlinemat-icon>
????<input?matInput?autocomplete="off"?type="text"?name="email"/>
app-fa-input>
最后可以看到,我們包括在組件tag中的內(nèi)容會被分配到我們希望他們出現(xiàn)的地方。來解讀下上面的“插槽”。上面兩個ng-content的select屬性查找組件tag標(biāo)記中的內(nèi)容的特定元素,匹配后就投影進來,不帶select的ng-content會將沒有匹配的內(nèi)容投影到組件中去。我們也可以查找具有特定類的元素,以結(jié)合多個選擇器。例如,根據(jù)類選擇某個input:<div?class="fa-input">
????圖標(biāo):
????<ng-content?select="mat-icon">ng-content>
????輸入框:
????<ng-content?select="input.text">ng-content>
????<ng-content>ng-content>
div>
對應(yīng)的使用的地方:<app-fa-input>
????<mat-icon>mail_outlinemat-icon>
????<input?matInput?class="text"?autocomplete="off"?type="text"?name="email"/>
????<input?autocomplete="off"?type="file"?name="email"/>
????<p>其他的一些投影p>
app-fa-input>
可以看到投影到了具體的input。

最后

當(dāng)然,這篇文章是我根據(jù)angular blog angular-ng-content這篇文章的翻譯和解讀,覺得啰嗦或者說不清楚的話,可以去看原文。

參考鏈接

https://blog.angular-university.io/angular-ng-content

總結(jié)

以上是生活随笔為你收集整理的投影元素直接隔离_Angular ngcontent 内容投影的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。