I am trying to make a button that is disabled if a certain condition is false. For a regular button this works:
<button #disabled="!IsReady">Click me</button>
#code
{
public bool IsReady { get; set; }
}
I am using MatBlazor library that has MatButton version of button:
<MatButton Disabled="#!IsReady">Click me</MatButton>
The latter does't work: it gives the following error:
Component attributes do not support complex content (mixed C# and markup)
In good ol' WPF we would probably use a converter in the binding, so the bound boolean is inverted. Is this a limitation of MatBlazor?
The quick solution I see to this is to do something like:
<MatButton Disabled="#IsNotReady">Click me</MatButton>
#code
{
public bool IsReady { get; set; }
public bool IsNotReady => !IsReady;
}
Is there a better way?
The solution ended up being very easy: you just need to surround C# code with parenthesis:
<MatButton Disabled="#(!IsReady)">Click me</MatButton>
When using the # symbol, you're usually switching from a HTML context, to a C# context. So when you say something like Disabled="#IsNotReady" - it works just fine. The moment you try and do something more complicated, even just a "!" operator, that's enough to be something that needs to be evaluated, and not just a reference to a value. So Blazor requires you to use #( /* code*/ ) and expects it to evaluate to something it can render as text to the output (html in this case).
Blazor requires a literal, a field, or a property. So you could create a property such as:
private bool isNotReady => !isReady;
This might seem pointless, but actually it's far more powerful - you can do a lot of logic that evaluates to a true/false or what ever type you need to present to some child component. So this could also be argued as being a better separation of concerns.
Your code could therefore read more like this:
<MatButton Disabled="#IsNotReady">Click me</MatButton>
Related
Trying to force text in certain fields in a view that is adding records to the DB via Entity framework to uppercase.
Can I do it in the view EditorFor or can I do it in the controller to all fields easily before firing db.SaveChanges()?
You could loop through the properties on your ViewModel server side using a helper method.
public T ViewModelToUpper<T>(T viewModel) where T : class
{
foreach (var property in viewModel.GetType().GetProperties())
{
var value = property.GetValue(viewModel, null);
if (value is string)
{
property.SetValue(viewModel, value.ToString().ToUpper());
}
}
return viewModel;
}
Then you can call viewModel = ClassName.ViewModelToUpper(viewModel). Now you don't have to worry about doing it for every string property as this will happen anyway.
You can do this in the UI with jQuery if you'd like. It's a little weird from a user perspective to be typing and have everything converted to uppercases (I'd check my caps lock). Convert to uppercase as user types using javascript
Your other option, better IMO, is to do this in the controller. You can use ToUpper()on the strings. Keep Globalization in mind.
Which approach has a better maintainablility and extendability?
Where are the limitations/restrictions for each approach?
Put the DataType inside the ViewModel
OR
Put the DataType/Control type in the view?
VIEWMODEL
[DataType(DataType.MultilineText)]
public string LongDescription { get; set; }
OR
VIEW
#Html.TextAreaFor(m => m.LongDescription)
As far as maintainability is concerned, defining it in your view model would be best.
Given your example suppose we had this in our VM:
[DataType(DataType.MultilineText)]
public string LongDescription { get; set; }
and this in our View:
#Html.EditorFor(m => m.LongDescription);
Now lets say that some requirements have changed and a simple string no longer cuts it for your LongDescription, say you created a special CustomRichTextFormat class to store it. You would change your VM to look like the following:
public CustomRichTextFormat LongDescription { get; set; }
You can then create an EditorTemplate called CustomRichTextFormat.cshtml and put it in the EditorTemplates folder in your View folder, and since you used #Html.EditorFor(m => m.LongDescription); in your original View, MVC will be smart enough to show the custom editor you have defined for any fields of the CustomRichTextFormat type.
So to summarize, the advantage of this approach was that you have a truly generic View that doesn't require any changes despite the underlying type of the field changing in the ViewModel.
In my opinion i would suggest having the ViewModel contain the DataType. The reason is because you may then do something like this for your viewModel.
#Html.EditorFor(m => m);
Which will decrease the amount of markup you need in your view.
Generally speaking, it's more powerful to use EditorFor, as this tries to guess what editor you need for a given model element. It's intelligent enough to select custom editor templates where you have defined templates for a given type. For example, if you had a custom editor for images, you could just use the EditorFor syntax and the view engine will resolve the editor for you.
It's a good solution, because it keeps the amount of information repetition down; you only have to specify type information in one place, creating a single source of truth that other components can read from to make decisions. That also means if you make changes to the types of your model, the view will still produce a suitable editor for those types.
However, there are going to be occasions when EditorFor fails to produce the result you need, at which point you will have to get closer to the metal and invoke the appropriate HtmlHelper methods, or even creating extension methods yourself.
You will need to use both approaches; don't be worried about choosing one over the other, as they both fill different needs. Generally, EditorFor will do more for you with less code, but don't by shy about going around it in special cases.
I am using a JS library that passes one of its query string parameters as just an underscore, ex.
SomeUrl/?_ABC123
I tried creating a simple class with an underscore as a property but that doesn't seem to work (nothing appears to be bound).
public class SomeUrlModel
{
public string _ { get; set; }
}
Am I stuck with just creating a custom model binder?
(I could easily just grab this out of the query string but it'd be nice to not have to embed this kind of code everyplace where I can expect this querystring).
Update
Well, apparently my test was incorrect. An underscore IS being properly bound. Something in the JS code is altering the URL between the point where it was set and when I hit my breakpoint in my C#. Strange that I didn't catch this when I first saw it in the network trace in Chrome.
In my ASP.NET MVC project, I have a polymorphic collection that I wish to render - say, an IEnumerable<ISomething> where the individual items may be a mix of different implementations of ISomething.
I'd like that list rendered, where each concrete type renders according to its own template (perhaps a strongly typed ViewUserControl).
In WPF, I'd be able to specify DataTemplates that would automatically bind concrete types to specific templates. Can I do something similar in ASP.NET MVC?
Obviously, I can iterate through the list and attempt a cast using the is keyword and then use a lot of if statements to render the desired control, but I was hoping for something more elegant (like WPF).
I ended up with developing a solution myself - I have described it in DataTemplating In ASP.NET MVC.
I'm not sure if I get you fully, but, why don't you implement a method to your ISomething interface like render for example, which by contract will be implemented to all of your other concrete entities, and then iterate through each item in the polymorphic collection and call it.
I had a similar issue and never found a "simple" answer. I had the benefit of knowing that all items in the list would render the same way so I created a decorator for ISomething, converted the list to IEnumerable using some code from the Umbrella project (http://umbrella.codeplex.com), and then extracted out the relevant pieces. Kinda like the following:
public interface ISomethingDecorator
{
string Description { get; }
string[] Actions { get; }
}
public class BigSomethingDecorator : ISomethingDecorator { /* ... */ }
public class SmallSomethingDecorator : ISomethingDecorator { /* ... */ }
Then, as I said, I use the Umbrella project to convert from ISomething to ISomethingDecorator and returned IEnumerable to the View.
Don't know if it'll help you with what you're trying to do -- especially being a month late -- but I thought I'd let you know how I handled it. If you're displaying completely different formats, it probably won't do the trick but maybe it can get you a starting point.
I've got a Fitnesse RowFixture that returns a list of business objects. The object has a field which is a float representing a percentage between 0 and 1. The consumer of the business object will be a web page or report that comes from a designer, so the formatting of the percentage will be up to the designer rather than the business object.
It would be nicer if the page could emulate the designer when converting the number to a percentage, i.e. instead of displaying 0.5, it should display 50%. But I'd rather not pollute the business object with the display code. Is there a way to specify a format string in the RowFixture?
You certainly don't want to modify your Business Logic just to make your tests look better. Good news however, there is a way to accomplish this that is not difficult, but not as easy as passing in a format specifier.
Try to think of your Fit Fixture as a service boundary between FitNesse and your application code. You want to define a contract that doesn't necessarily have to change if the implementation details of your SUT (System Under Test) change.
Lets look at a simplified version of your Business Object:
public class BusinessObject
{
public float Percent { get; private set; }
}
Becuase of the way that a RowFixture works we need to define a simple object that will work as the contract. Ordinarily we would use an interface, but that isn't going to serve our purpose here so a simple DTO (Data Transfer Object) will suffice.
Something Like This:
public class ReturnRowDTO
{
public String Percent { get; set; }
}
Now we can define a RowFixture that will return a list of our custom DTO objects. We also need to create a way to convert BusinessObjects to ReturnRowDTOs. We end up with a Fixture that looks something like this.
public class ExampleRowFixture: fit.RowFixture
{
private ISomeService _someService;
public override object[] Query()
{
BusinessObject[] list = _someService.GetBusinessObjects();
return Array.ConvertAll(list, new Converter<BusinessObject, ReturnRowDTO>(ConvertBusinessObjectToDTO));
}
public override Type GetTargetClass()
{
return typeof (ReturnRowDTO);
}
public ReturnRowDTO ConvertBusinessObjectToDTO(BusinessObject businessObject)
{
return new ReturnRowDTO() {Percent = businessObject.Percent.ToString("%")};
}
}
You can now change your underlying BusinessObjects around without breaking your actual Fit Tests. Hope this helps.
I'm not sure what the "polution" is. Either the requirement is that your Business Object returns a value expressed as a percentage, in which case your business object should offer that -OR- you are testing the true value of the response as float, which you have now.
Trying to get fitnesse to massage the value for readability seems a bit odd.