ReactiveUI WhenAnyObservable doesn't work for me - reactiveui

My app is a winforms application and it references ReactiveUI 6.5. I'm trying to close window when user type "Exit" in a TextBox, but for some reason nothing happens.
This is my View:
public partial class HomeView : Form, IViewFor<HomeViewModel>
{
public HomeView()
{
InitializeComponent();
ViewModel = new HomeViewModel();
this.Bind(ViewModel, x => x.EnteredText, x => x.textBox.Text);
this.ViewModel.WhenAnyObservable(x => x.ExitCmd).Subscribe(_ => this.Close());
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (HomeViewModel)value; }
}
public HomeViewModel ViewModel { get; set; }
}
And this is my ViewModel:
public class HomeViewModel : ReactiveUI.ReactiveObject
{
string _text;
public string EnteredText
{
get { return _text; }
set { this.RaiseAndSetIfChanged(ref _text, value); }
}
public ReactiveCommand<object> ExitCmd { get; private set; }
public HomeViewModel()
{
ExitCmd = ReactiveCommand.Create(this.WhenAny(x => x.EnteredText, y => y.Value == "Exit"));
}
}

In your ViewModel, you should describe the relationship between your Exit Command and EntertedText
public class HomeViewModel : ReactiveUI.ReactiveObject
{
private string _text;
public string EnteredText
{
get { return _text; }
set { this.RaiseAndSetIfChanged(ref _text, value); }
}
public ReactiveCommand<object> ExitCmd { get; private set; }
public HomeViewModel()
{
ExitCmd = ReactiveCommand.Create();
this.WhenAny (x => x.EnteredText, x => x.Value == "Exit")
.Where(k => k == true)
.DistinctUntilChanged()
.InvokeCommand (ExitCmd);
}
}
Then in your view, you can simply subscribe it
public partial class HomeView : Form, IViewFor<HomeViewModel>
{
public HomeView()
{
InitializeComponent();
ViewModel = new HomeViewModel();
this.Bind(ViewModel, x => x.EnteredText, x => x.textBox.Text);
this.ViewModel.ExitCmd.Subscribe (_ => this.Close());
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (HomeViewModel)value; }
}
public HomeViewModel ViewModel { get; set; }
}

You've almost got it, except nothing is telling your ExitCmd to Execute. The value you're passing to ReactiveCommand.Create is the canExecute property. You should change it to this:
ExitCmd = ReactiveCommand.Create();
this.WhenAnyValue(x => x.EnteredText)
.Where(x => x == "Exit")
.InvokeCommand(ExitCmd);
InvokeCommand actually executes the command parameter, passing each value from the observable sequence into the ExecuteAsync method as the parameter.

Related

how to add and delete favorites in asp.net core api

I develop a news application with ASP.net API. I want users to be able to add and deleted their favorites articles. I created a Favorites model like this:
public class Favorites{
public String userId {get; set;}
public UserEntity User {get; set;}
public int articleId {get; set; }
public virtual Article article {get; set;}
}
I initialized it with dotnet fluentapi:
modelBuilder.Entity<Favorites>().HasKey(a => new{ a.articleId, a.userId });
this is how the controller looks like :
public class FavoritesController : ControllerBase
{
private readonly DatabaseContext _context;
public FavoritesController(DatabaseContext context)
{
_context = context;
_context.Database.EnsureCreated();
}
// GET: api/Favorites
[HttpGet(Name = nameof(GetAllFavorites))]
public async Task<ActionResult<IEnumerable<FavoriteDTo>>> GetAllFavorites( [FromQuery] NewRoomQueryParameters queryParameter)
{
IQueryable<Favorites> favs = _context.Favorites;
if (!string.IsNullOrEmpty(queryParameter.sortBy))
{
if (typeof(Favorites).GetProperty(queryParameter.sortBy) != null)
{
favs = favs.OrderByCustom(queryParameter.sortBy, queryParameter.SortOrder);
}
}
if (!string.IsNullOrEmpty(queryParameter.userId))
{
favs = favs.Where(p => p.userId == queryParameter.userId);
}
return await favs.Include(a => a.Article)
.ThenInclude(a => a.Author)
.Include(a => a.Article)
.ThenInclude(a => a.Comments)
.Select(x => favoriteToDTo(x)).ToListAsync();
}
// GET: api/Favorites/5
[HttpGet("{id}")]
public async Task<ActionResult<FavoriteDTo>> GetFavorites(int id)
{
IQueryable<Favorites> favs = _context.Favorites;
var favorites = await favs.Include(x => x.Article).FirstOrDefaultAsync(x => x.articleId == id ) ;
if (favorites == null)
{
return NotFound();
}
return favoriteToDTo(favorites);
}
// POST: api/Favorites
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<FavoriteDTo>> PostFavorites(FavoriteDTo favDTo)
{
var fav = new Favorites
{
articleId = favDTo.articleId,
userId = favDTo.userId
};
_context.Favorites.Add(fav);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (FavoritesExists(fav.articleId))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtAction(nameof(GetFavorites), new { id = fav.articleId }, fav);
}
// DELETE: api/Favorites/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteFavorites(int id)
{
var favorites = await _context.Favorites.FindAsync(id);
if (favorites == null)
{
return NotFound();
}
_context.Favorites.Remove(favorites);
await _context.SaveChangesAsync();
return NoContent();
}
private static FavoriteDTo favoriteToDTo(Favorites favorites) => new FavoriteDTo
{
articleId = favorites.articleId,
Article = favorites.Article,
User = favorites.User,
userId = favorites.userId
};
private bool FavoritesExists(int id)
{
return _context.Favorites.Any(e => e.articleId == id);
}
}
I can add Favorites just fine. But I cannot remove them. Can somebody help me out ? if it is not the good
way to implement this functionality , please I would like to learn how to do it the right way.
It depends on How you want to delete the favorite. If you want to delete the Article from Favorite for a user,
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteFavoritesForUser(int Articleid, string userId)
{
var favorites = await _context.Favorites.FindBy(x=>x.UserId ==userId && x=>x.ArticleId ==Articleid);
if (favorites == null)
{
return NotFound();
}
_context.Favorites.RemoveRange(favorites);
await _context.SaveChangesAsync();
return NoContent();
}
Replace
_context.Favorites.Remove(favorites);
with
_context.Favorites.RemoveRange(favorites);

Complex Custom Validation in Foolproof Validation

I have implemented Complex custom Foolproof validation in my application from this link but sadly its not working.My requirement is simple,I have a input file for uploading an image and there should be a validation if the user chooses to upload file other than specified below
".jpg",".png",".gif",".jpeg"
Code is
[Required(ErrorMessage = "Please upload Photo", AllowEmptyStrings = false)]
[IsValidPhoto(ErrorMessage="Please select files of type .jpg,.png,.gif,.jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }
public class IsValidPhotoAttribute : ModelAwareValidationAttribute
{
//this is needed to register this attribute with foolproof's validator adapter
static IsValidPhotoAttribute() { Register.Attribute(typeof(IsValidPhotoAttribute)); }
public override bool IsValid(object value, object container)
{
if (value != null)
{
string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png", ".jpeg" };
var file = value as HttpPostedFileBase;
if (!AllowedFileExtensions.Contains(file.FileName.Substring(file.FileName.LastIndexOf('.'))))
{
return false;
}
}
return true;
}
}
CSHTML is
#Html.TextBoxFor(m => m.PhotoUrl, new { #class = "form-control imgUpload",
#placeholder = "Please upload Photo", #id = "txtPhoto", #type = "file" })
#Html.ValidationMessageFor(m => m.PhotoUrl)
You will not be able to get client side validation unless you also create a script to add the rules. It is not necessary to use foolproof and the following method and scripts will give you both server and client side validation
public class FileAttachmentAttribute : ValidationAttribute, IClientValidatable
{
private List<string> _Extensions { get; set; }
private const string _DefaultErrorMessage = "Only file types with the following extensions are allowed: {0}";
public FileAttachmentAttribute(string fileExtensions)
{
_Extensions = fileExtensions.Split('|').ToList();
ErrorMessage = _DefaultErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
HttpPostedFileBase file = value as HttpPostedFileBase;
if (file != null)
{
var isValid = _Extensions.Any(e => file.FileName.EndsWith(e));
if (!isValid)
{
return new ValidationResult(string.Format(ErrorMessageString, string.Join(", ", _Extensions)));
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "fileattachment",
ErrorMessage = string.Format(ErrorMessageString, string.Join(", ", _Extensions))
};
rule.ValidationParameters.Add("extensions", string.Join(",", _Extensions));
yield return rule;
}
}
Scripts
$.validator.unobtrusive.adapters.add('fileattachment', ['extensions'], function (options) {
var params = { fileattachment: options.params.extensions.split(',') };
options.rules['fileattachment'] = params;
if (options.message) {
options.messages['fileattachment'] = options.message;
}
});
$.validator.addMethod("fileattachment", function (value, element, param) {
var extension = getExtension(value);
return $.inArray(extension, param.fileextensions) !== -1;
});
function getExtension(fileName) {
var extension = (/[.]/.exec(fileName)) ? /[^.]+$/.exec(fileName) : undefined;
if (extension != undefined) {
return extension[0];
}
return extension;
};
and then use it as
[FileAttachment("jpg|gif|png|jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }

Bind local data to kendo grid and make it sortable

I am trying to setup kendo mvc ui grid using local data and make it sortable as in this demo Binding to local data.
However grid doesn't show data and if I hook up the onDataBound event, data inside there is undefined.
If I comment out DataSource setup on view, data gets populated at first, but disappears after sorting performed on any column.
I am using Kendo UI version: "2013.3.1119", asp.net mvc 4.
Please advice.
Thanks.
view
#model TestViewModel
#(Html.Kendo().Grid(Model.TestList)
.Name("grid2")
.Columns(columns =>
{
columns.Bound( p => p.Id ).Title( "ID" );
columns.Bound( p => p.Name ).Title("Product Name");
})
.Pageable()
.Sortable()
.Scrollable(scr=>scr.Height(430))
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.ServerOperation(false))
)
controller and model
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index()
{
var mdl = new TestViewModel();
mdl.TestList = Test().ToList();
return View(mdl);
}
public IEnumerable<TestMe> Test()
{
var lst = new List<TestMe>();
for( int i = 0; i < 5; i++ )
{
lst.Add( new TestMe
{
Id = i,
Name = i.ToString(),
} );
}
return lst;
}
}
public class TestViewModel
{
public List<TestMe> TestList { get; set; }
public TestViewModel()
{
TestList = new List<TestMe>();
}
}
public class TestMe
{
public int Id { get; set; }
public string Name { get; set; }
}

Asp.net MVC3 custom validator created using dataannotation showing messages in incorrect place

I am using Asp.net MVC3, razor view engine and data annotation for model validation.
I have a form in which have to input Url details(Url and Description).Both fields are not required. But if i input one field other must be required.If i input description Url is required and is in correct format and if i enter Url then description is required.
I created a customvalidator for data annotation .It validates and output error message.
But my problem is error message generated by ValidationMessageFor is in incorrect place.
ie,if i enter description ,the required url message, is part of description.
I expect that message as part of ValidationMessageFor url field.
Can any one can help me? Thanks in advance. Folowing are the code i used
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class IsExistAttribute : ValidationAttribute, IClientValidatable
{
private const string DefaultErrorMessage = "{0} is required.";
public string OtherProperty { get; private set; }
public IsExistAttribute (string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherProperty);
}
protected override ValidationResult IsValid(object value,ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
var strvalue=Convert.ToString(otherPropertyValue)
if (string.IsNullOrEmpty(strvalue))
{
//return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,ControllerContext context)
{
var clientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "isexist"
};
clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
return new[] { clientValidationRule };
}
}
in model
[Display(Name = "Description")]
[IsExist("Url")]
public string Description { get; set; }
[Display(Name = "Url")]
[IsExist("Description")]
[RegularExpression("(http(s)?://)?([\w-]+\.)+[\w-]+(/[\w- ;,./?%&=]*)?", ErrorMessage = "Invalid Url")]
public string Url { get; set; }
and in view
<div class="editor-field">
#Html.TextBoxFor(m => m.Description )
#Html.ValidationMessageFor(m => m.Description)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Url)
#Html.ValidationMessageFor(m => m.Url)
</div>
unobstrusive validation logic
(function ($) {
$.validator.addMethod("isexist", function (value, element, params) {
if (!this.optional(element)) {
var otherProp = $('#' + params)
return (otherProp.val() !='' && value!='');//validation logic--edited by Rajesh
}
return true;
});
$.validator.unobtrusive.adapters.addSingleVal("isexist", "otherproperty");
} (jQuery));
You should make the validation the other way round. Change:
if (string.IsNullOrEmpty(otherPropertyValue))
{
//return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});
}
To:
if (string.IsNullOrEmpty(Convert.ToString(value)) && !string.IsNullOrEmpty(otherPropertyValue))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
And remove if (value != null)
The field that will then get invalidated is the empty one, in the case the other one is filled.

Nested EditorTemplates in Telerik MVC Grid Edit Popup Window Not Displaying in Custom Edit Template

I have a grid using AJAX DATABINDING.
When I issue a POPUP EDITING command with TEMPLATENAME SPECIFIED my NESTED EDITOR TEMPLATES are not populating.
My Models
namespace eGate.BackOffice.WebClient.Model
{
public class TemplateTesterModel
{
public int TemplateModelId { get; set; }
public string TemplateModelName { get; set; }
public List<UserRole> UserRoles { get; set; }
}
}
{
public class TemplateTesterModels : List<TemplateTesterModel>
{
}
}
My View
#model eGate.BackOffice.WebClient.Model.TemplateTesterModels
#Html.EditorFor(m=>m)
#( Html.Telerik().Grid<eGate.BackOffice.WebClient.Model.TemplateTesterModel>()
.Name("Grid")
.DataKeys(keys => { keys.Add(m=>m.TemplateModelId); })
.Columns(columns =>
{
columns.Bound(o => o.TemplateModelId);
columns.Bound(o => o.TemplateModelName).Width(200);
columns.Bound(o => o.UserRoles).ClientTemplate(
"<# for (var i = 0; i < UserRoles.length; i++) {" +
"#> <#= UserRoles[i].RoleName #> <#" +
"} #>")
;
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Text);
}).Width(180).Title("Commands");
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_SelectAjaxEditing", "TemplateTester")
.Insert("_InsertAjaxEditing", "Grid")
.Update("_SaveAjaxEditing", "TemplateTester")
.Delete("_DeleteAjaxEditing", "TemplateTester")
)
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("TemplateTesterModel"))
)
My Controller
namespace eGate.BackOffice.WebClient.Controllers
{
public class TemplateTesterController : Controller
{
public ActionResult Index()
{
return View(GetTemplateTesters());
//return View(new GridModel(GetTemplateTesters()));
}
private TemplateTesterModels GetTemplateTesters() {
TemplateTesterModels returnme = new TemplateTesterModels();
returnme.Add(new TemplateTesterModel());
returnme[0].TemplateModelId = 0;
returnme[0].TemplateModelName = "Template Tester 0";
returnme[0].UserRoles = new List<UserRole>();
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role1", IsChecked = true, Description = "Role for 1" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role2", IsChecked = false, Description = "Role for 2" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role3", IsChecked = false, Description = "Role for 3" });
return returnme;
}
[GridAction]
public ActionResult _SelectAjaxEditing()
{
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _SaveAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
[GridAction]
public ActionResult _InsertAjaxEditing(){
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _DeleteAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
}
}
My EditorTemplates
Shared/EditorTemplates/TemplateTesterModel.cshtml
#model eGate.BackOffice.WebClient.Model.TemplateTesterModel
<div>TemplateTesterModel Editor</div>
<div>#Html.EditorFor(m=>m.TemplateModelId)</div>
<div>#Html.EditorFor(m=>m.TemplateModelName)</div>
<div>Roles</div>
<div>#Html.EditorFor(m=>m.UserRoles)</div>
Shared/EditorTemplates/UserRole.cshtml
#model eGate.BackOffice.WebClient.Model.UserRole
<div>
I can has user role?
#Html.CheckBoxFor(m=>m.IsChecked)
</div>
This renders out as such:
As you can see the #Html.EditFor statements that precede the grid filter down through to the userrole EditorTemplate as expected. Additionally we can see that role data is in the grid because it is showing up in the role column.
But click the edit window and this is the result:
As you can see the UserRoles template is not populating with the roles on the UserRoles property of the TemplateTesterModel we're editing.
Am I missing something? Why is the .UserRoles property not populating in the telerik grid pop-up window?
This could be a "by design" decision of ASP.NET MVC. It does not automatically render display and editor templates for nested complex objects. I even have a blog post discussing this.
Long story short you need to create a custom editor template for the parent model.

Resources