Reset a table data with an observable as its source - rxjs

I have an observable that fills a table like this:
<table class="table table-borderless" style="width:44%;" *ngIf="(borrowers_main_data$ | async) as borrowers_main_data">
<tbody>
<tr>
<td width="8%">CDI:</td>
<td>
<strong>{{ borrowers_main_data.Cdi }}</strong>
And this is the code that fills the table:
public borrowers_main_data$ = this.service.cdiVariable$.pipe(
switchMap(cdiVariable => {return this.dataSource.getCustomer(cdiVariable).pipe(
catchError(error => {
if (error.error instanceof ErrorEvent) {
this.errorMsg = `Error: ${error.error.message}`;
} else {
this.errorMsg = `Error: ${error.message}`;
}
return of({} as Customer);
}))
}));
The problem is that in the case of a second search (after a successful one) that returns the Observable of the empty object the table stays populated with the previous data.

Related

How to handle useState asynchronous in React hooks (in my case)

Here products state is null when I try to display the product. I know to handle this we can use useEffect again for displaying the product but still product state is null. Here is the part of the code that I tried to attempt.
function Product() {
const [products, setProducts] = useState(null);
useEffect(() => {
axios
.get("http://localhost:4000/products")
.then((res) => setProducts(res.data));
}, []);
useEffect(() => {
console.log(products); // Here still products is null
products.map((product) => (
<tr key={product.id}>
<td>{product.productName}</td>
<td>{product.productDesc}</td>
<td>{product.manufacturer}</td>
<td>{product.price}</td>
<td>{product.quantity}</td>
</tr>
));
}, [products]);
return <></>;
}
If I remove products.map then it is showing as two values first one is null and the second is an array of the object (i.e my data).
Right now, nothing will ever be rendered as the return is empty (almost).
Try
function Product() {
const [products, setProducts] = useState(null);
useEffect(() => {
axios
.get("http://localhost:4000/products")
.then((res) => setProducts(res.data));
}, []);
if (!products) {
return null;
}
return products.map(product => (
<tr key={product.id}>
<td>{product.productName}</td>
<td>{product.productDesc}</td>
<td>{product.manufacturer}</td>
<td>{product.price}</td>
<td>{product.quantity}</td>
</tr>
));
}
This is a typical example of lifting the state up to the higher-order component in React.js.
All states and API calls need to be written in your top-level-component.
import ProductItem from "./ProductItem";
function Product(){
const [products, setProducts] = useState(null);
useEffect(() => {
axios
.get("http://localhost:4000/products")
.then((res) => setProducts(res.data));
}, []);
return(
<table>
<thead>
<th>Name</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Price</th>
<th>Qunatity</th>
</thead>
<tbody>
<ProductItem products={products} />
</thead>
</table>
);
}
Here, the products state is passed as a prop to ProductItem. Now the ProductItem component will have the list of the product's item which can be accessed as a prop.
function ProductItem({products}) {
useEffect(() => {
console.log(products); // getting the list of the product in the developer console,
}, [products]);
return (
<>
{products.map((product) => (
<tr key={product.id}>
<td>{product.productName}</td>
<td>{product.productDesc}</td>
<td>{product.manufacturer}</td>
<td>{product.price}</td>
<td>{product.quantity}</td>
</tr>
))}
</>;
);
}
export default ProductItem;

Angular http rxjs/Rx Observable Subscribe data binding not working

I am trying to bind the data from PHP API to the client (Angular 2), however, it didn't work as expected. Any idea?
This is the laravel API code below,
return response()-> json('message',200);
This is the Angular 2 service and subscribe as below,
getString(): Observable<string> {
return this._http.get(this.api_misc_url)
.map((response: Response) => <string>response.json())
.catch(this.handleError);
}
This is the subscriber code as below,
public ngOnInit(): void {
this.chartId = `mydash-chart-${MydashChartComponent.currentId++}`;
this.laravelApiService.getString().subscribe(
messageFromLaravelApi => this.MessageFromLaravelApi = <string>messageFromLaravelApi,
error => this.errorMessage = <string>error);
}
This is the UI binding code below,
<div class='panel-heading' >
<h2>Charting </h2>
<td>{{ MessageFromLaravelApi }}</td>
</div>
But, I can see value shown on the console log.
Either use subscribe or async , not both. async takes an observable or promise and renders data .
Either this
this.MessageFromLaravelApi$ = this.laravelApiService.getString();
http :
<tr *ngFor="let message of MessageFromLaravelApi$ | async">
<td>{{ message }}</td>
</tr>
OR
this.laravelApiService.getString()
.subscribe(
messageFromLaravelApi =>
this.MessageFromLaravelApi = messageFromLaravelApi,
error => this.errorMessage = <any>error
);
http
<tr *ngFor="let message of MessageFromLaravelApi">
<td>{{ message }}</td>
</tr>
Updating the same code :
Service code
getString(): Observable<string> {
return this._http.get(this.api_misc_url)
.map((response: Response) => response.json())
.catch(err=> console.error(err));
}
Component Code :
public ngOnInit(): void {
this.chartId = `mydash-chart-${MydashChartComponent.currentId++}`;
this.laravelApiService.getString().subscribe(
messageFromLaravelApi =>
this.MessageFromLaravelApi = messageFromLaravelApi,
error => this.errorMessage = error);
}
HTML
<div class='panel-heading' >
<h2>Charting </h2>
<td>{{ MessageFromLaravelApi }}</td>
</div>

MVC and Razor to display integer when value is empty or 0

I have the following code in the View.
<span>#Html.DisplayFor(modelItem => item.comments.comments_id)</span>
<span>#item.comments.comments_id</span>
The code below works if comments_id is not 0 or empty.
<span>#((item.comments.comments_id == 0) ? 0 : #item.comments.comments_id)</span>
If its 0 or empty I get
System.NullReferenceException: Object reference not set to an instance of an object.
The class contains this
public Nullable<int> comments_id { get; set; }
How can overcome this issue? thanks in advance.
---- EDit more code ---
public ActionResult Show()
{
var showItems = db.Projects.Include(p => p.Comments).ToList(); // INCLUDES ITEMS FROM BOTH TABLES
return View(showItems);
}
View
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProjectName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comments.comments_id) // NOTICE this coming from a different table(Comments tbl).
</td>
<td>
<span>#((item.Comments.comments_id == 0) ? 0 : #item.Comments.comments_id) // HERE when item.Comments.comments_id has no contents it throws an error.
Create
</td>
</tr>
}
Someone posted something similar but I don't understand the answer.
http://forums.asp.net/t/1732717.aspx?How+to+check+if+model+property+is+empty+in+View+page
You need to use HasValue to check if a nullable variable has a value or not.
<span>#((item.comments.comments_id.HasValue) ? #item.comments.comments_id : 0)</span>
EDIT: Added Example
Your view code looks like it should work. Below is code I wrote to test your view. The lambda expression in your controller action won't be executed until it's used. So it's possible it's not executed until the view is rendered. This could cause a problem because the database connection may be closed by then. However, the call to ToList() should execute the lambda expression. So the problem is probably that item.Comments is null. You should test to see if it is as I did in my example.
Controller
public class HomeController : Controller
{
public class Project
{
public string ProjectName { get; set; }
public ProjectComments Comments { get; set; }
}
public class ProjectComments
{
public string Comments { get; set; }
public Nullable<int> comments_id { get; set; }
}
public ActionResult Index()
{
var showItems = new List<Project>();
showItems.Add(new Project()
{
ProjectName = "Project 1",
Comments = new ProjectComments() { Comments = "some comments", comments_id = 1 }
});
showItems.Add(new Project()
{
ProjectName = "Project 2",
Comments = new ProjectComments() { Comments = "more comments", comments_id = null }
});
return View(showItems);
}
}
View
#model IList<MvcApplication2.Controllers.HomeController.Project>
<table border="1">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProjectName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comments.comments_id)
</td>
<td>
<span>
#((item.Comments != null && item.Comments.comments_id.HasValue) ? #item.Comments.comments_id : 0)
Create
</span>
</td>
</tr>
}
</table>

Razor skipping nested foreach loops

In my database I have a string of child tables. In a razor view I am trying to loop through the ultimate parent and then filter a collection a few levels down.
I have got it to work using the following extract:
#foreach (var artist in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => artist.ArtistName)
</td>
</tr>
foreach (var album in artist.Albums)
{
foreach (var song in album.Songs)
{
if (song.SongPlays != null)
{
foreach (var songPlay in song.SongPlays)
{
<tr>
<td>
#Html.DisplayFor(a => songPlay.PlayTime)
</td>
</tr>
}
}
}
}
}
The issue with this is that I cannot sort the whole child collection by PlayTime. To solve this I then tried to get it working by skipping out the preceding foreach loops using:
foreach (var songPlay in artist.Albums.SelectMany(a => a.Songs.SelectMany(b => b.SongPlays)))
{
<tr>
<td>
#Html.DisplayFor(a => songPlay.PlayTime)
</td>
</tr>
}
This seems to work without the filter applied in the controller action, but returns a null result when the filter is applied. I have tried checking in the view if the result is null, but I could not get this to work - something like:
if (artist.Albums.Select(a => a.Songs.Select(b => b.SongPlays)) != null)
{
foreach (var songPlay in artist.Albums.SelectMany(a => a.Songs.SelectMany(b => b.SongPlays)))
{ ...
I still get a null result, so I am presuming that the null check is not doing what I was hoping it would do.
So if anyone can give me some guidance either on whether this approach is sensible and if I can do a null check on a grandchild collection somehow, or if there would be a better approach, it would be very much appreciated.
I'm not sure I get you right. As far as I understand you need to filter null items and second code sample in your question is ok in terms of orderting. In that case, you may add Where clause to selection:
foreach (var songPlay in artist.Albums.SelectMany(a => a.Songs.Where(b => b != null).SelectMany(b => b.SongPlays)))
{
<tr>
<td>
#Html.DisplayFor(a => songPlay.PlayTime)
</td>
</tr>
}

Asp.Net MVC 3 - Persist a List through different Views

i'm new at this tecnology and i'm having some trouble passing a list of an excel that i imported to my application, here's the code:
The problems is that the model in the Create Controller comes out null so i cant save into the database.
I can't save it before, in the uploadcomplete action because i intend to edit the values before save into the data base.
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
//Save the uploaded file to the disc.
string savedFileName = Server.MapPath("~/UploadedExcelDocuments/" + excelFile.FileName);
excelFileHandler.ImportExcel(savedFileName, excelFile);
return RedirecToAction("UploadComplete",excelFileHandler.DataToEdit);
}
else { return RedirectToAction("Error", "Upload"); }
}
public ActionResult UploadComplete(List<Persona> DataToEdit) // This comes out null so i cant render the view now
{
return View();
}
[HttpPost]
public ActionResult UploadComplete(IEnumerable<ExcelImport.Persona> model)
{
return View();
}
public ActionResult Create(IEnumerable<ExcelImport.Models.Person> model)
{
using (ExcelimportDBTestEntities context = new ExcelimportDBTestEntities())
{
foreach (ExcelImport.Models.Person person in model)
{
Persona newPerson = new Person();
newPersona.Id = person.Id;
newPersona.Birthday= persona.Birthday;
newPersona.LastName= persona.LastName;
newPersona.Name = persona.Name;
context.Persons.AddObject(newPersona);
context.SaveChanges();
}
return View();
}
}
This is my View, there must be something wrong here
#model IEnumerable<ExcelImport.Models.Person>
#{
ViewBag.Title = "UploadComplete";
}
<h2>UploadComplete</h2>
#Html.BeginForm(){
<table>
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Last Name
</th>
<th>
Birthday
</th>
<th>
Options
</th>
</tr>
#foreach (var item in Model) {
#Html.HiddenFor(model => item)
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Birthday)
</td>
<td>
</td>
</tr>
}
</table>
<input type="submit" value="Upload!"/>
}
EDIT: i was tired yesterday so i put some... lets go whit "test" that i was doing by error, now this is what i really want to do. I got an Index View that upload the file and send to the post Index Controller, from there i want to send the list to my UploadComplete Controller, so i can render the UploadComplete View (the list comes out null), and in the post of that Action i want to send the model that i render in the UploadComplete View, to my create controller, so i can storage my data into the database. And as i said before i cant save it into the datebase in the index action becouse i intend to edit this data in the uploadcomplete view.
Thanks in advance, Regards.
As I can see in your code:
There is no [HttpPost] attribute
Form action is GET
Incorrect page rendering (hidden elemens)
Look at this example. It shows how lists could be binded on POST.

Resources