I basically have an HTML5 date picker like this:
<input class="form-control" type="date" [(ngModel)]="startDate" />
And another area that displays the date from the model like this:
{{startDate | date:shortDate}}
I don't think it's relevant to this question but to be thorough, I have this kind of thing behind the scenes:
private startDate: string;
private endDate: string;
updateDateFilter() {
let from = new Date(this.startDate);
let to = new Date(this.endDate);
**Stuff to filter based on date range**
}
It's awesome. There's a lot more going on but it'd probably be extraneous detail.
The trouble is you can click into the date picker and, say, select the year and hit delete which does this:
No problem right? Wrong, because the Angular2 date pipe gets confused and my whole app crashes after displaying this surprisingly helpful error message:
ORIGINAL EXCEPTION: Invalid argument '' for pipe 'DatePipe'
The solution seems simple but I'm not sure how to go about it. I'm thinking I just need to catch the date string before it's processed by the DatePipe and make sure there's valid content for the day, month, and year. But Angular2 does magic and the since it's done using model binding I don't see a clear route to intervention.
I probably need to override the DatePipe, keeping its functionality but checking the string first. I don't know how. If you know how to do that, do share. Any alternative approaches would be helpful too. I just can't have the app crash whenever the date is incomplete.
Thanks!
You could create your own pipe and either extend the default DatePipe or just instantiate it and call it programmatically.
#Pipe({
name: 'customDate'
})
export class CustomDatePipe extends DatePipe {
transform(date: string, pattern?: string): string {
// validate/initialize date
return super.transform(date, pattern);
}
}
Or the second case:
export class CustomDatePipe {
private datePipe: DatePipe = new DatePipe();
transform(date: string, pattern?: string): string {
// validate/initialize date
return this.datePipe.transform(date, pattern);
}
}
Did not had the chance to check this but I assume both approaches should work.
Related
I am new to Blazor and MudBlazor. I am using a and I want to call an event when the selection changes. The documentation show there is a EventCallback method but there are no syntax examples. I have been searching a good part of the day but cannot find an example. Can anyone please share some simple code? I know I can bind to a variable, and I initially did that. What I want is to call code and do some different code based on the selected value. Seems to be easier to do in Blazor syntax vs MudBlazor.
I appreciate any help.
Thank you
<MudRadioGroup T="string" SelectedOption="#SelectedOption" SelectedOptionChanged="OnSelectedOptionChanged">
<MudRadio Option="#("Radio 1")" Color="Color.Primary">Primary</MudRadio>
<MudRadio Option="#("Radio 2")" Color="Color.Secondary">Secondary</MudRadio>
<MudRadio Option="#("Radio 3")">Default</MudRadio>
<MudRadio Option="#("Radio 4")" Color="Color.Primary" Disabled="true">Disabled</MudRadio>
</MudRadioGroup>
#code {
public string SelectedOption { get; set; }
private void OnSelectedOptionChanged(string selectedOption)
{
SelectedOption = selectedOption;
// call your stuff
}
}
https://try.mudblazor.com/snippet/mOQGYtGKUpgnQxqe
I am working on my first Android application using MVVM and Databinding. Some areas I am grasping but this one I am struggling with. The scenario:
I have a Create Account wizard activity, which uses Android Navigation Architecture to page through several fragments asking for input from the user. The first fragment/step asks the user for the first and last name. I do not want the button to proceed to the next step to enable until something is entered in both fields. I have enabled buttons based on ONE fields validation before, but not two. I feel like I am missing something silly.
Here is the button I want to enable after both fields have data in them:
<Button
android:id="#+id/continueToSecondStepButton"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:background="#drawable/button_transparent_background"
android:onClick="#{() -> viewModel.proceedToNextStep()}"
android:text="#string/step_proceed"
android:enabled="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid)}"
android:textAllCaps="false"
android:textColor="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid) ? #colorStateList/white : #colorStateList/transparent_white}"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/subtext" />
Primarily here the focus is:
android:enabled="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid)}"
android:textColor="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid) ? #colorStateList/white : #colorStateList/transparent_white}"
I have two Transformations to listen to key changes on the first and last name fields and execute the validation method:
firstNamesValid = Transformations.switchMap(firstName) { firstName -> isNamesValid() }
lastNamesValid = Transformations.switchMap(lastName) { lastName -> isNamesValid() }
and for now, just a simple method to check if both fields have data in them:
private fun isNamesValid(): LiveData<Boolean> {
var namesValid = false
if (!firstName.value.isNullOrEmpty() && !lastName.value.isNullOrEmpty()) {
namesValid = true
}
val mediatorLiveData: MediatorLiveData<Boolean> = MediatorLiveData()
mediatorLiveData.value = namesValid
return mediatorLiveData
}
It "kind of" works but not well. Can you enable a button based on validating two fields with Databinding? I have a feeling there is an easier way to do this. What happens with this setup is that, you fill out first and last name, and nothing happens, but if you then navigate back to the first name and enter another character it works and enables the button. I assume this is due to some logic error in my code. Thanks for looking.
I ended up figuring it out right after. Typing it up helped me find the issue and I will share what I did for others. I had a logic error in my code referencing the isNamesValid function.
I made the following changes:
isNamesValid now takes a string parameter and checks the specific string rather then hardcoded checking the first and last name values
private fun isNameValid(name: String?): LiveData<Boolean> {
var namesValid = false
if (!name.isNullOrEmpty()) {
namesValid = true
}
val mediatorLiveData: MediatorLiveData<Boolean> = MediatorLiveData()
mediatorLiveData.value = namesValid
return mediatorLiveData
}
Updated Transformations to call that method passing in the name to be checked.
firstNamesValid = Transformations.switchMap(firstName) { firstName -> isNameValid(firstName) }
lastNamesValid = Transformations.switchMap(lastName) { lastName -> isNameValid(lastName) }
I'm using palette components on a page and I want the available elements in two of them to change depending on what is selected in the first.
What is the best way to achieve this? Which events are thrown by the palette component, that I could listen to, adapt the palette's model and perform a zone update? I thought it would work the same way as for select components doing something like this:
void onValueChanged() {
// do something
}
Unfortunately that doesn't work for palettes.
I'm using Tapestry 5.4-beta-6, but I guess things haven't changed that much since earlier versions.
I'd probably do this with a mixin.
public class PaletteChange {
#Parameter
private String zone;
#InjectContainer
private Palette palette;
public void afterRender() {
Link eventLink = componentResources.createEventLink("change");
JSONObject args = new JSONOBject(
"id", pallete.getClientId(),
"url", eventLink,
"zone", zone
);
javascriptSupport.addScript("palleteChange(%s)", args);
}
Object onChange(#RequestParameter("value") String value) {
CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
resources.triggerEvent("change", new String[] { value }, callback);
return callback.getResult();
}
}
Javascript
function palleteChange(spec) {
var field = $('#' + spec.id + '/select[1]');
field.on('change', function() {
var zoneManager = Tapestry.findZoneManagerForZone(spec.zone);
var params = { value: field.val() };
zoneManager.updateFromURL(spec.url, params);
});
}
Then use the mixin in your code
<t:palette t:id="myPalette" t:mixins="paletteChange" zone="myZone" ... />
<t:zone t:id="myZone">
...
</t:zone>
Page
#Inject
private Zone myZone;
Block onChangeFromMyPalette(String value) {
doStuff(value);
return myZone.getBody();
}
See here for a similar mixin.
I finally used the didChange element together with a similar mixin like the Observe mixin. I put a demo on Github for anyone, who is interested.
Just a few notes:
I used 5.4 beta 6, it already has the necessary client side events.
I couldn't make it work using a Tapestry javascript module, so I still use javascriptSupport.addInitializerCall.
The remaining problem is, that updating the second palette with a zone update will reset any changes the user has made in this palette, since they are only kept on the client side (in a hidden field). I will still need to look into that, but it is not part of the original question.
We're using protobuf-net for sending log messages between services. When profiling stress testing, under high concurrency, we see very high CPU usage and that TakeLock in RuntimeTypeModel is the culprit. The hot call stack looks something like:
*Our code...*
ProtoBuf.Serializer.SerializeWithLengthPrefix(class System.IO.Stream,!!0,valuetype ProtoBuf.PrefixStyle)
ProtoBuf.Serializer.SerializeWithLengthPrefix(class System.IO.Stream,!!0,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(class System.IO.Stream,object,class System.Type,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(class System.IO.Stream,object,class System.Type,valuetype ProtoBuf.PrefixStyle,int32,class ProtoBuf.SerializationContext)
ProtoBuf.ProtoWriter.WriteObject(object,int32,class ProtoBuf.ProtoWriter,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.BclHelpers.WriteNetObject(object,class ProtoBuf.ProtoWriter,int32,valuetype
ProtoBuf.BclHelpers/NetObjectOptions)
ProtoBuf.Meta.TypeModel.GetKey(class System.Type&)
ProtoBuf.Meta.RuntimeTypeModel.GetKey(class System.Type,bool,bool)
ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(class System.Type,bool,bool,bool)
ProtoBuf.Meta.RuntimeTypeModel.TakeLock(int32&)
[clr.dll]
I see that we can use the new precompiler to get a speed boost, but I'm wondering if that will get rid of the issue (sounds like it doesn't use reflection); it would be a bit of work for me to integrate this, so I haven't tested it yet. I also see the option to call Serializer.PrepareSerializer. My initial (small scale) testing didn't make the prepare seem promising.
A little more info about the type we're serializing:
[ProtoContract]
public class SomeMessage
{
[ProtoMember(1)]
public SomeEnumType SomeEnum { get; set; }
[ProtoMember(2)]
public long SomeId{ get; set; }
[ProtoMember(3)]
public string SomeString{ get; set; }
[ProtoMember(4)]
public DateTime SomeDate { get; set; }
[ProtoMember(5, DynamicType = true, OverwriteList = true)]
public Collection<object> SomeArguments
}
Thanks for your help!
UPDATE 9/17
Thanks for your response! We're going to try the workaround you suggest and see if that fixes things.
This code lives in our logging system so, in the SomeMessage example, SomeString is really a format string (e.g. "Hello {0}") and the SomeArguments collection is a list of objects used to fill in the format string, just like String.Format. Before we serialize, we look at each argument and call DynamicSerializer.IsKnownType(argument.GetType()), if it isn't known, we convert it to a string first. I haven't looked at the ratios of data, but I'm pretty sure we have a lot of different strings coming in as arguments.
Let me know if this helps. If you need, I'll try to get more details.
TakeLock is only used when it is changing the model, for example because it is seeing a type for the first time. You shouldn't normally see TakeLock after the first time a particular type has been used. In most cases, using Serializaer.PrepareSerializer<SomeMessage>() should perform all the necessary initialization (and similar for any other contracts you are using).
However! I wonder if perhaps this is also related to your use of DynamicType; what are the actual objects being used here? It might be that I need to tweak the logic here, so that it doesn't spend any time on that step. If you let me know the actual objects (so I can repro), I will try to run some tests.
As for whether the precompiler would change this; yes it would. A fully compiled static model has a completely different implementation of the ProtoBuf.Meta.TypeModel.GetKey method, so it would never call TakeLock (you don't need to protect a model that can never change!). But you can actuallydo something very similar without needing to use precompile. Consider the following, run as part of your app's initialization:
static readonly TypeModel serializer;
...
var model = TypeModel.Create();
model.Add(typeof(SomeMessage), true);
// TODO add other contracts you use here
serializer = model.Compile();
This will create a fully static-compiled serializer assembly in memory (instead of a mutable model with individual operations compiled). If you now use serializer.Serialize(...) instead of Serializer.Serialize (i.e. the instance method on your stored TypeModel rather than the static method on Serializer) then it will essentially be doing something very similar to "precompiler", but without the need to actualy precompile it (obviously this will only be available on "full" .NET). This will then never call TakeLock, as it is running a fixed model, rather than a flexible model. It does, however, require you to know what contract-types you use. You could use reflection to find these, by looking for all those types with a given attribute:
static readonly TypeModel serializer;
...
var model = TypeModel.Create();
Type attributeType = typeof(ProtoContractAttribute);
foreach (var type in typeof(SomeMessage).Assembly.GetTypes()) {
if (Attribute.IsDefined(type, attributeType)) {
model.Add(type, true);
}
}
serializer = model.Compile();
But emphasis: the above is a workaround; it sounds like there's a glitch, which I'll happily investigate if I can see an example where it actually happens; most importantly: what are the objects in SomeArguments?
Im working on a MVVM Windows phone app that displays weather info.
When the app loads up it opens MainPage.xaml. It makes a call the the service to get weather info and binds that data to the UI. Both Fahrenheit and Celcius info are returned but only one is displayed.
On the setting page, the user can select to view the temp in either Fahrenheit or Celcius.
The user can change this setting at any time and its stored in IsolatedStorageSettings.
The issue Im having is this:
when the user navigates to the Settings page and changes their preference for either Fahrenheit or Celcius, this change is not reflected on the main page.
This issue started me thinking about this in a broader context. I can see this being an issue in ANY MVVM app where the display depends on some setting in IsolatedStorage. Any time any setting in the IsoStore is updated, how does the ViewModels know this? When I navigate back in the NavigationStack from the settings page back to MainPage how can I force a rebind of the page?
The data in my model hasnt changed, only the data that I want to display has changed.
Am I missing something simple here?
Thanks in advance.
Alex
Probably you have code like this:
public double DisplayTemperature
{
get { return (IsCelsium) ? Celsium : Fahrenheit; }
}
And IsCelsium is:
public double IsCelsium
{
get { return (bool)settings["IsCelsium"]; }
set { settings["IsCelsium"] = value; }
}
So you need to add NotifyPropertyChanged event to notify UI to get new values from DisplayTemperature property:
public double IsCelsium
{
get { return (bool)settings["IsCelsium"]; }
set
{
settings["IsCelsium"] = value;
NotifyPropertyChanged("DisplayTemperature");
}
}
Take a look at Caliburn Micro. You could implement something similar or use CM itself. When using CM I don't even think about this stuff, CM makes it so simple.
When your ViewModel inherits from Screen there are life-cycle events that fire that you can override. For example, OnInitialize fires the very first time the ViewModel is Activated and OnActivate fires every time the VM is activated. There's also OnViewAttached and OnViewLoaded.
These methods are the perfect place to put logic to populate or re-populate data.
CM also has some special built in features for allowing one to easily tombstone a single property or an entire object graph into Iso or phone state.
ok, so Ive come up with a solution. Before I get to it, let me provide some background. The app that Im working on uses both MVVM Light and WP7Contrib. That being the case, I am using Funq for DI and the MVVMLight Toolkit. After I posted my initial question, I gave the question a bit more thought. I remembered a video that I watched a while back from MIX2011 called Deep Dive MVVM with Laurent Bugnion
http://channel9.msdn.com/Events/MIX/MIX11/OPN03
In it, he talks about just this problem (view models not living at the same time) on Windows Phone. The part in question starts around the 19 minute mark.
Anyway, after I remembered that and realized that the ViewModel locator is exposed in App.xaml, this became a trivial problem to solve. When the user changes the Fahrenheit/Celcius option on the setting page, I simply get a reference to the MainViewModel via the ViewModelLocator and reset the collection that is bound to the UI thus causing the bindings to update.
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Contains(Key))
{
// If the value has changed
if (settings[Key] != value)
{
// Store the new value
settings[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
public bool ImperialSetting
{
get
{
return GetValueOrDefault<bool>(ImperialSettingKeyName, ImperialSettingDefault);
}
set
{
if (AddOrUpdateValue(ImperialSettingKeyName, value))
{
Save();
RaisePropertyChanged("ImperialSettingText");
var vml = new ViewModelLocator();
vml.MainViewModel.Cities = (App.Current as App).Cities;
}
}
}
It was a mistake on my part not to realize that I could get access to the viewModel via the ViewModelLocator. Hopefully this post saves someone else the time I burned on this issue.