How to reduce the amount of data transferred with Blazor server - data-binding

I have a blazor component that fetch data from a service and then render data into a table.
This is my scenario:
The service returns a list of objects, each object contains a large set of properties and an object hierarchy
The user interface must only display a small amount of this data, therefore only some #myObject.MyProperty properties are displayed
#page "/fetchdata"
#inject WeatherForecastService ForecastService
#if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
#code {
private List<WeatherForecast> forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = ForecastService.List;
ForecastService.MyEvent += _Event;
}
private void _Event(object sender, EventArgs e)
{
InvokeAsync(StateHasChanged);
}
}
So in this case WeatherForecast is a large object, but only a small set of its properties are displayed.
Is this scenario already optimized by the Blazor or does the server always "serve" the entire object to the client?

It is effectively 'optimized' to send only the changes.
https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-3.1#comparison-to-server-rendered-ui
From the Blazor docs (from Blazor Server section):
A UI update in Blazor is triggered by:
User interaction, such as selecting a button. App triggers, such as a
timer. The graph is rerendered, and a UI diff (difference) is
calculated. This diff is the smallest set of DOM edits required to
update the UI on the client. The diff is sent to the client in a
binary format and applied by the browser.

Related

Stimulus js Cross Controller events context

I have two, independent, stimulus controllers. One that manages a table and one that is used to trigger a new row for the table.
I have a button on the page that calls the turbo-table-new-row#show function, which does dispatch the event and the turbo-table#show function is called, but I don't seem to have access to the turbo-table's 'this', so I'm unable to access the targets, values, etc...
If I move the button into the turbo-table's scope, I don't need the second controller, and everything works. However, from the UI perspective, this isn't workable.
How do I get access to the receiving controller's 'this' after receiving the event?
<div data-controller="turbo-table-new-row turbo-table"
data-action="turbo-table-new-row:show->turbo-table#display">
<button data-action="click->turbo-table-new-row#show">
</div>
// turbo-table-new-row-controller
show(e) {
this.dispatch("show", { detail: { url: e.params.url} })
}
// turbo-table-controller
show(e) {
console.log("[turbo_table] - turbo-table-new-row->show event")
console.log(e.detail.url)
// I don't have access to the turbo-table-contoller 'this'
this.hasPanelTarget ...
}
It should be possible to dispatch an event from one controller and read it in another controller when not in the same DOM element.
When you dispatch an event from JavaScript, it will bubble up the DOM tree through to the window. You can listen to global events with the #window action descriptor to catch any events that have bubbled up outside of the controller's DOM tree.
See
https://stimulus.hotwired.dev/reference/actions#global-events
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters
You may need to be careful to check that it is the 'right' event you want, but as a basic set up you need to add the #window on your data action.
Example
Here is a working end to end example, not turbo-links, where the table controller is not a parent of your add row button (it is a sibling). Using the window event listener approach we can listen to events outside of the DOM tree.
Once the event is received in your tables controller, the class method should have access to that controller's this without issue.
If you want to access the original trigger button's target element you can do this via event.target.
<main>
<table data-controller="table" data-action="table-action:add#window->table#show">
<thead>
<tr>
<th data-table-target="status"></th>
</tr>
</thead>
<tr data-table-target="row">
<td>Item 1</td>
</tr>
<tr data-table-target="row">
<td>Item 2</td>
</tr>
<tr data-table-target="row">
<td>Item 3</td>
</tr>
<tr data-table-target="row">
<td>Item 4</td>
</tr>
</table>
<div>
<button data-controller="table-action" data-action="table-action#add"
data-table-action-url-param="https://path.to.tables/">
Add row
</button>
</div>
</main>
import { Controller } from '#hotwired/stimulus';
class TableController extends Controller {
static targets = ['row', 'status'];
show({ detail: { url } = {}, target }) {
console.log('event.target - the button that triggered the click', event.target);
if (url) {
const rowCount = this.rowTargets.length;
this.statusTarget.innerText = `Request from: ${url}, there are ${rowCount} rows.`;
}
}
}
export default TableController;
import { Controller } from '#hotwired/stimulus';
class TableActionController extends Controller {
add({ params }) {
this.dispatch('add', {
detail: { ...params },
bubbles: true,
cancelable: false,
});
}
}
export default TableActionController;
From v3.2.0 you can use Outlets API for cross-controller communication and coordination as an alternative to dispatching custom events on controller elements.
See
https://stimulus.hotwired.dev/reference/outlets

inside single view create form as well as display data

I am creating a view page where I need to display data and as well as a form to insert data. To insert data I have bootstrap modal. How can I bind my view page so that I can have data to display in the page as well as create form to insert data. I mean how can I bind my view to display data?
public ActionResult GetFirm()
{
return View(db.FirmModels.ToList());
}
My view page
#model models.FirmModel
// code for bootstrap modal
// code for data table
<table id="tblFirmData">
<thead>
<tr>
<th>Edit/Print</th>
<th style="visibility:hidden;">#Html.DisplayNameFor(model => model.FirmId)</th>
<th>NAME</th>
<th>CONTACT</th>
</tr>
</thead>
<tbody>
#foreach(var item in models)
{
int status = item.FirmRegistrationStatus;
}
</tbody>
</table>
When I do foreach(var item in models) getting error 'models' is a namespace but is used like a variable and when I do #foreach(var item in Model) I am getting error foreach statement cannot operate on variables of type 'FirmModel' because 'FirmModel' does not contain a public instance definition for 'GetEnumerator'.
How to solve this problem, shall I need to modify my GetFirm return method or need to change in view page?
Because of you pass a list to the view define you view model as:
#model IEnumerable<models.FirmModel>
The IEnumerable interface is implementing the GetEnumerator() method used to iterate through the collection.
Or:
#model IList<models.FirmModel>
The IList interface inherits the IEnumerable.
And correspondingly:
#foreach(var item in Model)
{
....
}

Populate drop down from database with Angular in .net

I'm attempting to learn ASP.net and Angular at the same time.
What I want to do is display a list of states (which is retrieved in from a database) in a drop down list using Angular. I can get the states to display in a table.
This is what I have:
My Controller function:
public ActionResult StateListDistinct()
{
var distinctStates = (from w in db.Addresses
select new { State =
w.address_state}).Distinct();
List<string> states = db.Addresses.Select(state => state.address_state).Distinct().ToList();
return View(states);
}
My current View:
#model List<String>
<table class="table">
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(model => item)
</td>
</tr>
}
</table>
What do I need to do to get a drop down populated using Angular?
You can visit the below link for learning dropdown binding using AngularJs
http://techbrij.com/angularjs-cascade-dropdownlist-asp-net-mvc
You can also visit this link
http://www.aspdotnet-suresh.com/2015/02/angularjs-bind-set-dropdownlist-value-text-using-ng-options-ng-repeat.html

Delete Field Spring MVC

I am using SimpleFormController with a result page looking like this:
<tr>
<td>Name: </td>
<td>${product.name}</td>
</tr>
<tr>
<td>Text: </td>
<td>${product.text}</td>
</tr>
A user can enter a name and some text. I'm trying to implement a delete functionality for each entry (there should be a link next to each entry). I'm having trouble with understanding, if it can be done in the same Controller as for the input or not (am new to Spring) and how. The onSubmit method helps to display data that was added, do I need to implement an extra delete method? If yes, how can I "map" it to my delete link in my jsp?
I suppose you are not wanting to put a delete link even when the user is just entering the name!
Delete links should normally appear when you are displaying data, not creating them.
Here is how you can create a delete link according to associated ids.
<tr>
<td>Name: </td>
<td>${product.name}</td>
<td>delete</td>
</tr>
and this should be in your controller:
#Controller
public class ProductController{
#RequestMapping("/delete/{id}")
public String deleteProduct(#PathVariable("id")Integer id) {
// get product by id
// delete that product
// save database
// or do as you wish
return "redirect:/index";
}
}
Hope that helps :)

How to output HTML Table instead of a Select box (generated with ListBoxFor) in ASP.NET?

I am generally new to ASP.NET and am modifying some code that I've inherited. There is a section of code that creates a Select box using a ListBoxFor based on a search term that's entered by the user:
#Html.ListBoxFor(m => m.SelectedItem,
Lookup.Models.myService.Search(Model.SearchTerm,
null == Model.SelectedCity ? 1 :
Int32.Parse(Model.SelectedCity))
The signature for Search() is:
public static List<SelectListItem> Search(string term, string city)
Instead of displaying a Select box, I want to output an HTML table instead with the data. I'm not even sure how to go about doing this or really what info to give you all in order to help me :)
The ListBoxFor helper displays <select> elements with multiple="multiple" attribute allowing multiple selections. If you want something else you should not use this helper. So you said that you wanted an HTML table.
Now, there's something very wrong with the code you have shown. You are calling Lookup.Models.myService.Search inside a view. Views are not supposed to pull data from some places. They are supposed to only display data that is being passed to them under the form of view models by a controller action. So this call should not be done in the view. It should be done beforehand and the result stored as a property on your view model:
public IEnumerable<SelectListItem> Items { get; set; }
Anyway. The first possibility is to write the markup yourself:
<table>
<thead>
<tr>
<th>Value</th>
<th>Text</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Items)
{
<tr>
<td>#item.Value</td>
<td>#item.Text</td>
</tr>
}
</tbody>
</table>
Of course writing this code over and over again could become cumbersome. So you could externalize it in a DisplayTemplate. For example you could put it inside a ~/Views/Shared/DisplayTemplates/MyTableTemplate.cshtml and then when you needed to render it in a view you could use:
#Html.DisplayFor(x => x.Items, "MyTableTemplate")

Resources