This commit is contained in:
parent
fd9ad7f8c5
commit
4df198dfaa
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -238,6 +238,17 @@ export class BrowserRenderer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We handle setting 'value' on a <select> in two different ways:
|
||||||
|
// [1] When inserting a corresponding <option>, in case you're dynamically adding options
|
||||||
|
// [2] After we finish inserting the <select>, in case the descendant options are being
|
||||||
|
// added as an opaque markup block rather than individually
|
||||||
|
// Right here we implement [2]
|
||||||
|
if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
|
||||||
|
const selectValue = newDomElementRaw[selectValuePropname];
|
||||||
|
newDomElementRaw.value = selectValue;
|
||||||
|
delete newDomElementRaw[selectValuePropname];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertComponent(batch: RenderBatch, parent: LogicalElement, childIndex: number, frame: RenderTreeFrame) {
|
private insertComponent(batch: RenderBatch, parent: LogicalElement, childIndex: number, frame: RenderTreeFrame) {
|
||||||
|
|
@ -317,7 +328,8 @@ export class BrowserRenderer {
|
||||||
// <select> is special, in that anything we write to .value will be lost if there
|
// <select> is special, in that anything we write to .value will be lost if there
|
||||||
// isn't yet a matching <option>. To maintain the expected behavior no matter the
|
// isn't yet a matching <option>. To maintain the expected behavior no matter the
|
||||||
// element insertion/update order, preserve the desired value separately so
|
// element insertion/update order, preserve the desired value separately so
|
||||||
// we can recover it when inserting any matching <option>.
|
// we can recover it when inserting any matching <option> or after inserting an
|
||||||
|
// entire markup block of descendants.
|
||||||
element[selectValuePropname] = value;
|
element[selectValuePropname] = value;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -330,10 +342,11 @@ export class BrowserRenderer {
|
||||||
element.removeAttribute('value');
|
element.removeAttribute('value');
|
||||||
}
|
}
|
||||||
// See above for why we have this special handling for <select>/<option>
|
// See above for why we have this special handling for <select>/<option>
|
||||||
const parentElement = element.parentElement;
|
// Note that this is only one of the two cases where we set the value on a <select>
|
||||||
if (parentElement && (selectValuePropname in parentElement) && parentElement[selectValuePropname] === value) {
|
const selectElem = this.findClosestAncestorSelectElement(element);
|
||||||
this.tryApplyValueProperty(batch, parentElement, attributeFrame);
|
if (selectElem && (selectValuePropname in selectElem) && selectElem[selectValuePropname] === value) {
|
||||||
delete parentElement[selectValuePropname];
|
this.tryApplyValueProperty(batch, selectElem, attributeFrame);
|
||||||
|
delete selectElem[selectValuePropname];
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -353,6 +366,18 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findClosestAncestorSelectElement(element: Element | null) {
|
||||||
|
while (element) {
|
||||||
|
if (element instanceof HTMLSelectElement) {
|
||||||
|
return element;
|
||||||
|
} else {
|
||||||
|
element = element.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private insertFrameRange(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, startIndex: number, endIndexExcl: number): number {
|
private insertFrameRange(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, startIndex: number, endIndexExcl: number): number {
|
||||||
const origChildIndex = childIndex;
|
const origChildIndex = childIndex;
|
||||||
for (let index = startIndex; index < endIndexExcl; index++) {
|
for (let index = startIndex; index < endIndexExcl; index++) {
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,19 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
Assert.Equal("Fourth choice", target.SelectedOption.Text);
|
Assert.Equal("Fourth choice", target.SelectedOption.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanBindSelectToMarkup()
|
||||||
|
{
|
||||||
|
var target = new SelectElement(Browser.FindElement(By.Id("select-markup-box")));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("select-markup-box-value"));
|
||||||
|
Assert.Equal("Second choice", target.SelectedOption.Text);
|
||||||
|
Assert.Equal("Second", boundValue.Text);
|
||||||
|
|
||||||
|
// Modify target; verify value is updated
|
||||||
|
target.SelectByText("Third choice");
|
||||||
|
Browser.Equal("Third", () => boundValue.Text);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanBindTextboxInt()
|
public void CanBindTextboxInt()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -145,18 +145,32 @@
|
||||||
<h2>Select</h2>
|
<h2>Select</h2>
|
||||||
<p>
|
<p>
|
||||||
<select id="select-box" @bind="selectValue">
|
<select id="select-box" @bind="selectValue">
|
||||||
<option value=@SelectableValue.First>First choice</option>
|
<optgroup label="Some choices"> <!-- Show it also works with optgroup -->
|
||||||
<option value=@SelectableValue.Second>Second choice</option>
|
<option value=@SelectableValue.First>First choice</option>
|
||||||
<option value=@SelectableValue.Third>Third choice</option>
|
<option value=@SelectableValue.Second>Second choice</option>
|
||||||
@if (includeFourthOption)
|
<option value=@SelectableValue.Third>Third choice</option>
|
||||||
{
|
@if (includeFourthOption)
|
||||||
<option value=@SelectableValue.Fourth>Fourth choice</option>
|
{
|
||||||
}
|
<option value=@SelectableValue.Fourth>Fourth choice</option>
|
||||||
|
}
|
||||||
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
<span id="select-box-value">@selectValue</span>
|
<span id="select-box-value">@selectValue</span>
|
||||||
<button id="select-box-add-option" @onclick="AddAndSelectNewSelectOption">Add and select new item</button>
|
<button id="select-box-add-option" @onclick="AddAndSelectNewSelectOption">Add and select new item</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h2>Select (markup block)</h2>
|
||||||
|
<p>
|
||||||
|
<select id="select-markup-box" @bind="selectMarkupValue">
|
||||||
|
<optgroup label="Some choices">
|
||||||
|
<option value="First">First choice</option>
|
||||||
|
<option value="Second">Second choice</option>
|
||||||
|
<option value="Third">Third choice</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
<span id="select-markup-box-value">@selectMarkupValue</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
string textboxInitiallyBlankValue = null;
|
string textboxInitiallyBlankValue = null;
|
||||||
string textboxInitiallyPopulatedValue = "Hello";
|
string textboxInitiallyPopulatedValue = "Hello";
|
||||||
|
|
@ -187,6 +201,7 @@ Guid textboxGenericGuidValue = Guid.Empty;
|
||||||
bool includeFourthOption = false;
|
bool includeFourthOption = false;
|
||||||
enum SelectableValue { First, Second, Third, Fourth }
|
enum SelectableValue { First, Second, Third, Fourth }
|
||||||
SelectableValue selectValue = SelectableValue.Second;
|
SelectableValue selectValue = SelectableValue.Second;
|
||||||
|
SelectableValue selectMarkupValue = SelectableValue.Second;
|
||||||
|
|
||||||
void AddAndSelectNewSelectOption()
|
void AddAndSelectNewSelectOption()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue