When a user clicks on a date using the Calendar it will populate the grid dynamically with the times. If someone enters data and there is no corresponding record the grid will insert a new record if the user edits an existing row that will fire an update. How do we implement this?
The telerik components operate on a datasource behind the scenes. I find the jquery datasource examples to be helpful for working with them telerik jquery datasource
when you configure your grid and add the save option it will call the sync method on the datasource. the grid will track what types of changes you have made to the datasource ie insert,update,delete and will call the appropriate method for each change that you made.datasource configuration and events. you just need to be sure you have a method in your controller to do what you want with each type of change.
if your grid is setup like below
#(Html.Kendo().Grid<Mymodel>()
.Name("MyGrid")
.Columns(col =>
{
col.Bound(x => x.MyId).Hidden();
col.Bound(x => x.AnotherField).Title("Product Name");
col.Bound(x => x.Athirdfield);
col.Command(cmd => { cmd.Edit(); }).Title("Actions");
})
.ToolBar(tb => { tb.Save(); })
.Editable(ed => ed.Mode(GridEditMode.InLine))
.DataSource(ds => ds
.Ajax()
.Model(md =>
{
md.Id(m => m.MyId);
})
.PageSize(10)
.Read(read => read.Action("Read", "MyGrid"))
.Create(cr => cr.Action("Create","MyGrid"))
.Update(up => up.Action("Update", "MyGrid"))
.Destroy(de => de.Action("Delete", "MyGrid")))
.Filterable()
.Pageable()
)
Now when your grid sees that an insert (or creation) is necessary it will use the method and controller you identified in the .Create() method.
Then your controller just needs to handle the business logic of your operations like below (i only included a read and update method here)
public class MYGridController : Controller
{
private readonly dbContext _context;
public MyGridController(DB context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> Read([DataSourceRequest]DataSourceRequest request)
{
return Json(await _context.MyModel.ToDataSourceResultAsync(request));
}
public async Task<IActionResult> Update([DataSourceRequest]DataSourceRequest request, MyModel pm)
{
try
{
_context.Update(pm);
await _context.SaveChangesAsync();
return Json(await new[] { pm }.ToDataSourceResultAsync(request, ModelState));
}
catch (DbUpdateException)
{
ModelState.AddModelError("", "Error");
}
return Json(await new[] { pm }.ToDataSourceResultAsync(request, ModelState));
}
}
I hope this helps. I know how hard it can be to get the hang of these grids, but they are great once you understand how they work
Related
I am trying to follow DryIoc and IServiceProvider on Prism for Xamarin.Forms (DryIoc.Microsoft.DependencyInjection) however I am using RefitClient for IHttpClientFactory
containerRegistry.RegisterServices(services =>
{
services.AddTransient<HttpLoggingHandler>();
services.AddTransient<AuthorizationDelegatingHandler>();
services.AddRefitClient<IMyApi>()
.ConfigureHttpClient(c =>
c.BaseAddress =
new Uri(apiBaseUrl))
.AddHttpMessageHandler<AuthorizationDelegatingHandler>()
.AddHttpMessageHandler<HttpLoggingHandler>()
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromMilliseconds(300),
TimeSpan.FromSeconds(600),
TimeSpan.FromSeconds(800)
}))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
});
I have added
protected override IContainerExtension CreateContainerExtension() => PrismContainerExtension.Current;
When I try to make a request with IMyApi.
BaseAddress must be set on the HttpClient instance
at Refit.RequestBuilderImplementation+<>c__DisplayClass14_0`2[T,TBody].<BuildCancellableTaskFuncForMethod>b__0 (System.Net.Http.HttpClient client, System.Threading.CancellationToken ct, System.Object[] paramList) [0x00030] in /_/Refit/RequestBuilderImplementation.cs:236
I personally had to deal with all of that and ended up by creating Apizr where auth and logging handlers are built in, policies are resolved from registry and many more features like connectivity test, caching or prioritizing. If it could help.
I'm using Unity rather than Dryloc but the solution is the same.
The key seems very much to depend on installing the correct packages. Install ONLY these:
Prism.Forms.Extended
Prism.Unity.Extensions
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//Omitted Code
containerRegistry.RegisterServices(serviceCollection =>
{
serviceCollection.AddHttpClient<IApiService, ApiService>(client =>
{
client.BaseAddress = new Uri("Your Address Here");
});
});
}
public class ApiService : IApiService
{
Func<IApi> _createClient;
public ApiService(HttpClient client)
{
_createClient = () =>
{
return RestService.For<IApi>(client, new RefitSettings
{
ContentSerializer = new NewtonsoftJsonContentSerializer()
});
}
}
public IApi GetApi()
{
return new Lazy<IApi>(() => _createClient()).Value;
}
}
In part also provided by this post:
https://xamgirl.com/consuming-restful-web-service-xamarin-forms-using-refit-part-2/
I am having trouble with my CollectionView which is showing duplicates objects each time I display the page, meaning the first time I will have 1 object (which is normal), but if I navigate and return to that page it will duplicate the same object, so I get it twice, then thrice ..etc...
Below is the code in the constructor of the ViewModel of my page:
Cache
.AutoRefresh(x => x.Code)
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _list)
.DisposeMany();
_list is a ReadOnlyObservableCollection<AddressViewModel>
Cache is IObservable<IchangeSet<AddressModel, string>> obtained from a GetAllObjects from Akavache cache.
_blobCache
.GetAllObjects<AddressModel>()
.ToObservableChangeSet(t => t.Code)
.AsObservableCache()
.Connect();
The binding to my CollectionView is
this.OneWayBind(ViewModel, vm => vm.List, v => v.addressList.ItemsSource).DisposeWith(disposables);
Thank you for any help or hint
UPDATE
The duplicating behaviour happens ONLY when navigating between tabs, if I push a new page and go back I find again 1 object which is the intended behaviour.
Here is the code setting the BindingContext of the page
In code behind of the TabbedPage
public partial class MainTabbedPage : ReactiveTabbedPage<MainViewModel>
{
public MainTabbedPage()
{
InitializeComponent();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.AddressVm, x => x.addressView.ViewModel)
.DisposeWith(disposables);
//other tabs...
});
}
}
In the MainViewModel
public AddressViewModel AddressVm => new AddressViewModel(HostScreen);
public MainViewModel(IScreen hostScreen) : base(hostScreen)
{
}
I'm relatively new to ReactiveUI and I'm trying to asynchronously execute a database query from a ReactiveCommand. From what I can tell, the problem isn't with executing the async query, but when I try to load the results into a ReactiveList in my view model. I believe that this is a scheduling issue but I'm not familiar enough with RxUI to come up with the correct approach.
I've tried subscribing to the command in the view model using ObserveOn with both RxApp.TaskPoolScheduler and RxApp.MainThreadScheduler but neither seems to help.
My view model:
public class UsersViewModel : ReactiveObject, IRoutableViewModel
{
ReactiveList<LisUser> _users;
IUserManagementService UsersService { get; }
public IScreen HostScreen { get; }
public ReactiveCommand<Unit, IEnumerable<LisUser>> LoadUsers { get; }
public String UrlPathSegment => "users";
public ReactiveList<LisUser> Users
{
get => _users;
set => this.RaiseAndSetIfChanged(ref _users, value);
}
public UsersSubPageViewModel(
IScreen screen,
IUserManagementService usersService)
{
HostScreen = screen ?? throw new ArgumentNullException(nameof(screen));
UsersService =
usersService ?? throw new ArgumentNullException(nameof(usersService));
Users = new ReactiveList<LisUser>();
LoadUsers = ReactiveCommand.CreateFromTask(async () =>
{
return await UsersService.GetUsersAsync().ConfigureAwait(false);
});
LoadUsers
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(list =>
{
Users.Clear();
foreach (var u in list)
{
Users.Add(u);
}
});
}
}
My view:
public partial class UsersView : ReactiveUserControl<UsersViewModel>
{
public UsersPageView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.WhenAnyValue(x => x.ViewModel.LoadUsers)
.SelectMany(x => x.Execute())
.Subscribe()
.DisposeWith(disposables);
});
}
}
What I want to occur is when the UsersView is activated, the GetUsers method of the UsersService executes asynchronously and loads the returned list of users into the Users ReactiveList. Instead, I see a new tab in VS with a title of "Source Not Found" and a message saying that "RxApp.cs not found". The actual exception is a System.Exception with a message stating "Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away."
So, my first question is "is this actually a scheduling issue?" And the second question is "how do I resolve it?"
. First as stated in my comment reactivelist is deprecated.
There is a problem with the way the LoadUsers command is invoked.
One is you can do WhenActivated in your view model. You derive your view model from ISupportsActivation, your view will call the WhenActivated block inside your view when it's WhenActivated is called. See https://reactiveui.net/docs/handbook/when-activated/#viewmodels for ideas.
Second way is to use
this.WhenAnyValue(x => x.ViewModel.LoadUsers)
.Select(x => x.Execute())
.Switch()
.Subscribe()
.DisposeWith(disposables);
So the above says get the LoadUsers command, get the Execute() which is a observable, Switch() means only subscribe to the newest first, and dispose the old execute when a new value comes in, subscribe to that we will run the command, and dispose to stop potentially.
It looks like the issue wasn't scheduling, but async related.
IUserManagementService has a reference to an IUserRepository, which itself has an GetUsersAsync method to actually query the database. The actual implementation of IUserManagementService was missing ConfigureAwait on the call to IUserRepository.GetUsersAsync. This wasn't an issue in the unit tests, but definitely a problem once an UI was involved.
I like the Automatic HTTP 400 responses functionality new to ASP.NET Core 2.1 and it's working out really well for most cases.
However, in one action I need to do a bit of pre-processing before validation the payload. I have a custom validator that requires two values in the model to perform validation. One of those values is in the path so I would like to set that value on the model from the path then validate.
I don't want to switch the functionality off for all actions with:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}
Is there any way I could switch it off just for an individual action?
Edit:
I tried modifying the InvalidModelStateResponseFactory but it didn't solve my problem because I still need to get into the controller action:
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
var ignore = actionContext.ActionDescriptor.FilterDescriptors.Any(fd => fd.Filter is SuppressModelStateInvalidFilterAttribute);
if (ignore)
{
// Can only return IActionResult so doesn't enter the controller action.
}
return new BadRequestObjectResult(actionContext.ModelState);
};
});
[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : FormatFilterAttribute
{
}
Edit:
Here's a link to an issue I raised on the asp.net core repo in case I get anywhere with that - https://github.com/aspnet/Mvc/issues/8575
Update: you can just use the following code in ConfigureServices in Startup.cs:
services.Configure<ApiBehaviorOptions>(apiBehaviorOptions => {
apiBehaviorOptions.SuppressModelStateInvalidFilter = true;
});
Based on Simon Vane's answer, I had to modify the attribute for ASP.Net Core 2.2 as follows:
/// <summary>
/// Suppresses the default ApiController behaviour of automatically creating error 400 responses
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention {
private static readonly Type ModelStateInvalidFilterFactory = typeof(ModelStateInvalidFilter).Assembly.GetType("Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilterFactory");
public void Apply(ActionModel action) {
for (var i = 0; i < action.Filters.Count; i++) {
if (action.Filters[i] is ModelStateInvalidFilter || action.Filters[i].GetType() == ModelStateInvalidFilterFactory) {
action.Filters.RemoveAt(i);
break;
}
}
}
}
I had a response from Microsoft - https://github.com/aspnet/Mvc/issues/8575
The following worked a charm.
[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention
{
public void Apply(ActionModel action)
{
for (var i = 0; i < action.Filters.Count; i++)
{
if (action.Filters[i] is ModelStateInvalidFilter)
{
action.Filters.RemoveAt(i);
break;
}
}
}
}
In my controller I could then make changes to the model before re-validating it (note the ModelState.Clear(), TryValidateModel add to existing model state):
if (model == null)
{
return BadRequest(ModelState);
}
model.Property = valueFromPath;
ModelState.Clear();
if (TryValidateModel(model) == false)
{
return BadRequest(ModelState);
}
You could play with ApiBehaviorOptions.InvalidModelStateResponseFactory property to handle specific cases based on actionContext details:
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
// Do what you need here for specific cases with `actionContext`
// I believe you can cehck the action attributes
// if you'd like to make mark / handle specific cases by action attributes.
return new BadRequestObjectResult(context.ModelState);
}
});
This could probably be solved by implementing your own validator for your specific case. It is covered quite well in the documentation.
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.1#custom-validation
Either that or possibly a custom model binder to create your model with all the preprocessing done before it is validated.
I encountered similar problem and came up with this solution.
public class SuppressModelStateInvalidFilterAttribute : ActionFilterAttribute
{
public SuppressModelStateInvalidFilterAttribute()
{
Order = -2500;
}
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
context.ModelState.Clear();
return next.Invoke();
}
}
I have used below code in View and Controller for Grid Binding:
View:
#( Html.Telerik().Grid<MvcApplication1.Models.Movie>()
.Name("Grid")
.EnableCustomBinding(true)
.Columns(columns =>
{
columns.Bound(m => m.Title).Width(100);
columns.Bound(m =>m.Director).Width(200);
columns.Bound(m => m.DateReleased).Format("{0:MM/dd/yyyy}").Width(120);
})
.DataBinding(dataBinding => dataBinding.Ajax().Select("_AjaxBinding", "Home"))
)
Controller:
[GridAction]
public ActionResult _AjaxBinding()
{
return View(new GridModel<Movie> { Data = GetMovies() }); }
private IEnumerable<Movie> GetMovies()
{
return _db.Movie.ToList();
}
But the problem is it is never reaching _AjaxBinding Grid Action.
Please let me know am i missing any web.config changes related to Telerik control.
Thanks in advance
Since you have custom binding enabled, you need to set EnableCustomBinding of the GridActionAttribute attribute of your action method to true.
[GridAction(EnableCustomBinding = true)]
public ActionResult _AjaxBinding(GridCommand command) {
// ...
}
See Custom Ajax Binding.
But judging from the simplicity of your view and controller, it doesn't look like you need custom binding.
See simple Ajax Binding.