Assume i have a costum actionscript class.
public class myClass
{
private var myVariable:ArrayCollection;
...
}
Suppose also that i have a different class, that changes a second variable, which has the metadatatag [Bindable]. What methods and events do i have to implement in either of these classes, to make myVariable change, whenever the other is changend?
If you make the myVariable public, then you can just use [BindingUtils.bindProperty()][1]:
public class MyClass
{
public var myVariable:ArrayCollection;
public function MyClass(other:OtherClass) {
BindingUtils.bindProperty(this, "myVariable", other, "propertyName");
}
}
If you prefer to keep myVariable private, then you can use [BindingUtils.bindSetter()][2]:
public class MyClass
{
private var myVariable:ArrayCollection;
public function MyClass(other:OtherClass) {
BindingUtils.bindSetter(
function(newVal:*):void {
this.myVariable = newVal;
}, other, "propertyName");
}
}
Related
I have different configurations all inheriting from a base configuration that are customized in forms. I want all of these to be handled by a single action result.
[HttpPost]
public IActionResult Register(AbstractBaseConfig config)
{
...do some logic...
return View("../Home/Index");
}
However, this is not possible because you cannot base in abstract classes as a parameter to an action result. Is there any work around for this so I don't need a seperate action result for each configuration? (I still want each configuration to be it's own class, I only need access to the base class methods in the action result logic).
Basically you can't, and the reason is that MVC will try to do new AbstractBaseConfig() as part of the Data Binding process (which parses the URL or the Form Post and puts the results in a concrete object). And by definition, doing new AbstractBaseConfig() is impossible for an abstract class.
It also makes sense for other reasons, I will explain why.
You seem to expect that MVC can determine the class from the parameters that are being passed in. That is not how it works, in fact the opposite is true: the Action Method has to specify the exact class, and then the Binder will instantiate that exact class and try to bind its properties.
Suppose you had this:
public abstract class Thing { public int ID { get;set; } }
public class NamedThing : Thing { public string Name { get;set; } }
public class OtherThing : Thing { public string Name { get;set; } }
and suppose it would be allowed to use:
public IActionResult Register(Thing thing)
then what would you expect to be in thing after Data Binding: a Thing object with only the ID set? Or one of the other object types, with Name set, but how would MVC ever be able to know which class you meant?
So for all these reasons, this is not possible.
You could have a base class inherit the abstract class and all your classes inherit from that base class whilst having that base class as your parameter
Take for example
public abstract class ABase
{
public void stuff()
{
var stuff = string.Empty;
stuff = "hello";
}
public virtual void otherstuff()
{
var stuff = string.Empty;
stuff = "hello";
}
}
public class Base : ABase
{
//empty
}
public class Derived : Base
{
public void mystuff()
{
this.stuff();
}
public override void otherstuff()
{
// Custom code
}
}
public ActionResult Register(Base config)
{
}
I don't know how to define a callback that doesn't need any parameter. In order to describe the question, let me make up a setup - mine is different but contains several more classes, harder to digest.
Lets say we have
public class CustomerA {
public Payment payWithCash() {...}
}
and
public class CustomerB {
public Payment payWithCreditCard() {...}
}
and in addition
public abstract class Factory {
public Callback<Void, Payment> getPaymentCallback();
// Some other methods
...
}
then here comes the problem: I'd like to implement something like this
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and in a different class
public class CashFactoryB extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
What happens is, that the compiler complains, that CustomerB does not define payWithCreditCard(Void) and CustomerA likewise fails for payWithCash(Void).
So how to state this correct that there is no parameter to the Callback?
I am aware that I could probably solve my problem as well with interfaces, but I like to understand how to solve this with a Callback.
Thank you in advance!
Consider making Factory generic:
public abstract class Factory<T> {
public Callback<T, Payment> getPaymentCallback();
// Some other methods
...
}
And then you can do
public class CashFactoryA extends Factory<CustomerA> {
public Callback<CustomerA, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and
public class CashFactoryB extends Factory<CustomerB> {
public Callback<CustomerB, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
EDIT:
This answer is a wrong approach.
First, you are trying to refer payWithCash from not a instance of CustomerA but the class CustomerA, so payWithCash must be static method.
public class CustomerA {
public static Payment payWithCash() {...}
}
Then use lambda expression which is the same interface as your Callback.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return param -> CustomerA.payWithCash();
}
}
The code above has the same meaning as the code below. The param argument will be ignored as a result.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return new Callback<Void, Payment>(){
#Override
public Payment call(Void param) {
return CustomerA.payWithCash();
}
};
}
}
in my implementation, I have an interface as: ICachingManager. I've got now one implementation. I also created a manager class as:
public class CachingManager
{
#region Members
private ICachingManager service;
#endregion
#region Constructors
public CachingManager(ICachingManager service)
{
this.service = service;
}
#endregion
#region Public Methods
public void EnCache<T>(string key, T value)
{
this.service.EnCache<T>(key, value);
}
public T DeCache<T>(string key)
{
return this.service.DeCache<T>(key);
}
#endregion
}
In case I had one implementation, then I can easily register the CachingManager class with Unity, automatically Unity resolves and injects the ICachingManager.
In case I had more than one implementation using named types, then how can I can make use of Unity? Do I need to make use of an Abstract Factory to decide on which named type to initialize?
Is it a good idea to make use of such a composite class or use directly implementations of the interface with Abstract Factory?
You don't have to create an abstract factory. You can inject a given named implementation:
public class MyClient
{
[Dependency("NamedManager")]
public ICachingManager CachingManager { get; set; }
// or in the constructor
public MyClient([Dependency("NamedManager")] ICachingManager cachingManager) {
// ...
}
}
or you can configure the container to do the same thing:
public class MyClient
{
public MyClient(ICachingManager cachingManager) {
// ...
}
}
...
void ContainerBuilder() {
...
Container.RegisterType<MyClient>(
new InjectionConstructor(
new ResolvedParameter<ICachingManager>("NamedManager")));
...
}
I have a class like this. Property "isPag" is based on filed "ecboardid", I found that when ecboardid is changed, UI controls seem not be able to detect that "isPag" is also changed. So, how to make a property like this bindable?
[Bindable]
public class Encoder extends EventDispatcher
{
public var ecboardid : String;
/*-.........................................Methods..........................................*/
public function copyFrom(newEncoder:Encoder):void
{
ecboardid = newEncoder.ecboardid;
this.dispatchEvent(new Event('isPagChanged'));
}
[Bindable (event="isPagChanged")]
public function get isPag():Boolean
{
if(this.ecboardid != null)
{
if(this.ecboardid.search('xxx') != -1)
{
return false;
}
return true;
}
else
{
return false;
}
}
}
Edit:
If I change the property to a static function,
[Bindable]
public class Encoder extends EventDispatcher
{
public var ecboardid : String;
/*-.........................................Methods..........................................*/
public function copyFrom(newEncoder:Encoder):void
{
ecboardid = newEncoder.ecboardid;
this.dispatchEvent(new Event('isPagChanged'));
}
public static function isPag(String ecboardid ):Boolean
{
if(ecboardid != null)
{
if(ecboardid.search('xxx') != -1)
{
return false;
}
return true;
}
else
{
return false;
}
}
}
Will bind like this :
visible = {Encoder.isPag(encoder.ecboardid)}
work? Will visible change when encoder.ecboardid change?
I don't believe you can make read only properties Bindable, since Flex uses calls to the setter method for a property to issue the change events. Take a look here at the section on binding to properties of objects.
You need to have a setter as well as a getter.
Also, you have the class marked as bindable, if you weren't specifying the event name, then the [Bindable] on the method would be redundant.
Usually the compiler complains if you have just a getter and no setter and try to mark it [Bindable]
Code like this:
visible = {Encoder.isPag(encoder.ecboardid)}
Isn't how binding works. The {var} notation is for use in MXML. And isPag isn't static, so you can't refer to it as a class property. You need an instance of the Encoder class for this to be legal.
This is a simple change - just create getters/setters and dispatch your change event in the setter (after changing the value), like this:
private var _ecboardid : String;
public function get ecboardid():String
{
return _ecboardid;
}
public function set ecboardid(value:String):void
{
_ecboardid = value;
this.dispatchEvent(new Event('isPagChanged'));
}
My Grails application has a large number of enums that look like this:
public enum Rating {
BEST("be"), GOOD("go"), AVERAGE("av"), BAD("ba"), WORST("wo")
final String id
private RateType(String id) {
this.id = id
}
static public RateType getEnumFromId(String value) {
values().find {it.id == value }
}
}
If I have a command object such as this:
class MyCommand {
Rating rating
}
I would like to (for example) automatically convert a request parameter with value "wo" to Rating.WORST.
The procedure for defining custom converters is described here (in the context of converting Strings to Dates). Although this procedure works fine, I don't want to have to create a class implementing PropertyEditorSupport for each of my enums. Is there a better alternative?
I found a solution I'm pretty happy with.
Step 1: Create an implementation of PropertyEditorSupport to convert text to/from the relevant Enum
public class EnumEditor extends PropertyEditorSupport {
private Class<? extends Enum<?>> clazz
public EnumEditor(Class<? extends Enum<?>> clazz) {
this.clazz = clazz
}
public String getAsText() {
return value?.id
}
public void setAsText(String text) {
value = clazz.getEnumFromId(text)
}
}
Step 2: Define a class that registers EnumEditor as a converter for the various enum classes. To change the list of enum classes that are bindable by id, just modify BINDABLE_ENUMS
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
private static final String REQUIRED_METHOD_NAME = 'getEnumFromId'
// Add any enums that you want to bind to by ID into this list
private static final BINDABLE_ENUMS = [Rating, SomeOtherEnum, SomeOtherEnum2]
public void registerCustomEditors(PropertyEditorRegistry registry) {
BINDABLE_ENUMS.each {enumClass ->
registerEnum(registry, enumClass)
}
}
/**
* Register an enum to be bound by ID from a request parameter
* #param registry Registry of types eligible for data binding
* #param enumClass Class of the enum
*/
private registerEnum(PropertyEditorRegistry registry, Class<? extends Enum<?>> enumClass) {
boolean hasRequiredMethod = enumClass.metaClass.methods.any {MetaMethod method ->
method.isStatic() && method.name == REQUIRED_METHOD_NAME && method.parameterTypes.size() == 1
}
if (!hasRequiredMethod) {
throw new MissingMethodException(REQUIRED_METHOD_NAME, enumClass, [String].toArray())
}
registry.registerCustomEditor(enumClass, new EnumEditor(enumClass))
}
}
Step 3: Make Spring aware of the registry above by defining the following Spring bean in grails-app/conf/spring/resources.grooovy
customPropertyEditorRegistrar(CustomPropertyEditorRegistrar)
So the default Databinding binds on the Enum name and not a separately defined property of the Enum. You can either create your own PropertyEditor as you have mentioned or do a work-around similar to this:
class MyCommand {
String ratingId
Rating getRating() {
return Rating.getEnumFromId(this.ratingId)
}
static constraints = {
ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
}
}