I am using #dynamicOptions in my Aurelia application to facilitate wrapping a data grid that has a ton of options.
But now I need to have the equivilant of :
#bindable({ defaultBindingMode: bindingMode.twoWay }) doStuffCallback: () => void;
but if I do that I get an error. I am guessing that #dynamicOptions fights with #bindable.
Is there a way to set up and (and set) a two way binding using #dynamicOptions?
Related
I would like to create a custom Binding Behavior that allows me to detect ANY changes to the properties of an object, like this:
<my-form model.bind="myObject & objectObserver:myObjChanged()"></my-form>
I know that I can use Aurelia's binding engine to create a property observer, and perhaps I can build this into a custom Binding Behavior to detect the properties of the object and create property observers for each one. But I can't make sense of the binding object that is given to me inside the custom Binding Behavior. Here's my code so far:
import { inject, bindingBehavior, BindingEngine } from 'aurelia-framework';
#bindingBehavior('objectObserver')
#inject(BindingEngine)
export default class ObjectObserverBindingBehavior {
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
}
bind(binding, scope, interceptor) {
console.warn('hello', binding, scope, interceptor);
}
unbind(binding, scope) {
console.warn('observer.unbind()', binding, scope);
}
}
When the bind happens and the console text is output, I see
So I know it's working, but I don't know what the best object is to start watching. I see the bound object inside targetObserver.currentValue. Is that the best property to start watching? Is there another way that utilizes existing functionality of the Aurelia Binding Engine?
I found a solution that is not Aurelia specific, based on the Proxy functionality built into Javascript.
export function onChangeObj(object, onChange) {
// creates Proxy to detect changes in object properties and call a function
if (typeof onChange !== 'function' || typeof object !== 'object') {
throw new Error('onChangeObj: incorrect parameters');
}
const handler = {
set(obj, prop, value) {
onChange(prop, value);
return Reflect.set(obj, prop, value);
},
};
return new Proxy(object, handler);
}
In order to use it, just call it like this:
this.myObject = onChangeObj(this.myObject, () => this.myObjChanged());
Effectively, the object is replaced by a wrapper Proxy that calls the provided function every time one of the properties is modified (with the setter).
If anyone finds a solution via Aurelia Binding Behavior, I would still be interested.
If I have a class
export class Person {
public name: string = "";
public foo: string = "";
}
ValidationRules
.ensure((p :Person) => p.name)
.required()
.withMessage("name is required")
.on(Person);
Is there any way to extend those rules on a per controller basis? For example in my App class I also want to ensure the foo property is set, however adding this as a rule here seems to override the name rule from the above code.
export class App {
public person: Person = new Person();
#observable
public message: string = "";
constructor(public vc: ValidationController, public vld: Validator) {
ValidationRules
.ensure((p: Person) => p.foo).required().withMessage("foo is required").on(this.person);
this.vc.addObject(this.person);
this.vc.validate();
}
}
Yes that's possible, but it requires a slightly different approach.
There are 2 important things to note here:
The fluent api initializer (static method ensure() on ValidationRules) always instantiates a new FluentEnsure object. It doesn't look for existing stuff - not even if you finalize on the same target. To extend a ruleset with more rules, you need to call .ensure() on the existing ruleset.
The finalizer (instance method on() on FluentEnsure) stores the rules in the .prototype.__rules__ (if it's a Function) or .__rules__ property of the target you pass in and will overwrite any existing one.
In other words when you finalize on an instance of Person, you're storing a brand new rules object on person.__rules__ which effectively hides Person.prototype.__rules__.
For the record, the static methods Rules.set(target, rules) and Rules.get(target) are wrappers around the .__rules__ property. You'll definitely want to call those and not try to access the property directly.
Now you might think something like Rules.get(Person).ensure(...).on(person) but that would also modify the original rules on Person.prototype.
So how to work with that?
Enter tags
It can get messy with fetching and combining rulesets, but here's the basic idea:
export class Person {}
ValidationRules.ensure(p => p.name).required().on(Person)
And elsewhere:
Rules.get(Person).ensure(p => p.foo).required().tag("foo");
And elsewhere:
Rules.get(Person).ensure(p => p.bar).required().tag("1234");
Then when it's time to validate:
const allRules = Rules.get(Person); // "1234" is included
const fooRules = ValidationRules.untaggedRules(allRules)
.concat(ValidationRules.taggedRules(allRules, "foo"); "1234" not included
vc.addObject(person);
vc.validate({ fooRules });
Or hand roll something
I've never actually used tags myself before and I've seen an issue or two regarding them. You could also do something similar yourself if you want more control/transparency:
export class Person {
public static scopedRules: { [scope: string]: any } = Object.create(null);
}
ValidationRules.ensure(p => p.name).required().on(Person)
And elsewhere:
Person.scopedRules.foo = Object.create(null);
ValidationRules.ensure(p => p.foo).required().on(Person.scopedRules.foo)
Then when it's time to validate:
const rules = Rules.get(Person).concat(Rules.get(Person.scopedRules.foo));
vc.addObject(person);
vc.validate({ rules });
Of course this is just a "simplest possible thing" example. In a real world scenario you'd probably have your rule storage/retrieval/merging etc tucked away somewhere.
I want to configure my ninject container using conventions AND create instances of all selected services at the same time. My current solution is:
var singletons = new List<Type>();
kernel.Bind(x =>
x.FromThisAssembly() // Scans currently assembly
.SelectAllClasses()
.WithAttribute<SingletonAttribute>()
.Where(type =>
{
var include = MySpecialFilterOfSomeSort(type);
if (include)
{
singletons.Add(type);
}
return include;
}) // Skip any non-conventional bindings
.BindDefaultInterfaces() // Binds the default interface to them
.Configure(c => c.InSingletonScope()) // Object lifetime is current request only
);
singletons.ForEach(s => kernel.Get(s));
MORE
I have an intra-process service bus. Some components are decorated with [Singleton] and will register themselves with the service bus:
// the constructor
public FooEventsListenerComponent(IServiceBus serviceBus) {
serviceBus.Subscribe<FooEvent>(e => HandleFooEvent(e));
}
I need a place in the app to create instances of all the service bus observers. Doing it next to type mapping is convenient (but is it appropriate?) because 1. types are already enumerated, 2. I have an access to the DI container.
In the case you describe i think it would make sense to make service bus registration explicit. To expand on my answer to your other question about conventions:
Create an interface for the listeners:
public interface IServiceBusSubscriber
{
void SubscribeTo(IServiceBus serviceBus);
}
then you can adapt your convention to bind all types which inherit from IServiceBusSubscriber to their default interface (then they must be named like FooServiceBusSubscriber and BarServiceBusSubscriber) or to IServiceBusSubscriber explicitly.
After the kernel is initialized with all bindings just do:
IServiceBus serviceBus = kernel.Get<IServiceBus>();
foreach(IServiceBusSubscriber subscriber in kernel.GetAll<IServiceBusSubscriber>())
{
subscriber.SubscribeTo(serviceBus)
}
I have used fluent validation for hard code validations like this:
RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);
I guess it would not be a problem to replace MinimumCreditLimit by some (meta) database driven value in the code. Did someone ever attempt this and what would be the best practises in this context (apart from the fact that MinimumCreditLimit could stem from some strategy design pattern). Could one potentially use expression trees against fluent validation to make it even more meta program-ish?
Well, the easiest way would be to add a ctor to your Validation class.
public class EntityValidator : AbstractValidator<Entity> {
public EntityValidator(int minimumCreditLimit) {
Rulefor(customer => customer.CreditLimit).GreaterThan(minimumCreditLimit);
}
}
now you could use it like that (if you don't use the "attributes" way).
var minimumCreditLimit = <GetTheLimitFromDbWithAnyMethod>();
var validator = new EntityValidator(minimumCreditLimit);
<yourEntityInstance>.ValidateAndThrow(validator);
Another (similar) way would be to pass some way to get data from db to your validator (in ctor for example), and create a custom validator / extension method to use this.
I have a class that has a password property that I want to store encrypted in the db. The property is a string type, and I have a custom type EncryptedStringType that I want NHibernate to use to map this to the database. Here is my relevant automapping code:
var mappings = AutoMap.AssemblyOf<Business>()
.Where(x=>x.IsSubclassOf(typeof(EntityBase)))
.IgnoreBase(typeof(EntityBase))
.Conventions.Add
(
ConventionBuilder.Id.Always(x =>
x.GeneratedBy.HiLo(HILO_TABLE, HILO_COLUMN, HILO_MAX_LO)),
ConventionBuilder.HasMany.Always(x => x.Cascade.AllDeleteOrphan()),
Table.Is(o => Inflector.Pluralize(o.EntityType.Name)),
PrimaryKey.Name.Is(o => "Id"),
ForeignKey.EndsWith("Id"),
DefaultLazy.Always(),
DefaultCascade.All()
);
I cannot figure out the syntax to override the type for the UserPassword property of the Business class though. I thought I should be able to do something with overrides like:
mappings.Override<Business>(map=> /* Not sure what to do here */);
Any help is appreciated.
Found the answer myself.
mappings.Override<Business>(map =>
{
map.Map(x => x.UserPassword).CustomType<EncryptedStringType>();
});
You could always create a mapping override class. Any conventions that can still be applied will be, but you can basically specify mappings similarly to a ClassMap that override the default conventions.
Using the call to mappings.Override(), it'd look something like:
mappings.Override<Business>(map=>map.Map(x=>x.UserPassword).CustomType(typeof(EncryptedStringType)));