Angular changing dataset without $watch - javascript

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.

Related

Vue, building a select list out of array of options but pre-selecting based on async response

I've been trying to test out a way in vue to build a select list with a hardcoded array of options, however, if a certain async response/event comes in with an assignee attached, I am setting that as 'currentAssignee' which is my preselected option.
This kind of works, but it initially looks empty/invisible. If I click the seemingly non-existent select box, the options will show 'Name One', 'Name Two' and 'John Doe' which is the name from the response. But it doesn't actually satisfy the 'selected' option because it is essentially invisible to the user on page load, until it's clicked
Should I be doing something different?
<select class="firstLastNames linkBox" v-model="currentAssignee" #change="changeAssignee()" >
<option :selected="true">{{currentAssigneeFirst}} {{currentAssigneeLast}}</option>
<option v-for="assignee in assigneeOptions" >{{assignee.email}}</option>
</select>
data () {
return {
currentAssignee: '',
assigneeOptions: [
{id: 0, email: "Name one"},
{id: 1, email: "Name two"}
],
},
}
/**further down, I set currentAssignee based on async event**/
this.currentAssignee = triggerEvent[0].assignee;
I put a code sample together here which I think fixes your issue:
https://codepen.io/timfranklin/pen/bGWYggG
Take a look at what is being bound by the v-model. The "value" of a select is not the object itself, it's some value of an object.
<select class="firstLastNames linkBox" v-model="currentAssignee" #change="changeAssignee($event)" >
<option disabled >Choose One</option>
<option v-for="assignee in assigneeOptions" :key="assignee.id" :value="assignee.id">{{assignee.email}}</option>
</select>
The important note here is the :value="assignee.id";

Dynamically change dropdown select defaults depending on existing values in array

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

Selecting default option value with knockout

I'm trying to select a default select option based on one of the property with which I'm populating my select option.
This code is copied straight from #rneimeyer's fiddle. I did tweak it to do what I wanted to do.
So, I have choices as my observableArray.
var choices = [
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
];
function ViewModel(choices, choice) {
this.choices = ko.observableArray(choices);
};
The difference between rneimeyer's fiddle and mine is that I have choice property added on my object inside the observableArray instead of having a separate observable for the option that we want to be default.
Here's the fiddle on my attempt.
Now I'm checking in my select element tag whether the choice attribute is true or not. And if it is then I want to set the name to the value attribute so that it becomes the default.
<select data-bind="options: choices, optionsText: 'name', value: choice"></select>
I've tested this with simple data model in my fiddle here as well which is working just as I wanted.
I guess what my real query is how to check choice property in the data-bind. I see that optionText is being able to access the name property just fine. Not sure why it isn't same for choice property in value attribute.
I might have misdirected to some people. Also, I apologize for not mentioning the version that I'm using. I'm currently using Knockout 3.0.0 (you'll see why this is important later)
Also, just to note that I'm not saying #XGreen's method is wrong but that wasn't exactly what I was looking for and this might be due to my poor explanation.
Let me first try to clarify what I was trying to accomplish.
First of all, I will be having an array of object with the information for the options.
[
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
]
Now, what I wanted to do was to data-bind select option to that array with choice true being the default selected one.
I'm not intending to create any extra observable except the array itself which is going to be an observableArray.
After much research I finally found optionsAfterRender attribute for options property in Knockout's Docs.
<select data-bind="options: choices,
optionsValue: 'name',
optionsAfterRender: $root.selectDefault">
</select>
So what optionsAfterRender really does is, on each array element it calls custom function which I've set to check if the choice is true or not and make the value of select option that which has the true.
Do note that ko.applyBindingsToNode does not work on version 2.2.0 which I had in my original fiddle.
function ViewModel(choices) {
this.choices = ko.observableArray(choices);
this.selectDefault = function(option,item){
if(item.choice){
ko.applyBindingsToNode(option.parentElement, {value: item.name}, item);
}
};
};
ko.applyBindings(new ViewModel(choices));
And here's the fiddle for it.
Ok If I understand you want to set the true choice as your default selected value.
First you need to involve id in your drop down so it becomes the value of the options as we will filter our collection based on that unique id
<select data-bind="options: choices, optionsText: 'name', optionsValue: 'id', value: selectedChoice"></select>
As you see now you need to create a new observable called selectedChoice and we are going to populate that observable with the choice that is true using a computed.
var choices = [
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
];
function ViewModel(choices) {
var self = this;
self.choices = ko.observableArray(choices);
self.trueChoice = ko.computed(function(){
return ko.utils.arrayFirst(self.choices(), function(item){
return item.choice === true;
});
});
self.selectedChoice = ko.observable(self.trueChoice().id);
};
ko.applyBindings(new ViewModel(choices));
the new computed property trueChoice uses the arrayFirst method in order to return the first item in your choices collection that has its choice property set to true.
Now that we have our true choice all we have to do is to set the selected value of the dropdown aka selectedChoice to be the id of that true choice so the item becomes selected in the drop down.
Here is also a working fiddle for this
Added a Gist that disabled the first option in a select drop down list, and work nicely with KO's optionsCaption binding, using a optionsDisableDefault binding:
https://gist.github.com/garrypas/d2e72a54162787aca345e9ce35713f1f
HTML:
<select data-bind="value: MyValueField,
options:OptionsList,
optionsText: 'name',
optionsValue: 'value',
optionsCaption: 'Select an option',
optionsDisableDefault: true">
</select>
You could create a computed that holds the selected items
self.selected_options = ko.computed({
read: function() {
return self.choices.filter(function(item){ return item.choice });
},
write: function(value) {
self.choices.forEach(function(item) { item.choice = value.indexOf(item) > 0;});
}
})
Then bind to that as the selected options.

Angularjs - showing element based on a presence of id in array

Does anyoen know how to check (in the View) for a presence of id in a given array?
Lets say I have an array like so:
var arr = [123, 456, 789];
and a data source like this:
var data = [
{id: 123, name: 'foo'},
{id: 456, name: 'bar'},
{id: 789, name: 'baz'}
];
Now in the view while iterating over the data array I would like to show/hide elements based on a presence of id in the arr like so:
<div ng-repeat="item in data">
<span ng-show="item.id in arr"></span>
</div>
The above code item.i in arr of course does not work for obvious reasons. Anyone know how to achieve that functionality? Not to mention the ng-show block should always kick-in whenever the arr array is altered.
Thanks in advance.
You can just create a method in your scope
function MyCtrl( $scope ) {
$scope.data = ...
$scope.enabled = ...
$scope.visible = function (id ) {
return $scope.enabled.indexOf(id) > -1
}
}
and
<span ng-show="visible(item.id)" ></span>

How to use parameters within the filter in AngularJS?

I want to use parameter in filter, when I iterate some arrays with ng-repeat
Example:
HTML-Part:
<tr ng-repeat="user in users | filter:isActive">
JavaScript-part:
$scope.isActive = function(user) {
return user.active === "1";
};
But I want to be able to use filter like
<tr ng-repeat="user in users | filter:isStatus('4')">
But its not working. How can I do something like that?
UPDATE: I guess I didn't really look at the documentation well enough but you can definitely use the filter filter with this syntax (see this fiddle) to filter by a property on the objects:
<tr ng-repeat="user in users | filter:{status:4}">
Here's my original answer in case it helps someone:
Using the filter filter you won't be able to pass in a parameter but there are at least two things you can do.
1) Set the data you want to filter by in a scope variable and reference that in your filter function like this fiddle.
JavaScript:
$scope.status = 1;
$scope.users = [{name: 'first user', status: 1},
{name: 'second user', status: 2},
{name: 'third user', status: 3}];
$scope.isStatus = function(user){
return (user.status == $scope.status);
};
Html:
<li ng-repeat="user in users | filter:isStatus">
OR
2) Create a new filter that takes in a parameter like this fiddle.
JavaScript:
var myApp = angular.module('myApp', []);
myApp.filter('isStatus', function() {
return function(input, status) {
var out = [];
for (var i = 0; i < input.length; i++){
if(input[i].status == status)
out.push(input[i]);
}
return out;
};
});
Html:
<li ng-repeat="user in users | isStatus:3">
Note this filter assumes there is a status property in the objects in the array which might make it less reusable but this is just an example. You can read this for more info on creating filters.
This question is almost identical to Passing arguments to angularjs filters, to which I already gave an answer. But I'm gonna post one more answer here just so that people see it.
Actually there is another (maybe better solution) where you can use the angular's native 'filter' filter and still pass arguments to your custom filter.
Consider the following code:
<li ng-repeat="user in users | filter:byStatusId(3)">
<span>{{user.name}}</span>
<li>
To make this work you just define your filter as the following:
$scope.byStatusId = function(statusId) {
return function(user) {
return user.status.id == statusId;
}
}
This approach is more versatile because you can do comparisons on values that are nested deep inside the object.
Checkout Reverse polarity of an angular.js filter to see how you can use this for other useful operations with filter.
If you have created an AngularJs custom filter, you can send multiple params to your filter.Here is usage in template
{{ variable | myFilter:arg1:arg2... }}
and if you use filter inside your controller here is how you can do that
angular.module('MyModule').controller('MyCtrl',function($scope, $filter){
$filter('MyFilter')(arg1, arg2, ...);
})
if you need more with examples and online demo, you can use this
AngularJs filters examples and demo
This may be slightly irrelevant, but if you're trying to apply multiple filters with custom functions, you should look into:
https://github.com/tak215/angular-filter-manager
Example I have a students list as below :
$scope.students = [
{ name: 'Hai', age: 25, gender: 'boy' },
{ name: 'Hai', age: 30, gender: 'girl' },
{ name: 'Ho', age: 25, gender: 'boy' },
{ name: 'Hoan', age: 40, gender: 'girl' },
{ name: 'Hieu', age: 25, gender: 'boy' }
];
I want to filter students via gender to be boy and filter by name of them.
The first I create a function named "filterbyboy" as following:
$scope.filterbyboy = function (genderstr) {
if ((typeof $scope.search === 'undefined')||($scope.search === ''))
return (genderstr = "")
else
return (genderstr = "boy");
};
Explaination: if not filter name then display all students else filter by input name and gender as 'boy'
Here is full HTMLcode and demo How to use and operator in AngularJs example

Categories