(Edit: corrected wrong bracket)
(Edit: Changed the HTML code to use *ngFor)
I am new (and struggling) with Angular and the MEAN stack, so please be patient.
I am having problems displaying the data i get from MongoDB in my HTML <select>.
my service:
getLacadorByClubeId(idClube){
let headers = new Headers();
headers.append('Content-Type','application/json');
let ep = this.authService.prepEndpoint('lacador/listByClubeId');
//important: returning the res with a Map. Maybe the problem is here?
return this.http.get(ep,{headers: headers, params: {idClube: idClube}})
.map(res => res.json());
}
my component:
ngOnInit() {
this.clubesdelacoService.getClubesdelaco().subscribe((clubesdelaco) => {
this.Clubedelaco = clubesdelaco;
console.log(clubesdelaco);
};
my HTML:
<div *ngIf="!isView" class="controls col-md-8 ">
<select name="clube" id="repeatSelect" class="input-md form-control" style="margin-bottom: 10px;">
<option *ngFor="let clube of this.Clubedelaco" [ngValue]="clube._id">{{clube.name}}</option>
</select>
<option ng-repeat="clube in this.Clubedelaco" value="{{clube._id}}">{{clube.name}}</option>
</select>
</div>
That results in messages on the browser CONSOLE:
Image of Browser console, please note that the data seems to be inside an object
And nothing in shown in the combobox :(
So, my view SUPPOSEDLY is receiving the data, but I am failing to show it.
My question is, what is the problem?
Wrong use of the <select> ? Need to transform the object into array? If so, how?
To populate the <select> you can use *ngFor. There is no ng-options or ng-repeat in Angular 2+. You can use [ngValue]="someExpression" to set the value attribute of each <option> to a given property on the clube data object, in your case it looks like that would be the _id property.
This assumes that Clubedelaco is an array of objects.
<div *ngIf="someCondition">
<select name="clube">
<option *ngFor="let clube of Clubedelaco.clubesdelacolist" [ngValue]="clube._id">{{clube.name}}</option>
</select>
</div>
Here is an example in action. It's been set up to demonstrate/simulate async setting Clubedelaco in the ngOnInit() lifecycle hook.
To bind the data as part of a form you'll need to either use Template Driven Forms or Reactive Forms.
To avoid using nested objects, you can utilize the RxJS map() operator to transform the result from your http get call. You could for example, just return the inner array. It would look something like this:
import { Observable } from 'rxjs/Observable';
// instead of any, you'd ideally want to make a class/interface to represent the clube object
getLacadorByClubeId(idClube): Observable<any[]> {
// setting headers as before
return this.http.get(ep,{headers: headers, params: {idClube: idClube}})
.map(res => res.json())
// additional map() chained on
// emit the clubesdelacolist array value instead of original object
// you can do more formatting/projection here if needed
.map(res => res['clubesdelacolist']) as Observable<any[]>;
}
Then in your component:
#Component({})
export class SomeComponent {
// create a class or interface to represent the clube objects and their properties
Clubedelaco: any[];
ngOnInit() {
this.clubesdelacoService.getClubesdelaco()
// clubesdelaco comes in as an array now
.subscribe((clubesdelaco) => {
this.Clubedelaco = clubesdelaco;
console.log(clubesdelaco);
};
}
This would allow you to do the following in the HTML:
<option *ngFor="let clube of Clubedelaco" [ngValue]="clube._id">{{clube.name}}</option>
Hopefully that helps!
Related
I'm new to vue.js but I'm trying to display a list of options from an underlying array. However, when the underlying array is changed the list does not update/re-render. I am assigning the new array directly to the vue instance data value (not using splice or assigning via index) and in the console if I try and print out the underlying data (vm.clarifyings) that is updated, it is just the re-rendering that is not working. Even using vm.clarifyings.push(object) does not update the view in the browser.
Vue instance code:
var vm = new Vue({
delimiters: ['$[', '$]'], //change delimiters to allow django integration
el: '#vue_app',
data: {
title: init_info.title, //page title
active: 'presentations',
navClass: 'nav-item nav-link', //necessary for navbar rendering
features: features,
question: '',
response: '',
feature_id: init_info.opening,
last_feature: '',
clarif_id: '',
clarifyings: [],
show_clarifying: false,
},
Relevant method update:
fetch(url)
.then(response => response.json())
.then(function (data) {
// Type out question and response
typeWriter(data.question, 'question');
typeWriter(data.answer, 'response');
// Save selected option and disable previous selected option
option_disable(vm.last_feature);
vm.last_feature = option_ref;
// Show clarifying questions
vm.clarifyings = data.clarifyings;
if (vm.clarifyings.length){
vm.show_clarifying = true;
}
else {
vm.show_clarifying = false;
}
}
All of this executes normally it is simply the re-rendering that isn't working. If I specify the array when I initialize the Vue instance it renders properly it simply does not update.
HTML code:
<select class="selectpicker" data-live-search="true" v-model="clarif_id">
<option v-for="question in clarifyings">$[question.id$] - $[question.name$]</option>
</select>
Vuejs has a bad reactivity for arrays. See the official doc : https://v2.vuejs.org/v2/guide/reactivity.html#For-Arrays
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
You can use Vue.set(vm.clarifyings, indexOfItem, newValue) to overcome this problem. (Instead of vm.clarifyings.push(object))
The problem appeared to be a result of interference with bootstrap-selectpicker, it was fixed by using nextTick functionality in vue like so:
if (feature_asked) {
vm.clarifyings = data.clarifyings;
if (vm.clarifyings.length) {
vm.show_clarifying = true;
vm.$nextTick(function () {
$("#clarifying_qs").selectpicker("refresh");
});
Right, so I am iterating over an array of information and the information is showing the way that I want it to, however, I am getting some amaing looking errors in my console: ERROR TypeError: "_v.context.$implicit is undefined"
api service:
private extractData(res: Response) {
let body = res;
return body || {};
}
getWeather(city: string, isoCode: string): Observable<any> {
return this.http.get(`${this.endPoint}${city},${isoCode}${this.constants.apiKey}`)
.pipe(map(this.extractData));
}
component using api service:
theWeather:any = [];
countryList = COUNTRIES;
isLoading: boolean = true;
showWeather: boolean = false;
constructor(private apiCall:ApiService) { }
ngOnInit() {
this.retrieveWeather()
};
retrieveWeather() {
console.log('COUNTRY LIST', this.countryList);
this.theWeather = [];
this.countryList.map((element, i) => {
this.apiCall.getWeather(element.city, element.countryIso)
.subscribe((data: {}) => {
element.weatherInfo = data;
});
this.isLoading = false;
});
this.showWeather = true;
};
and the html file:
<div class="country-container">
<div *ngIf="isLoading">
<mat-card>
<mat-progress-spinner class="spinner-style" color="primary" mode="indeterminate"></mat-progress-spinner>
</mat-card>
</div>
<div *ngIf="showWeather" class="card-container">
<mat-card *ngFor="let c of countryList" class="card">
<mat-card-title>Weather for: {{c.city}}, {{c.countryName}}</mat-card-title>
<mat-card-content>The current weather is: {{c.weatherInfo.weather[0].description}}</mat-card-content>
</mat-card>
</div>
</div>
finally an image of my console:
Thank you for the help!
edit: made the title more specific to the issue.
You get this error when the angular template is looping through an array of items, and one or more of the array elements is undefined.
This can happen if you create your array by placing a value in a position using the following notation:
myArray[1] = myObject;
If you do this, then myArray[0] will not have a value, and when angular loops through it, you'll get the error "_v.context.$implicit is undefined".
It's safer to use:
myArray.push(myObject);
You might also get this if you remove an element from an array leaving an undefined value at an index.
according to me on the 5th element (it says line 10 index 4 (which is element 5)) in the list it cannot fetch the weather conditions... check for bad request parameters and/or bad return data. remove temp the element or check it and see if the error "moves". maybe the best thing is to check for undefined even when there is no http error.
I am trying to display some data from server in my component.
export class CommerceComponent implements OnInit {
dealList;
ngOnInit() {
this.getDeals();
}
getDeals(){
this.gatewayService.searchDeals(this.searchParams).subscribe(
(data:any)=>{
this.dealList = data.result;
console.log("Deal list",this.dealList[0]);
},
(error)=>{
console.log("Error getting deal list",error);
}
);
}
Service
searchDeals(data){
var fd = new FormData();
fd.append('token',this.cookieService.get('token'));
fd.append('search',data.keyword);
return this.http.post(config.url+'hyperledger/queryByParams',fd);
}
In html
<div class="deal1" *ngFor="let deal of dealList">
{{deal.Deal.title}}
</div>
But the list is not rendering, however, I am getting console.log("Deal list",this.dealList[0]); single object and this.dealList return array of objects
If you get the result in this.dealList[0], you need also to iterate over [0] indexed item.
<div class="deal1" *ngFor="let deal of dealList[0]">
{{deal.title}}
</div>
But your dealList is undefined, so accessing [0] will throw an exception. You need to initialize it as well
dealList = []
The title you wanted to display is located result[i].Deal.title
Here is the image of your JSON response .
<div class="deal1" *ngFor="let deal of dealList">
{{deal.Deal.title}}
</div>
EDIT: Live Code Editor added: https://ide.c9.io/dosstx/wordpress
I am trying to filter a Wordpress JSON data object using VueJS2 and the Wordpress REST API (I have a custom post type in my real world example).
I'm having trouble with the wiring and getting the table to filter based on the search terms that are typed into the search box.
Without the search function, everything works fine, but once I try to filter using a searchterm, nothing happens -- no error in console.
I have my Vue instance like so:
var vm = new Vue({
el: '#app',
data: {
searchTerm: '',
posts: []
},
computed: {
filteredItems: function(){
return this.posts.filter(function(post) {
return this.post.searchTerm; //i believe this line is the culprit
});
}
},
created: function(){
$.get('mylocalhost/wp-json/wp/v2/products/' + '?_embed=true')
.done(function(data) {
vm.posts = data;
});
}
});
My HTML:
<div id="app">
<form>
<input type="text" v-model="searchTerm">
</form>
And further down my HTML....:
<tr v-for="post in filteredItems">
<td>{{post.title.rendered}}</td>
...snip ...
</div>
Any clues on how to fix would be greatly appreciated.
You aren't using the filter method correctly.
From the MDN Docs for the filter method:
filter() calls a provided callback function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true.
The callback passed to filter should return a Boolean value to determine whether or not to include the element of the array in the filtered array.
In your case, I'm assuming your post objects have some property (say content) you want to search and that you want to only include posts with content that contain the search term. So you can do something like this:
computed: {
filteredItems: function() {
return this.posts.filter(function(post) {
return post.content.indexOf(this.searchTerm) != -1;
});
}
},
I am building a site using Angular 2. I have a detail page where I access it by passing the id in the url (http://localhost:3000/#/projectdetail/1). I try to access a service to get the correct project by the id that I pass through. This project is saved in a project variable, but the variable is actually undefined all the time.
This are the errors I get when I go to the detail page:
This are the code pieces that I use (if you need more code just ask):
The projectdetail.page.html:
<div class="container-fluid">
<h1>{{project.name}}</h1>
<div>{{project.description}}</div>
</div>
The projectdetail.page.ts:
public project: Project;
constructor(private projectsService: ProjectsService, private route: ActivatedRoute) {
for (let i = 0; i < 4; i++) {
this.addSlide();
}
}
ngOnInit() {
this.route.params.map(params => params['id']).subscribe((id) => {
this.projectsService.getProject(id).then(project => this.project = project);
});
}
The projects.service.ts:
getProject(id: number): Promise<Project> {
return this.http.get(this.url).toPromise().then(x => x.json().data.filter(project => project.id === id)[0]).catch(this.handleError);
}
The error you're getting is from the template.
At the time of template render, project is not available yet.
Try to wrap it in an *ngIf to avoid this.
<div class="container-fluid" *ngIf="project">
<h1>{{project.name}}</h1>
<div>{{project.description}}</div>
</div>
Template rendering is happening before you are getting data,
what you need is to create an empty object for project property,
option 1:
public project: Project = new Project (); // Provided its a class.
option 2:
If you may not instantiate Project (is an interface), you may use nullable properties
{{project.name}} -> {{project?.name}}
Hope this helps!!