watch the value in angularjs - javascript

how to watch the array value of ng-model in angularjs i have created fiddle but does not work Please take a look and suggest.
Thanks
<div ng-app ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
<table>
<tr ng-repeat="user in profiles track by $index">
<td>
<input
type="text"
name="parktime_{{user.user_id}}"
ng-model="orderItems[user.user_id].parktime"
>
</td>
</tr>
</table>
</div>
</div>
function ParentCtrl($scope) {
}
function ChildCtrl($scope) {
$scope.parkOptions = {};
$scope.profiles = [
{user_id: '01', park_name: 'England Park'},
{user_id: '02', park_name: 'France Park'}
];
$scope.orderItems = {};
$scope.orderItems.parktime = "Test";
$scope.$watch('orderItems[profiles.user_id].parktime', function(newVal, oldVal){
console.log(newVal);
})
}
http://jsfiddle.net/w5vskLfc/59/

I suggest you use ng-change instead. You can't use watch this way, it is to watch a single property instead.
I've also updated your model to use ng-init. This is something you should be careful with using, but in your case, you need to have an key defined at each position of your orderItems object. If you will always statically assign these, I recommend iterating through them once and creating a key that way instead.
HTML
<input type="text" name="parktime_{{user.user_id}}"
ng-init="orderItems[user.user_id] = {}"
ng-model="orderItems[user.user_id].parktime"
ng-change="onChange(user.user_id)">
JS
$scope.onChange = function(items) {
console.log(items);
}

You are trying to access indexes which does not exist. profiles is an array of objects and I don't think there is anything available on profiles.user_id index. Also, I don't think orderItems[profiles.user_id] will work as that index also doesn't exist.

Related

how to watch an array property of array of array?

$scope.arr = [
[["TTT", 23],3],
[["PPP", 23],3],
[["KKK", 23],3]
];
I need to apply watch on arr[0][0][0] element of array.
Rendering arr[0][0][0], arr[1][0][0], arr[2][0][0] in text box using ng-model through ng-repeat for all arrays.
how to apply watch on ng-model variable as soon as i type something in text box?
I tried to apply watch on entire array arr but it didn't trigger below watch function
$scope.$watch($scope.arr, function (newVal, oldVal)
{
$log.log(newVal);
}
);
html:
<div ng-repeat="el in arr">
Name: <input type="text" ng-model = "el[0][0]" />
</div>
It seems the issue lies in the fact that a new scope is being generated for each iteration of ng-repeat. To get around this, you can attach a separate controller for each iteration, like explained here.
The simplest way I can think of to get around this without multiple controllers is to utilize the ng-change directive, like so:
JS:
$scope.getChanged = function(newValue) {
alert('getChanged(), new value: ' + newValue);
}
HTML:
<input type="text" ng-model="a[0][0]" ng-change="getChanged(a[0][0])" />
Here's a fiddle showing it in action.
If you only want to watch just those specific elements withing the array, you can use the Angular Scope's $watchGroup method to watch multiple expressions. Try out the example below.
angular.module("myApp", []).controller("TestCtrl", function($scope) {
$scope.arr = [
[
["TTT", 23], 3
],
[
["PPP", 23], 3
],
[
["KKK", 23], 3
]
];
$scope.$watchGroup($scope.arr.map((a, i) => "arr[" + i + "][0][0]"), function(newVals, oldVals) {
alert("Updated from " + oldVals + " to:" + newVals);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="TestCtrl">
<div ng-repeat="el in arr">
Name:
<input type="text" ng-model="el[0][0]" />
</div>
</div>
</body>

Filter separate variable in Angular

So I have some code that looks like this:
<input ng-model="search" type="text">
<td ng-repeat="key in targets">
{{ display_names[key] }}
</td>
To be more clear:
targets is a variable containing not-human readable ids such as key012
display_names is an object which has keys like: key012: "USA"
I would like to filter the display_names value from the search? Looking at the angularjs docs, I know I can filter key, but I haven't figured out how to filter display_names
Example
Here's a full example:
var TS = angular.module('myapp', []);
TS.controller('test', function($scope) {
$scope.targets = ["id_1", "id_2"];
$scope.display_names = {
"id_1": "USA",
"id_2": "Mexico"
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myapp" ng-controller="test">
<input ng-model="search" placeholder="Search...">
<ul>
<li ng-repeat="key in targets">{{display_names[key]}}</li>
</ul>
</body>
<td ng-repeat="key in targets">
<span ng-if="display_names[key].indexOf(search) > -1">{{ display_names[key] }}</span>
</td>
use ng-if, or you could also use ng-show. Differences here
This way, as you write in search (which should be in $scope.search) angular will refresh the ng-repeat values to show
If you want to search it case-insensitive, you could use toLowerCase() function before using indexOf
display_names[key].toLowerCase().indexOf(search) > -1
You can't use a filter | in the html because you don't have the value you want to filter against in the array you are iterating over. Instead you can use ng-if to show/hide the elements based on the search. Something like:
<div ng-repeat="key in targets" ng-if="!search || !!display_names[key].match(search)">
{{ display_names[key] }}
</div>
The !! boolean cast is done because otherwise a new Regex object will be returned for the match which triggers a digest cycle which will return another new object and so on.
You also probably want to iterate over <tr> rather than <td>, and you need a <table> element for these elements to be allowed.
Example: http://plnkr.co/edit/qrpLKD9x4IBXowpIgnrf?p=preview
You could also write a custom filter for this, but it is a lot more work:
.filter('displayNames' function () {
return function (key, names, search) {
return !search || !!names[key].match(search);
};
});
And use it like key in targets | displayNames:display_names:search

Angular2 ngModel against ngFor variables

Is it not possible (or not yet possible) to use ngModel against values from ngFor? Is Angular trying to protect me from bad performance?
Works great: http://jsfiddle.net/langdonx/n5pjgev6/
<input type="text" [(ng-model)]="value">{{value}}
Does not work so great: http://jsfiddle.net/langdonx/n5pjgev6/1
<li *ng-for="#name of names">
<input type="text" [(ng-model)]="name">{{name}}
</li>
EXCEPTION: Cannot reassign a variable binding name
I tried binding to the array as well, which... kind of works, but hijacks focus and also throws an exception: http://jsfiddle.net/langdonx/n5pjgev6/2/
<li *ng-for="#name of names; #i = index">
<input type="text" [(ng-model)]="names[i]">{{name}}
</li>
EXCEPTION: LifeCycle.tick is called recursively
Edit:
I can get around the LifeCycle.tick issue using a more direct approach, but the focus is still stolen because ngFor redraws things: http://jsfiddle.net/langdonx/n5pjgev6/3/
<li *ng-for="#name of names; #i = index">
<input type="text" [value]="names[i]" (input)="names[i] = $event.target.value">{{names[i]}}
</li>
I think ngFor don't like tracking array elements which are primitive values having ngModel on them.
If you remove the ngModel inside the loop, it works.
It works too when I update jsfiddle with :
this.names = [{name: 'John'}, {name: 'Joe'}, {name: 'Jeff'}, {name: 'Jorge'}];
and
<li *ng-for="#n of names"><input type="text" [(ng-model)]="n.name">{{n.name}}</li>
A solution is to reference the value inside ngModel by its index. Therefore [(ngModel)]="names[index]".
But this is not sufficient because *ngFor tracks items by value. As soon as the value is changed the old value cannot be tracked. So we need to change the tracking function to return an index, thus trackBy: trackByIndex.
This issue is explained here.
Solution:
#Component({
selector: 'my-app',
template: `
<div>
<input type="text"
*ngFor="let name of names; let nameIndex = index; trackBy: trackByIndex"
[(ngModel)]="names[nameIndex]"/>
<br/>
{{ names | json }}
</div>
`,
})
export class App {
names: string[];
constructor() {
this.names = ['a', 'b', 'c'];
}
public trackByIndex(index: number, item) {
return index;
}
}

adding an array property to object in angular js

I have an object called user:
It has properties like user.name, user.age e.tc.
Now, I need to add an array to user object called subjects via ng-model.
This is what i did inside of ng-repeat:
<input type="text" ng-model="user.subjectname[$index]"
name="subject" ng-required="true" placeholder="Enter a subject name">
and when I do console.log(user.subjectname), it looks like this:
Now, after this, I wanted to do something like this:
<div ng-repeat="data in user.sujects">
<td>{{data}} </td>
</div>
But it does not work.
Try2:
In my my service that is called from controller, I tried this,
if(dataArray== null)
var dataArray=[];
console.log("scop.user.subjectname...................." + user.subjectname);
for(var i in user.subjectname)
{ dataArray.push(user.subjectname[i])
}
user.data=dataArray;
so, now if I do,
<div ng-repeat="data in user.data">
<td>{{data}} </td>
</div>
I still get nothing.
Can I get some direction to deal with this?
Like I told you in your previous question, and showcased in a jsbin.
You need to use the (key, val) in object syntax to get those values out of user.subjectname
ng-repeat="(key, val) in user.subjectname" ng-bind="val"
If you need to transform this object into an array:
$scope.arr = [];
Object.keys(user.subjectname).forEach(function (key) {
arr.push(user.subjectname[key]);
});
Then if you need to output that
ng-repeat="a in arr" ng-bind="a"
edit: after further explanation from you, I'm assuming this jsBin highlights something close to what you are after.

AngularJS: How to make local variables work which are not part of ng-repeat

I have an application where I am receiving a list of person objects in persons and iterating over to build a table dynamically.
The code looks like this:
<table>
<tr ng-repeat = "personInfo in persons">
<td>{{personInfo.name}}</td>
<td>{{personInfo.gender}}</td>
<td>
<select ng-model="selected_address" ng-options="address.placename for address in personInfo.address"></select>
<span ng-show="make_address_editable==false;">{{selected_address.address}}</span>
<input type="text" style="width: 22em;" ng-show="make_address_editable" ng-model="selected_address.address" />
<input style="margin-left: 2em;" type="button" value="{{btnValue}}" ng-show="selected_address" ng-click="make_address_editable=!make_address_editable; changeBtnValue($index)"/>
</td>
</tr>
</table>
The part where I am stuck is how to change the value of input[type="button"] or {{btnValue}} individually for each person which edits/saves a person's address. I don't want to assign it as a property on the personInfo object as I would not need it later. In my controller I initialize the value of make_address_editable and btValue but I'm only able to control the behavior of make_address_editable on ng-click for each person. If I call a function on ng-click which changes btnValue depending on what the user clicked it changes the btnValue for each row/person.
Controller code:
$scope.make_address_editable = false;
$scope.btnValue = 'Edit';
$scope.changeBtnValue = function(index){
if ($scope.btnValue === 'Edit'){
$scope.btnValue = 'Save';
}
else{
$scope.btnValue = 'Edit';
}
}
I don't know how to use index in the method to change btnValue for each person in the table.
Any help would be much appreciated.
Thanks!
There should be no problem including a btnValue within the personInfo object as this is a ViewModel and you are really driving the view (value of button) based on what is in that model. You may not be using it later in terms of using that value when persisting the person to a datastore, but your view is definitely using it constantly as it is keeping the two-way binding in sync.
You should really be putting make_address_editable inside your personInfo model as well since I don't think you want to have a "global" flag for each individual person. Having make_address_editable directly on $scope will prevent you from tracking it on a person by person basis.
If you really must make a local variable, you can try this. See jsfiddle I made starting with your example:
angular.module('coolApp', [])
.controller('PersonCtrl', function ($scope) {
$scope.persons = [{'name': 'Alex', 'gender': 'Male', 'address': [{'placename': 'Home', 'address': '1234 Dr'},{'placename': 'Office', 'address': '12345 Dr'}]}, {'name': 'Alexis', 'gender': 'Female', 'address': [{'placename': 'Home', 'address': '12345 Dr'},{'placename': 'Office', 'address': '123456 Dr'}]}];
//$scope.make_address_editable = false;
//$scope.btnValue = 'Edit';
$scope.changeBtnValue = function (viewModel) {
if (viewModel.editable === true) {
viewModel.btnValue = 'Save';
} else {
viewModel.btnValue = 'Edit';
}
};
});
Then use ng-init within your repeater to make a viewModel object that is local to the repeater scope. This will keep your persons from changing:
<div ng-app="coolApp">
<div ng-controller="PersonCtrl">
<table>
<tr ng-repeat="personInfo in persons" ng-init="viewModel={btnValue: 'Edit', editable: false}">
<td>{{personInfo.name}}</td>
<td>{{personInfo.gender}}</td>
<td>
<select ng-model="selected_address" ng-options="address.placename for address in personInfo.address"></select> <span ng-show="viewModel.editable===false;">{{selected_address.address}}</span>
<input type="text" style="width: 22em;" ng-show="viewModel.editable" ng-model="selected_address.address" />
<input style="margin-left: 2em;" type="button" value="{{viewModel.btnValue}}" ng-show="selected_address" ng-click="viewModel.editable=!viewModel.editable; changeBtnValue(viewModel);" />
</td>
</tr>
</table>
<br><br>
{{persons}}
</div>
</div>
btnValue is supposed to change for all "persons". But I think you'd want it to change for specific "person" objects only.
There is an ugly way to do this. Pass the event to the function.
ng-click="make_address_editable=!make_address_editable; changeBtnValue($event)"
Inside the changeBtnValue function, change the "value" attribute of the corresponding input tag.
$scope.changeBtnValue = function(event){
var inputTag = event.target;
}
"inputTag" variable will hold HTML Element object representing the corresponding input tag. Change the "value" attribute of the tag to "save"/"edit" or anything else.
THis method is ugly because you manipulate the View from within the JS code. People usually try to avoid this.

Categories

Resources