I am trying to figure out how to pull data from an array of values, rather than a single value while binding using [(ngModel)] in my Angular 2 app. What I currently have works fine with a single value like this. The single value that's being checked to pull data from the db is whatever's assigned to "value", in this case "staff":
<div class="radio">
<input type="radio" value="staff" name="category" [(ngModel)]="category"> Staff
</div>
This works as expected with a single value. However, one of the values I'm checking against isn't just a single value, but rather an array of values - something like ['advertising', 'accounting', 'adminstration'];
I tried handling this like this in my component:
staff = [
{ value: 1, name: 'advertising' },
{ value: 2, name: 'administration' },
{ value: 3, name: 'accounting' }
];
And then in my view I tried binding to this "staff" array like this:
<div class="radio">
<input type="radio" [value]="staff.value" name="category" [(ngModel)]="category"> Staff
</div>
... but this didn't work. How would I check against an array of values rather than a single value?
I think you want to do a *ngFor and an *ngIf.
Loop through the array and when the array has value=staff then output.
probably change the array name to staffs
*ngFor="let staff of staffs
*ngIf="value=staff.value
Related
hello everyone i have array of objects, and inside each object i have also array of objects..
i did the v-for inside the v-for to display data
at first i wanted for each row to show the data of the first element of the each child of the parent array and onClick event, i wanted to change the data only in the specifique row.
infos: [{
name: 'name1',
infosName: [{
place: 'place.1.1',
surface: '100'
},
{
place: 'place.1.2',
surface: '200'
}
]
},
{
name: 'name2',
infosName: [{
place: 'place.2.1',
surface: '300'
},
{
place: 'place.2.2',
surface: '400'
}
]
}
]
i created a method to display the data and got on parametres Two indexs
this a jsfiddle to understand more the problem
Thank you
https://jsfiddle.net/f0ehwacm/2
There are several issues needing fixing, but you are on the right lines.
Most importantly, you need to store not just one "myIndex" but a separate "myIndex" for each row
That is the root cause of your problem.
Let me rephrase your question?
I believe you are hoping for four buttons. The top two buttons choose between two options.
Completely separately, the bottom two buttons choose between two options.
It would be easier for readers to understand your intention if you called the two top buttons "Question 1, Option A", and "Question 1, Option B". And then the bottom two "Question 2...". Then they would understand why when you click on one of the buttons, you want to affect the output of the table for that row only.
Avoid using generic terms like "index" and "i"
These make it unnecessarily difficult for people to understand what you intend. Better to use a concrete noun, in this case "question" or "answer", and prefix it with "i" when you mean the index, such as "iQuestion" for the index of the question and "question" for the question itself.
You seem to have a single function "getInfos" which does BOTH getting and setting of information
This is a major problem. You should separate the two functions.
When you click, you want to run a "set" function, that updates your index.
When you are simply displaying, you can access a "get" function, which does not change anything.
You need to store an index for each row
In my terminology, you need to store the index of your answer to each question.
So instead of this.myIndex starting at 0, you have it starting at [0,0]. Each of the two values can be updated separately, allowing the program to update the answer to one row (i.e. one question), while leaving the other row unchanged.
I have renamed this variable to this.myAnswer to make it easier to understand.
this.$set when writing to an array that you want Vue to react to
I initially wrote the "setAnswer" function as follows:
this.myAnswer[iQuestion]=iAnswer
However, I found that the on-screen display was not updating. This is a common problem in Vue, when you update not the main property listed in data(), but an array element of that property.
This is because Vue is not tracking the updates of the array elements, only the array itself. So if you were to reassign the entire array, Vue would notice.
The workaround is to tell Vue explicitly that you are updating something that needs to be reactive. Vue will then update it on screen.
To do this, change your assignment from this format:
this.array[index] = value
To this
this.$set(this.array, index, value)
Vue provides this function this.$set, which executes your normal this.array[index] = value and tells Vue to do the screen update.
How to cope with missing "infosName"
In response to your question in the comments. You have a convenient place to solve this: your getAnswer() function.
Change from this:
getAnswer(iQuestion,iAnswer){
return {
'name' : this.infos[iQuestion].infosName[iAnswer].place,
'surface' : this.infos[iQuestion].infosName[iAnswer].surface
}
to this:
getAnswer(iQuestion,iAnswer){
if (this.infos.length>iQuestion &&
this.infos[iQuestion].infosName &&
this.infos[iQuestion].infosName.length>iAnswer
){
return {
'name' : this.infos[iQuestion].infosName[iAnswer].place,
'surface' : this.infos[iQuestion].infosName[iAnswer].surface
}
else return {
name : "",
surface: ""
}
}
Solution
html:
<div id="app">
<div v-for="(question,iQuestion) in infos">
<div class="row d-flex">
<span style="margin-right:10px" v-for="(answer,iAnswer) in question.infosName" class="badge badge-primary" #click="setAnswer(iQuestion,iAnswer)"><i class="fa fa-eye" style="margin-right:10px;cursor: pointer"></i>{{ answer.place }}</span> </div>
<div class="row">
<p>Name : {{ getAnswer(iQuestion,myAnswer[iQuestion]).name }} </p>
<p>Surface : {{ getAnswer(iQuestion,myAnswer[iQuestion]).surface }}</p>
</div>
</div>
</div>
JS:
new Vue({
el :'#app',
data : function(){
return {
myAnswer : [0,0],
infos : [
{
name : 'name1',
infosName : [
{
place : 'Question 1, Option A',
surface : '100'
},
{
place : 'Question 2, Option B',
surface : '200'
}
]
},
{
name : 'name2',
infosName : [
{
place : 'Question 2, Option A',
surface : '300'
},
{
place : 'Question 2, Option B',
surface : '400'
}
]
}
]
}
},
methods:{
setAnswer(iQuestion,iAnswer){
this.$set(this.myAnswer,iQuestion,iAnswer)
},
getAnswer(iQuestion,iAnswer){
return {
'name' : this.infos[iQuestion].infosName[iAnswer].place,
'surface' : this.infos[iQuestion].infosName[iAnswer].surface
}
}
}
})
I am new to web development and AngularJS and I have been struggling with how to go about this. Sorry for the bad English.
I use an ng-repeat that creates the correct number of dropdowns I need as this needs to be dynamic. The dropdowns have a label like:
Test1: <dropdown here>
Test2: <dropdown here> ...etc.
I have a HTTP request that returns an array. If the array has "Test1 State1" in it, I would like the dropdown for Test1: to change to State1 on default. (continues with all the Tests)
How can I go about this?
HTML
<div ng-repeat="o in options track by $index">
<label for="{{::$o}}" class="col-xs-3">{{o}}:</label>
<select id="{{::$o}}" ng-model="stateModel"
ng-options="state.changeToState for state in states"
ng-change="onStateSelect(stateModel.platformReleaseNotes, o)">
{{state}}
</select>
</div>
$scope.states = [
{
changeToState: 'State1',
notes: 'Hello World'
},
{
changeToState: 'State2',
notes: 'Goodbye'
},
{
changeToState: 'State3',
notes: ' is State3'
},
{
changeToState: 'State4',
notes: ' is State4'
}
];
You cannot share model if you want to have different values for all drop downs.
ng-model should be different for all drop downs and this can be achieved by having array of drop downs as below.
$scope.dropDowns = [{
dropDownName: 'Test1:',
id: 'test1',
selectedOption: ''
}, {
dropDownName: 'Test2:',
id: 'test2',
selectedOption: ''
}];
see the running example in
http://plnkr.co/edit/jsAn1jwGkQfxXK5I9G6J?p=preview
I have to say, I am new to the whole Vue framework. I have created a selectable table. The data selected from this table is stored in an object. This function should run in the back. So, I think I should run it the computed section. My object looks like this. I am trying to retrieve only the ids. This data is stored in the variable selected.
[ { "id": 5, "name": "CD", "box": "A5", "spot": 1 }, { "id": 2, ""name": "DVD", "box": "A2", "spot": 1 } ]
I would like to only retrieve the values from the key id. These values should be stored in an array. The array should be pushed when the submit button is clicked. I will later use this array to store this information in this array into a database.
You can find my code below. I guess I am missing something because it doesn't seem to work. How should I refer to this function that it runs automatically and that the array can be called in my controller to put it into the database?
Thanks for the help.
Template
<div class="row">
<div class="col-2">
<b-form-input v-model="date" class="form-control" type="date" value="getDate" ></b-form-input>
<pre class="mt-3 mb-0">{{ date }}</pre>
</div>
<div class="col-6">
<b-form-input v-model="description" placeholder="Enter some text"></b-form-input>
<pre class="mt-3 mb-0">{{ description }}</pre>
</div>
<!-- Submit data to db -->
<div class="col-4">
<button class="btn btn-primary custom-button-width" center-block>Request antibody pool</button>
</div>
</div>
JavaScript
data() {
return {
// data from db
data: this.testerdata,
// selected is the object where my selected data is stored.
selected: [],
// here should my id end up
selectedids: {},
description: '',
date: '',
}
},
computed: {
tester() {
var array = [];
for (var test in this.selected) {
if (test == "id") {
array += this.selected[test];
}
}
console.log(array);
}
},
methods: {
storeData: async function() {
axios.post('/panel', {
description: this.description,
date: this.date,
selectedids: this.tester(selectedids)
}).then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
}).finally(function() {
// always executed
});
},
}
The problem is with this line:
array += this.selected[test];
This the way to add an item to an array:
array.push(this.selected[test]);
Additionally, you are trying to loop over properties of this.selected, but it is an array which contains objects and has no properties. Instead, you want to loop over each object item in the array and push the id property.
And, since this.selected is an array, not an object, you should not use a for...in loop-- which is for objects-- because the order will not be guaranteed. You can read more about that in the MDN for...in docs.
You could use a normal for loop or the array's forEach loop. I'll show the forEach:
this.selected.forEach(obj => {
array.push(obj.id);
});
I understand that you might be looking for vanilla Javascript answer here.
However a lot of code can be saved if you can use RamdaJS library to solve such issues.
It has an excellent set of functions like pluck, which will do the same thing in 1 line.
let array = R.pluck('id')(this.selected);
I have an element in my View in Aurelia that is not getting updated when an object from its Viewmodel is getting updated. I've seen the documentation about Pub/Sub and Event Aggregators, however this seems a little heavy-handed for what I want to do, since I am not trying to communicate between two different resources, but rather just within a View and its Viewmodel.
When a change occurs to the object in the Viewmodel, I don't know how to correctly update (or trigger an update to) the string interpolation in the View.
My code is as follows
myview.html
<h1>My List</h1>
<ul>
<li repeat.for="group of modelObject.groups">
<span>${group.id}</span>
<span repeat.for="state of group.region">${state}</span>
</li>
<ul>
<button click.delegate(editModelObject())>Edit</button>
myviewmodel.js
constructor()
{
this.modelObject = {
<other stuff>,
"groups": [
{
"id": "default",
"regions" : ["NY", "CT", "NJ"]
},
{
"id": "west",
"regions" : ["CA", "OR"]
}
],
<more stuff>
}
}
editModelObject() {
<something that updates both the id and regions of a group in this.modelObject>
}
For some reason, the states are correctly changing in the view, but the id's are not. Do I need to use something like Pub/Sub to get the two-way binding to work correctly? Or is there a simple thing that I am missing or doing wrong?
This works if you change a property of one of the array's objects. But this doesn't work if you assign one of the array's index because this would require dirty-checking. See https://github.com/aurelia/binding/issues/64
To solve your problem you should use splice() instead of indexed assignment. For instance:
const newItem = { id: 77, name: 'Test 77', obj: { name: 'Sub Name 77' } };
//instead of this.model.items[0] = newItem; use below
this.model.items.splice(0, 1, newItem);
Running example https://gist.run/?id=087bc928de6532784eaf834eb918cffa
I have an array of objects like this
UserList = [
{name:'user1',id:1,data:{}},
{name:'user4',id:4,data:{}},
{name:'user7',id:7,data:{}}
]
And html select like this
<select ng-model="data.selectedUser">
<option ng-repeat="item in data.items" value="{{item.id}}">{{item.name}}</option>
</select>
<p>{{data.userPhone}}</p>
Inside my controller I use
$scope.data = {};
$scope.data.selectedUser = 0;
$scope.data.items = UserListModel.items;
$scope.data.userPhone = UserListModel.items[$scope.data.selectedUser].phone;
Is there a way to update selected user phone on selectedUser change without using $watch and stuffing the "$scope.data.userPhone" inside it?
Imagine you have a data like this:
$scope.data = {};
//set the data
$scope.data= [{
id: 1,
name: "cyril",
phone: "1234567"
}, {
id: 2,
name: "josh",
phone: "1237"
}, {
id: 3,
name: "sim",
phone: "4567"
}];
//selected hold the object that is selected in the selectbox.
$scope.selected = $scope.data[0];
Your html will look like this below so now when you select the new user from the list it will be updated in the model selectedItem, the selectedItem has the phone number in it (so you dont need a watch to update phone number seperately as you doing).
<body ng-controller="MainCtrl">
<p>selected item is : {{selectedItem}}</p>
<p> name of selected item is : {{selectedItem.name}} </p>
<select ng-model="selectedItem" ng-options="item.name for item in items track by item.id"></select>
</body>
working example here
One possibility would be to have
$scope.data.userPhone = function () {
return UserListModel.items[$scope.data.selectedUser].phone;
}
This would mean though that you'd have to update any bindings to use data.userPhone() instead.
This might be worse than using a watch though, as the function would get called during every digest.
Without knowing how selectedUser gets updated it's difficult to suggest a best way as, with most things, it depends.