Array object received from service but cannot iterate through it? - arrays

I have got a recipe web app, and i have a 'add recipe' page with a child component 'add ingredient' nested and called within its HTML as seen here:
<h2>Add {{addRecipeForm.controls.name.value}}</h2>
<form [formGroup]="addRecipeForm">
...
<app-add-ingredient></app-add-ingredient>
<hr>
<button type="submit">Add new recipe</button> <button (click)="goBack()">Back</button> <button (click)="test()">check if array was caught</button>
</form>
<p>Recipe ingredient list (from service)</p>
<div *ngIf="ingredientList">
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Quantity</th>
<th>Units and Style</th>
</tr>
<tr *ngFor="let ingre of ingredientList">
<td>{{ingre.ID}}</td>
<td>{{ingre.name}}</td>
<td>{{ingre.quantity}}</td>
<td>{{ingre.unitsAndStyle}}</td>
</tr>
</table>
</div>
I then added a button to ensure the data sent from the service was actually received in correct format etc (as seen in the screenshot I got it sends the object perfectly when the child component adds the ingredient) but when I try and present it with the table as shown I get this error:
Cannot find a differ supporting object '[object Object]' of type
'fawf'. NgFor only supports binding to Iterables such as Arrays.
I know the service isnt the problem because my 'test()' function just logs the array and its all there as seen in the screenshot.

https://github.com/angular/angular/issues/6392
This seems to list a similar issue and they resolved it by making a small 'hack' to overcome that error.
hack(val) {
return Array.from(val);
}

A workaround found here Iterate over object in Angular
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({ name: 'values', pure: false })
export class ValuesPipe implements PipeTransform {
transform(value: any, args: any[] = null): any {
return Object.keys(value).map(key => value[key]);
}
}
did the trick

Related

I delete a datarow from my database but not from my webpage until i refresh it. How can i delete that row immediately from my table in Angular5

i'm using WebApi for backend and Angular 5 for frontend, webapi are connected to a DB from which I take the data to be included in my site.
When i press on "delete" button, the data is delited from my database but not from my webpages, and in Angular(as far as I know, I'm a beginner) the page would be refresh instantly.
Console didn't gave me an error so i think probably it's a route configuration error. Maybe i would use the ActivatedRoute, because the page has
RouterModule.forChild([
{path:'mioprofilo', component: MyProfileComponent},
Anyway my deleted method in my-profile.component.service.ts is:
deleteRelative(id: number): Observable<Relative>{
const url = `${this.relativeUrl}/${id}`;
return this.http.delete<Relative>(url).pipe(
tap(rel => console.log( `deleted relative id=${id}`)),
catchError(this.handleError)
);
}
And this is the method i call with the click event in my-profile.component.ts:
delete(id:number): void {
this.myProfileService.deleteRelative(id).subscribe(
data=>{this.route;
});
That is my-profile.component.html with a ngif to test the presence of the data in db and an ngfor* to create a table with the db data:
<table *ngIf = 'relatives && relatives.length'
class="table" id="myTable" style="margin:20px; width: 90%;border-style: outset">
<thead>
<tr>
<th>Cognome</th>
<th>Nome</th>
<th>Telefono</th>
<th>Relazione</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor ='let rel of relatives'>
<td>{{rel.lastName}}</td>
<!-- <td><input type="text" name="name" [(ngModel)]=rel.lastName></td> -->
<td>{{rel.firstName}}</td>
<td>{{rel.phone}}</td>
<td>{{rel.relationType}}</td>
<td>
<a (click)="delete(rel.relativeId)" ><img src="/Images/elimina.png"/></a>
</td>
<td>
<input class="Caregiver" type="checkbox" value="Caregiver">
<span>Caregiver</span>
</td>
</tr>
</tbody>
</table>
I think can be useful to find help me to find a solution if i post below my app.component.module.ts:
#NgModule({
declarations: [
AppComponent,
WelcomeComponent//,
//CalendarComponent,
],
imports: [
BrowserModule,
HttpClientModule,
RouterModule.forRoot([
{path: 'home', component: WelcomeComponent},
{path: '', redirectTo:'home',pathMatch:'full'},
{path:'**', redirectTo:'home', pathMatch:'full'}
]),
MyProfileModule,
CalendarModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
If you need some other code from me do not be afraid to ask me.
WebApi work very good so the problem must be in my angular code.
Thanks all!
Because you are using *ngFor ='let rel of relatives' for your <tr>'s all you need to do is remove that deleted data from your relatives array. This will trigger angular to refresh that component with the new data; removing the deleted item from your table.
To do this, you can use splice:
relatives.splice(arrayIndex, 1);
so...
delete(id:number): void {
this.relatives.splice(arrayIndex, 1);
this.myProfileService.deleteRelative(id).subscribe(
data=>{this.route;}
);
}

Getting undefined when using target '_blank' with ui-router

Hi I'm having the below issue.
Below is my html page
<div class="content" ng-controller="DataController">
<table class="table table-striped" id="show" ng-if="datalist.length>0">
<thead>
<th>State</th>
<th>District</th>
<th>Non-DND's</th>
</thead>
<tbody>
<tr dir-paginate="data in datalist| filter:search|orderBy:sortKey:reverse|itemsPerPage:10" style="cursor:pointer;">
<td>{{data.state}}</td>
<td>{{data.district}}</td>
<td><a ng-click="getnre(data.nondnd,data.dnd,data.land,data.email)" ui-sref="numsemailsdata" target="_blank">{{data.nondnd.length}}</a></td>
</tr>
</tbody>
</table>
<dir-pagination-controls max-size="10" direction-links="true" boundary-links="true" style="float:right" ng-if="datalist.length>0">
</dir-pagination-controls>
</div>
Below is the code in my controller.js
app.controller("DataController", function($scope, DataService) {
$scope.datalist=DataService.getData();
$scope.getnre=function(ndnd,dnd,land,email) {
$scope.numsem = {
ndnds : ndnd,
dnds : dnd,
lands : land,
emails : email
}
}
});
Below is the numsdetails.html
<div class="content" ng-controller="DataController">
<table class="table table-striped">
<thead>
<th>Non-DND's</th>
</thead>
<tbody>
<tr ng-repeat="ndnd in numsems.ndnds">
<td>{{ndnd}}</td>
</tr>
</tbody>
</table>
</div>
Here I'm displaying the non-dnd's count in my first html page and I need to display the non-dnd's in new tab which is numsemails.html
When I'm trying to bind the data to numsemails.html, I'm getting the data as undefined even I'm binding the data from same controller.
Please help me with a solution.
The reason you are getting undefined for numsems is because when you open a new page (blank) you create an entire new instance of your app. It is like loading the page for the first time. Think of it as a totally different browser instance, because that is what it is. You can pass a parameter using stateparams, this gets passed in the url, however you are trying to pass an entire object so it becomes a bit more difficult.
There are multiple solutions to your problem. You can pass some data in the url, or you can use localstorage, $window, or cookies to store the data. I'm sure there are also other solutions. Choose one of these methods to hand your data properly and we can help you with it.
This issue has been discussed in other threads.
ui-router: open state in new tab with target=“_blank” with object params
Your controller code is wrong, you're missing brackets and braces:
app.controller("DataController", function($scope) {
$scope.datalist = DataService.getData();
$scope.getnre = function(ndnd,dnd,land,email) {
$scope.numsem = {
ndnds : ndnd,
dnds : dnd,
lands : land,
emails : email
};
}
});

Passing AngularJS object to JSF-call

I'm using AngularFaces and I want to pass an Angular JS object to an JSF bean method.
I'm iterating over a table and want to put specific buttons for every iteration:
<table ng-table="tableParams">
<tr ng-repeat="user in $data">
<td data-title="'User id'">{{user.id}}</td>
<td data-title="'Name'">{{user.name}}</td>
<td data-title="'Hide User'">
<p:commandButton value="Hide" id="hideUser" action="#{bean.hideUser({{user.id}})}"">
<f:param name="myId" value="{{disponent.dispNr}}" />
</p:commandButton>
</td>
</tr>
</table>
The important line is:
<p:commandButton value="Hide" id="hideUser" action="#{bean.hideUser({{user.id}})}">
I tried several ways, e.g.
<p:commandButton value="Hide" id="hideUser" action="#{bean.hideUser(user.id)}">
But nothings works. How can I achieve this?
Passing an AngularJS object to JSF caused me a lot of headache, too. After a while I've found a solution, but I'm still not happy with it. I hope to find a better solution in one of the next versions of AngularFaces.
As for now, you can use the global function afToJson to serialise the AngularJS object and pass it to JSF as a string:
<button class="carFilterButton" jsf:action="#{selectionBean.showDetails()}"
ng-click="selectionBean.carAsJSon=afToJson(car);">show me!</button>
(see
https://github.com/stephanrauh/AngularFaces/blob/master/AngularFaces_2.0/AngularFaces-2.0-examples/src/main/webapp/carshop/ngtable.xhtml).
On the server side, you can use the JSONUtilites of AngularFaces to deserialize the string back to a regular object:
private String carAsJSon;
private Car car;
public String getCarAsJSon() {
return carAsJSon;
}
public void setCarAsJSon(String carAsJSon) {
this.carAsJSon = carAsJSon;
int pos = carAsJSon.indexOf(",\"$$hashKey\"");
if (pos > 0)
carAsJSon = carAsJSon.substring(0, pos) + "}";
Car car = (Car) JSONUtilities.readObjectFromJSONString(carAsJSon, Car.class);
return getCarBean().showDetails(car);
}
(see https://github.com/stephanrauh/AngularFaces/blob/master/AngularFaces_2.0/AngularFaces-2.0-examples/src/main/java/de/beyondjava/jsf/sample/carshop/SelectionBean.java).

Protractor sendKeys returns NoSuchElementError

I am learning Protractor and am trying to sendKeys in an input field in the angularjs.org page but I am getting this error:
NoSuchElementError: No element found using locator:
By.model("projectList.search")
Here is my code:
describe ("Search list", function() {
it ("should search for jQuery and display 1 result", function() {
browser.get("https://angularjs.org/");
element(by.model("projectList.search")).sendKeys("jQuery");
});
});
I tried using by.id and got the same error. I am assuming that this is caused because element is not visible yet. When I add browser.pause() and go step by step (slowly), the test passes. What is the best way to solve this?
Related HTML from angularjs.org
<input type="text" ng-model="projectList.search" class="search-query" id="projects_search"
placeholder="Search">
<table>
<thead>
<tr>
<th>Project</th>
<th>Description</th>
<th><i class="icon-plus-sign"></i></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="project in projectList.projects | filter:projectList.search | orderBy:'name'">
<td><a ng-href="{{project.site}}" target="_blank">{{project.name}}</a></td>
<td>{{project.description}}</td>
<td>
<a ng-href="#/edit/{{project.$id}}"><i class="icon-pencil"></i></a>
</td>
</tr>
</tbody>
</table>
I managed to find a small library called waitReady.js and it has solved my problem. Here is my final code,
require './waitReady.js')
var searchBtnElm = element(by.model('projectList.search'));
searchBtnElm.waitReady().then(function() {
searchBtnElm.sendKeys("myText");
});
You can try something like that :
var elementSelector = by.model("projectList.search");
browser.driver.isElementPresent(elementSelector).then(function(isPresent){
element(elementSelector).sendKeys("jQuery");
});
See protractor docs for more usefull functions
Is waitReady.js really works for you ?
Have tried to use it to click on stale elements but sometimes anyway exceptions occurs.
Have written function to click on element:
function clickWait(element) {
(element).waitReady().then(function () {
return element.click();
});
};
And invoke it like that:
clickWait(element);
framework: jasmine2

How to get Angular to update the Ui from within the controller

I have the following html.
<div ng-controller="CustCtrl">
<table class="table table-condensed">
<thead>
etc.
</thead>
<tbody>
<tr ng-repeat="customer in customers" data-cust-id="{{customer.Id}}">
<td>
<button ng-model="Id" tracking-{{customer.Tracking}} ng-click="startTrackingCustById(customer.Id)">
</button>
</td>
etc.
</tr>
</tbody>
</table>
So the button has a class that is databound to the customer.Tracking value which is either true or false. When the button is clicked the startTrackingCustById() method is successfully called and the customer object in the customers object is successfully changed like customer.Tracking = true.
But the buttons class is not updated. What am I missing?
Look at using ng-class . For a boolean value in scope you would use:
ng-class="{'classNameToAddIfTrue':customer.Tracking}"
In the CustCtrl I wrapped the call that updated the customers array like this
$scope.$apply($scope.customers[i].Tracking = true);
Based on the suggestion in an answer I will link to when I find it that basically said "If you are having trouble updating the view you most likely need to use $scope.$apply
So that get's it to work. Now I need to figure out why and how.

Resources