I have an issue related to the existing question
Cannot disable matInput element with this
Suggested answer works just fine:
ngOnInit() {
this.form = this.fb.group({
name: new FormControl({ value: '', disabled: this.disabled })
});
But when I change this.disabled value to true - disabled attribute isn't changed. Is there a way to change the disabled attribute for matInput?
You can't use that form, because when you create a FormControl you are passing that value, in your case the value of this.disabled. You are not binding properties, you are only passing a value to make some checks, this value is not reflecting input properties' changes.
You can't achieve your goal by this way, you need to enable and disable your input manually, like this:
let control = this.form.get('name')
control.disabled ? control.enable() : control.disable();
Obviously you can put it into a click event directly into your template, something like this:
<button (click)="this.form.get('name').enable()">Enable</button>
Related
(I'm new to Svelte so it is quite likely that I'm doing something wrong here)
UPDATE: I've added a second, slightly different REPL which may demonstrate the problem better. Try this one: https://svelte.dev/repl/ad7a65894f8440ad9081102946472544?version=3.20.1
I've encountered a problem attempting to bind a text input to a reactive value.
I'm struggling to describe the problem in words, so hopefully a reduced demo of the issue in the attached REPL will make more sense.
https://svelte.dev/repl/6c8068ed4cc048919f71d87f9d020696?version=3.20.1
The demo contains two custom <Selector> components on a page.
The first component is passed two string values ("one" and "two"):
<Selector valueOne="one" valueTwo="two"/>
Clicking the buttons next to the input field sets selectedValue to one of these values.
This, in turn, triggers the following reactive declaration to update:
$: value = selectedValue
The input field is bound to this reactive value:
<input type="text" bind:value>
So clicking the "One" button sets the input text to "one", and clicking the "Two" button sets the input field to "two".
Importantly though, you can still type anything into the input field.
The second component is passed two array values:
<Selector valueOne={[1, "one"]} valueTwo={[2, "two"]}/>
Again, clicking the buttons sets selectedValue to one of these.
However this time the reactive declaration depends on an array element:
$: value = selectedValue[1]
Everything works as before, except now you can no longer type into the input field at all.
So the question is - why does <input bind:value> behave differently for these two:
$: value = aString
vs
$: value = anArray[x]
It seems that this is only an issue when using two-way bindings.
By switching to a one-way and an on:input handler, the problem goes away:
i.e. instead of this:
<input type="text" bind:value={valX}/>
use this:
<input type="text" value={valX} on:input={e => valX = e.target.value}/>
I'm pretty sure your reactive declaration is overwriting your bound value as soon as it changes, which is with every key stroke on the input and every button press. Meaning it technically is working, you're just reverting it each time it changes. Check out this version of it that uses a watcher.
Also binding to a reactive declaration means you're never actually changing the variables with the input (which you can see in your JSON result on the first selector when you type in the input the value doesn't update only on button click).
Why not lose the reactive declaration and bind directly to the variable you want. Then use an {#if} block to switch between which version of the input you're showing based on the truthiness of index?
<script>
export let valueOne;
export let valueTwo;
export let index;
let selectedValue = index? [] : '';
let selectValue = (val) => selectedValue = val;
</script>
{#if index}
<input type="text" bind:value={selectedValue[index]} placeholder="Type anything...">
{:else}
<input type="text" bind:value={selectedValue} placeholder="Type anything...">
{/if}
<button on:click={() => selectValue(valueOne)}>One</button>
<button on:click={() => selectValue(valueTwo)}>Two</button>
<p>
<strong>Selected value:</strong> {JSON.stringify(selectedValue)}
</p>
By binding directly to the selectedValue or an index of it you have the added benefit of changing the value with the input. Here's a working example in the REPL
I am disabling controls when the user is not in edit mode.
this.theForm = this.builder.group({
name: [{ value: this.model.name, disabled: !this.isEditMode}, Validators.required],
})
When they change to edit mode I want the control to be enabled. However, it seems that once this is set it will not change then the component value changes.
This was working when I had this in the markup:
<input [disabled]="!isEditMode" type="text" formControlName="name" />
This was causing a runtime warning suggesting that I handle it with the formControl.
This is the warning:
It looks like you're using the disabled attribute with a reactive form
directive. If you set disabled to true when you set up this control in
your component class, the disabled attribute will actually be set in
the DOM for you. We recommend using this approach to avoid 'changed
after checked' errors.
Is there a way to bind this value when I setup the control?
Do I need to loop though the controls and toggle this whenever it changes?
You can subscribe to the control change and update it there, something like this (off the top of my head):
ngOnInit() {
for (let nut of this.userSettings.nutrientData) {
this.foodSettingsForm.controls[nut.abbr].valueChanges
.subscribe(v => {
this.completeValueChange(nut.abbr, v, (mode=="edit" ? false : true));
});
}
}
completeValueChange(field: string, value: boolean, disable: boolean) {
this.myForm.controls[myField]
.setValue(({value: vale, disabled: disable}, { onlySelf: true });
}
I am giving the 3.5 -> 4.0 upgrade another go and 'almost' have this use case working like it was. What I am stuck on now is how to ALWAYS add a certain option to the ajax results list.
Here is what I have right now :
html:
<select id="vedit-filter" name="settings[filter]" class="form-control select2">
<option value="default" selected="">default</option>
</select>
js:
$("#vedit-filter").select2({
placeholder: "Select or enter application...",
allowClear: true,
multiple: false,
tags: true,
ajax: {
dataType: 'json',
delay: 1000,
type: 'post',
url: '/process/get_application_list.php',
data: function (term, page) {
return {
term: term, // search term
page_limit: 25, // page size
page: page // page number
};
},
results: function (data, page) {
var more = (page * 25) < data.total; // whether or not there are more results available
return {
results: data.results,
more: more
};
}
}
});
That will load 'default' as the initial selected option. If the user changes this then it is gone. I want a way for them to revert back to that initial selection if need be. Is the only way to include it in my returned results from ajax as an option? Or is there a way to do this on the js side?
UPDATE:
The default selection will always be dynamic, but in this example we are using a value/name of 'default'.
<select id="vedit-filter" name="settings[filter]" class="form-control select2">
<option value="default" selected="">default</option>
</select>
then in the js:
var default_filter = $("#vedit-filter").val(); //get the default loaded from the html
placeholder: {
id: default_filter, // or whatever the placeholder value is
text: default_filter // the text to display as the placeholder
},
I recently explained this on GitHub but didn't realize how much of a breaking change it was.
I want a way for them to revert back to that initial selection if need be.
Select2 provides the placeholder option, which can allow you to specify a placeholder like "Select an item" to the user if a selection is not already made. In order to support a <select>, which will select the first option by default (that's done by the browser), Select2 requires that a "placeholder" <option> exists. This also doubles as the option which Select2 uses to determine if the placeholder needs to be displayed.
In addition to the placeholder option, Select2 also allows the user to remove their selected option, which will revert the selection back to the placeholder option. This can be enabled through the allowClear option.
Is the only way to include it in my returned results from ajax as an option? Or is there a way to do this on the js side?
In your example, you are using a placeholder option with a value set on it. Because Select2 expects that the value is blank/empty by default, you need to tell Select2 what to look for when detecting the placeholder. This can be done by passing a data object into placeholder which Select2 will use when checking.
placeholder: {
id: 'default', // or whatever the placeholder value is
text: 'Select or enter application...' // the text to display as the placeholder
}
This will tell Select2 to display the placeholder when the value is set to default, which in your example appears to be the placeholder/default option.
What I am stuck on now is how to ALWAYS add a certain option to the ajax results list.
While I think we might have solved the XY problem here, I did want to note that adding a new option to the AJAX results list is as simple as overriding processResults. This is because processResults passes the list of data objects directly to Select2, which gives you a safe place to inject new options into the list.
You just need to .push the extra data object into the results and then pass them back to Select2.
I'm able to get this working except for two things that I just can't figure out:
Problem 1: I need to get both of the following but right now can only achieve one OR the other:
Select options need to have value and text
The selectedOption captured in the model needs to return the object of the selected option and not just the value of the option
I can affect which one of these works by including or excluding the following from my select markup:
...data-bind="optionsValue = 'Id'"...
How can I achieve both?
Problem 2: I need to set the selected option in the dropdown to an object I retrieve from a cookie containing the user's preferred value. My below implementation does not succeed in setting the selected value at all. What am I missing?
$(document).ready(function(){
var userOption = $.cookie("userPref") == null ? undefined : JSON.parse($.cookie("userPref"));
var model = function(){
this.options = ko.observableArray();
this.childOptions = ko.observableArray();
this.selectedOption = ko.observable(userOption); //this does nothing to set the value
this.selectedOption(userOption); //this also does nothing
this.options.subscribe(function(){
//this.selectedOption() returns an object if optionsValue is excluded from select databinding and returns option value if included
$.cookie("userPref", JSON.stringify(this.selectedOption());
this.childOptions(undefined);
this.childOptions(this.selectedOption() ? this.selectedOption().children : []);
}.bind(this));
};
var viewModel = new model();
ko.applyBindings(viewModel);
$.ajax({
type: "GET",
url: "myurl",
success: function(data){
viewModel.options(data);
}
});
});
<select data-bind="options: options, optionsText: 'text', optionsValue: 'Id', value: selectedOption, optionsCaption: 'Select Option'"></select>
You can't use the entire object as the value because an option value must be a simple type: string, int, etc. I suppose you could JSON encode the object (so it's a string) and set the value to that, but then you'd have to reciprocate on the backend and decode the posted string value back into an object and somehow work with that. However, said object would be disconnected from EF, so you'd have to fetch the object new from the database anyways in order to do anything meaningful with it. And, your second problem is the same as your first, you can't use a full object.
The way this is usually done is with ids. You make the option value the object's id. Then, you can set the selected option based on the object's id from the cookie. When you get to the backend, if you need to work with the object, you fetch it by the posted id. Anything else is not really a tenable situation.
I have a checkbox and a textbox (both are enabled and the checkbox starts unchecked [false]).
What I need is the following:
When I write something in the textbox and leave it (loses focus) the
checkbox is checked automatically.
When I write something in the
textbox, remove it and leave it the checkbox should remain
unchecked.
When I write something in the textbox and click the
checkbox, the checkbox is checked now and the data in the textbox is
not cleared.
When I write something in the textbox and click the
checkbox twice, first happens step 3 and then the checkbox is
unchecked and the data in the textbox is cleared.
When I click in the checkbox the checkbox is checked, then I write in the textbox
and uncheck the checkbox, then the data in the textbox is cleared.
What I tried so far is the following code:
//The checked property in the checkbox is binded to
that.BuildingCriteria.IncludeLoadingDocks
that.BuildingCriteria.IncludeLoadingDocks.subscribe(function (newValue) {
if (!that.updatingTextBox && !newValue) {
that.BuildingCriteria.LoadingDocksMin(null);
}
});
//The textbox value is binded to that.BuildingCriteria.LoadingDocksMin
that.BuildingCriteria.LoadingDocksMin.subscribe(function (newValue) {
that.updatingTextBox = true;
that.BuildingCriteria.IncludeLoadingDocks(true);
that.updatingTextBox = false;
});
This works if you try all the steps above, for all of them but then, when you try some of them again stops working for some... specially if you write something in the textbox with the checkbox unchecked and then leave the textbox, it doesn't check the checkbox automatically anymore.
I tried using flags as you can see but I couldn't make it to work on ALL the cases ALWAYS.
I've been working on this for days so if you can help me out soon I'd appreciate it a lot!
Thanks in advance!!
It's near impossible to gave a straight up answer to your question, but from it I feel the closest thing may be to note a few KO features that you may yet need to consider.
The value binding supports a valueUpdate = 'afterkeydown' version, which would allow you to keep your textbox and checkbox in synch real time. This may well remove the need for requirement 3.
The computed observable supports specializing read and write operations, which at times may be clearer than using subscriptions.
You may need to introduce a "grace" period for the checkbox, if you must stick with requirement 3. Just don't allow updating the checkbox too shortly after leaving the textbox. The throttle extender and hasfocus binding can help you with that.
There's a great blogpost on when to use which feature.
In any case, your requirements are a bit hard to understand without the business case, and it might even be that you're experiencing an XY-problem. From your implementation requirements I'd assume functional (not implementation) requirements like this:
There's a textbox to hold the actual order/criterium/name/whatever.
There's a checkbox to indicate such an order/etc is wanted.
This checkbox should be in synch (checked) with whether the user typed some text.
This checkbox should be in synch (unchecked) if the user empties the textbox.
If the user checks the checkbox then
If there was text for the order/etc it should be cleared.
If there was no text a default order/etc should be suggested.
Here's a jsfiddle with a demo of how you could approach these functional requirements. For completeness, here's the relevant code, starting with the View:
<input type="checkbox" data-bind="checked: isChecked" />
<input type="textbox" data-bind="value: someText, valueUpdate: 'afterkeydown', selectSuggestion: someText" />
The custom binding for selecting the "default suggestion text":
var suggestion = "<enter something>";
ko.bindingHandlers.selectSuggestion = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var currentText = ko.utils.unwrapObservable(valueAccessor());
if (currentText === suggestion) element.select();
}
};
And the ViewModel:
var ViewModel = function() {
var self = this;
var privateIsChecked = ko.observable(false);
var privateText = ko.observable("");
self.isChecked = ko.computed({
read: privateIsChecked,
write: function(value) {
if (!privateIsChecked() && value && privateText() === "") {
privateText(suggestion);
}
if (privateIsChecked() && !value) {
privateText("");
}
privateIsChecked(value);
}
});
self.someText = ko.computed({
read: privateText,
write: function(value) {
privateIsChecked(value !== "");
privateText(value);
}
});
}
I'm aware that this doesn't directly answer your question, but like I said that's pretty hard to do for us on Stack Overflow, without knowledge of your business case.