Pass #Published where #Binding is required (SwiftUI, Combine) - data-binding

A ViewModel class has a sourceProperty that is being edited by the TextField. That property is #Published. I'd like to pass it to the Logic class which has an initializer with Binding<String>. That class will be listening to the sourceProperty changes, react on them and set it's output to the #Published output property.
How can I pass #Published sourceProperty as an initializer parameter to the Logic class?
Relevant code:
final class ViewModel {
#Published var sourceProperty: String = ""
private var logic: Logic?
init() {
self.logic = Logic(data: $sourceProperty)
$logic.output.sink({result in
print("got result: \(result)")
})
}
}
final class Logic: ObservableObject {
private var bag = Set<AnyCancellable>()
#Published var output: String = ""
#Binding var data: String
init(data: Binding<String>) {
self._data = data
$data.sink({ newValue in
output = newvalue + "ABCDE"
}).store(in: &bag)
}
}
So far I'm getting the following error:
Cannot convert value of type 'Published.Publisher' to expected
argument type 'Binding'
The goal is to use a change in the object's own property to trigger a method invocation in another object and then bind that second object's output to some view.
View Layer:
public struct ViewLayer: View {
#Binding private var sourceProperty: String
public init(_ placeholder: String,
sourceProperty: Binding<String>,
) {
self.placeholder = placeholder
self._sourceProperty = sourceProperty
}
public var body: some View {
TextField(placeholder, text: $sourceProperty)
}
}

If I understand your question correctly, you are probably looking for something like that:
final class ViewModel: ObservableObject {
#Published var sourceProperty: String = ""
private lazy var logic = Logic(data: $sourceProperty)
private var cancellable: AnyCancellable?
init() {
cancellable = logic.$output
.sink { result in
print("got result: \(result)")
}
}
}
final class Logic: ObservableObject {
#Published private(set) var output: String = ""
init(data: Published<String>.Publisher) {
data
.map { $0 + "ABCDE" }
.assign(to: &$output)
}
}

Related

how to send file along with data in formdata in angular 10

i want to send data in FormData along side with Files in the same object.
i have an object
var dataForm : FormData = new FormData();
var objectToSend : any = {};
objectToSend.object1 = {
Name:'Name',
ID: 1
};
objectToSend.Arr = [1,2,3];
i 've found out how to put a file in the FromData
this.dataForm.append('file', <File>files[0], (<File>files[0]).name);
but when putting the data object in it the same way i put the file like this ...
var stringfiedObejct = JSON.stringify(objectToSend);
this.dataForm.append('data', new Blob([stringfiedObejct], { type: 'application/json' }));
and the call like
this.Http.post(`apiPath`, this.dataForm, { reportProgress: true, observe: 'events' })
.subscribe(event => {
});
it generate error 415 Unsupported Media although the api method has the same definition for the object
public class DataObject
{
public int ID {get;set;}
public string Name {get;set;}
}
public class RequestParams
{
public List<int> Arr {get;set;}
public DataObject object1 {get;set;}
}
and the api is ...
[HttpPost("uploadFile"), DisableRequestSizeLimit]
public IActionResult Upload(RequestParams param)
{
var file = Request.Form.Files[0];
return Ok();
}
although the file is received in the api, but as soon i put the object in the FormData it generate that error
If you want to pass object in formdata,you can do like this:
DemoModel:
public class DemoModel
{
public IList<IFormFile> Images{ get; set; }
public DataObject object1 {get;set;}
}
pass object to FormData:
var pdata = new FormData();
pdata.append('object1.Name', 'Name');
pdata.append('object1.ID', 1);

Can I transform AnyPublisher to Published.Publisher?

So I have a value of type AnyPublisher<Foo, Error> and I need to transform it to Published<Foo>.Publisher so that I can use it as #Published
so the idea is that I have a MVVM architecture
struct SwiftUIView: View {
#ObservedObject var viewModel: MyViewModel
var body: some View {
Text(/*Foo has a title, how do I access it? */)
}
}
class MyViewModel: ObservableObject {
var cellModels: AnyPublisher<Foo, Error>
// so the idea is to have and then access it in swiftUI view via viewModel.cellModels2.title
#Published cellModels2 = convert_somehow_to_publisher(cellModels)
}
Apparently I cannot use viewModel.cellModels
Is that possible?
Looks like what you need is to have a Foo property on your view model, and update it using the AnyPublisher<Foo, Error> publisher that you have.
class MyViewModel: ObservableObject {
var cellModels: AnyPublisher<Foo, Error> = ...
private var cancellable: AnyCancellable?
#Published var foo: Foo?
init() {
cancellable = cellModels.assign(to: \.foo, on: self)
}
}
Then you can access .foo in the view normally:
struct SwiftUIView: View {
#ObservedObject var viewModel: MyViewModel
var body: some View {
Text(viewModel.foo?.title ?? "")
}
}

How to implement class in TypeScript?

Could you help me to create class 'MyClass'
class M should be Newable and implement IMyInterface
export interface IMyInterface<A>
{
SomeData : A;
}
export class MyClass<T,M inherits IMyInterface<T> and new() >
{
list = new Array<M>();
privete Creator()
{
const obj = new M();
obj.SameData = 'Hello data';
list.push( obj );
}
}
Think you're looking for something like this...
export interface IMyInterface<A> {
SomeData: A;
}
export class MyClass<T, M extends IMyInterface<T>> {
private list: M[] = [];
constructor(private constructorFunction: {new(): M; }) {
}
public add(item: T): void {
const obj = new this.constructorFunction();
obj.SomeData = item;
this.list.push(obj);
}
}
export class MyItem implements IMyInterface<string> {
public SomeData: string = '';
}
const collection = new MyClass<string, MyItem>(MyItem);
collection.add('Hello data');
I've tweaked your psuedocode so that it actually compiles and does what I think you were aiming for in the question. The important thing to note is that the types in TypeScript have no representation at runtime so you can't do new T(). Instead you need to pass in the constructor function for your class which has a type of { new(): M; }. You can then do a new X() with this value to get an object which extends your interface.

ASP.NET MVC custom client date validation, how to get the input name?

This is the simplified model :
public class Person {
public int Id;
public string Name;
}
public class Task {
public int Id;
public int PersonId;
public DateTime StartDate;
[GreaterThan("StartDate")]
public DateTime EndDate;
}
To validate the EndDate >= StartDate, i write general GreaterThanAttribute. The server side is trivial, but i have problem on client side validation.
My GreaterThanAttribute got the other property (ex: "StartDate") from constructor, then i pass this other property name to javascript as validation rule. But it won't work, because the JS will not found this element, because MVC will render & named it as "Task.StartDate", not "StartDate".
My question is, how i can get the prefix which will be used by the controller to render my model inside IClientValidatable.GetClientValidationRules() ?
Thanks
Here's how you could implement it:
public class GreaterThanAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public GreaterThanAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, _otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(
string.Format(
CultureInfo.CurrentCulture,
"unknown property {0}",
_otherProperty
)
);
}
var otherValue = (DateTime)property.GetValue(validationContext.ObjectInstance, null);
var thisValue = (DateTime)value;
if (thisValue <= otherValue)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "greaterthandate";
rule.ValidationParameters["other"] = "*." + _otherProperty;
yield return rule;
}
}
and on the client side:
(function ($) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
};
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
};
$.validator.unobtrusive.adapters.add('greaterthandate', ['other'], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name=' + fullOtherName + ']')[0];
options.rules['greaterThanDate'] = element;
if (options.message) {
options.messages['greaterThanDate'] = options.message;
}
});
$.validator.addMethod('greaterThanDate', function (value, element, params) {
var otherDate = $(params).val();
var thisDate = value;
// TODO: put your custom date comparison implementation here between
// the 2 values. Be careful here. Javascript date handling is culture dependent
// so you might need to account for that when building your js Date instances
return false;
}, '');
})(jQuery);
This has already been done. I suggest you use FoolProof validation. If you do not want to, you can at least check out their soure code. Here's a link

Get a static property from class in actionscript

I have this class
package somePackage
{
public class SomeClass
{
public static const FOO: SomeClass = new SomeClass("0");
public static const BAR: SomeClass = new SomeClass("1");
}
}
I want to be able to get those static property given it's name.
Example:
public static function getProperty(propertyName: String): SomeClass {
//don't know what goes here
}
var property1:SomeClass = SomeClass.getProperty("FOO"); // property1 == SomeClass.FOO
var property2:SomeClass = SomeClass.getProperty("BAR"); // property2 == SomeClass.Bar
You could use square brackets like this:
SomeClass['FOO']
Or if you want to put it in a method that returns a typed object:
public static function getProperty(propertyName: String):SomeClass {
return SomeClass[propertyName]
}

Resources