how to watch an array property of array of array? - javascript

$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>

Related

how can I update ng-model dynamically from inside a directive?

I'm new to Angular and can't figure out what I am doing wrong while trying to get ng-model to display an object value.
The goal:
I want to create a directive that reads an Object and generates radio buttons based on Object data. Inside of this Object I have {logAs: 'radioGroupName'}. This should get stored into another object called formData.
Example:
{logAs: 'dog', options: [{option: 'poodle'}, {option: 'pitbull'}]}
This should create two radio buttons, one for pittbull and another for poodle. Both should have ng-model set to dog. If user selects Poodle then final output in the formData Object should be:
{dog: 'poodle'}
The Issue:
I believe I am close, everything works fine except for ng-model. Inside of ng-model the actual Object value (dog) wont show up as I'd like, instead I get the Object path that I typed in.
This is my html:
<div ng-app="test">
<div ng-controller="myController">
<p>formData: {{formData}}</p>
<h3>Static (updates formData)</h3>
<input type="radio" ng-model="formData.dog" value="pitbull"> Pitbull<br/>
<input type="radio" ng-model="formData.dog" value="Bull Dog"> Bull Dog<br/>
<input type="radio" ng-model="formData.dog" value="Poodle"> Poodle
<h3>Dynamic (wont update formData)</h3>
<directive form="formInfo"></directive>
</div>
This is the Javascript:
angular.module('test', [])
.controller('myController', function($scope){
$scope.formData = {};
$scope.formInfo = {
title: 'What type of dog do you have?',
logAs: 'dog',
options: [{option:'Pitbull'}, {option: 'Bull Dog'}, {option:'Poodle'}]
}
})
.directive('directive', function(){
return {
restrict: 'E',
template: '{{form.title}}<div ng-repeat="option in form.options"> <input type="radio" ng-model="form.logAs" value="jack" > {{option.option}}</div>',
scope: {
form: '='
}
}
});
How can I get ng-model to show the output of the object ('dog') instead of the object path?
Here is a fiddle
In your fiddle, it's because on line 3 of your html, you have {{formData}} when it should be {{formData.dog}}

Avoid reflow while editing ordered input ng-repeat on Angular

I'm experiencing some problems while inputing user data into ordered fields displayed with Angular ng-repeat.
Say that you want some values to display on a list, and those values might be editable. At the same time, you are ordering that data. Due to how ng-model works and Angular reflow cycle, if the value of one input surpases another one while still editing, you'll find yourself typing on the wrong field. Look at this example:
var app = angular.module('app', []);
app.directive('myrow', Row);
app.controller('controller', Controller);
function Controller () {
this.order = '-value';
this.inputs = [
{value: 1, tag: "Peas"},
{value: 2, tag: "Apples"},
{value: 3, tag: "Potatos"}
];
}
function Row($compile, $sce){
var linker = function($scope, $element, $attrs){
var template = '<div>- <input type="number" ng-model="data.value"><span ng-bind="data.tag"></span></div>';
a = $element.html(template);
$element.html(template);
$compile($element.contents())($scope);
}
return {
restrict: 'AE',
replace: true,
scope: {
data: "="
},
link: linker
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<div ng-app="app" ng-controller="controller as ctrl">
List:
<div ng-repeat="item in ctrl.inputs | orderBy: ctrl.order">
<div myrow data="item"></div>
</div>
</div>
I've made this simplified example as the original component has thousands of lines and some dependencies. Here this problem is not reproduced exactly, yet, when you write, sometimes the input loses focus, thing that, for example, doesn't happen when not compiling on the directive (which is completly necessary in my real code). Any ideas on how to solve this? Is it possible to activate ng-model update on change instead of on user typing.
You can use ng-model-options and its updateOn property so that your model is updated only when user leaves the field.
You can see how it works here: https://docs.angularjs.org/api/ng/directive/ngModelOptions (There is a sample in the 'Triggering and debouncing model updates' section)
example:
<input ng-model-options="{ updateOn: 'blur'}" />

Remove object from ng-repeat

I have a PhoneGap + Onsen UI + AngularJS app in the works, where I have a list in the view, where the items will be fetched from the controllers variable.
I want to be able to remove items from this list, by clicking on them.
The list looks like this:
<ons-list>
<ons-list-item modifier="tappable" class="item" ng-repeat="citem in completeditems" ng-click="delete(citem)">
<ons-row>
<ons-col>
<div class="titlediv">
<header>
<span class="item-title">{{citem.name}}</span>
</header>
</div>
<div class="item-dates">
<span class="item-start">{{citem.start}}</span>
</div>
</ons-col>
</ons-row>
</ons-list-item>
</ons-list>
The completeditems object in the $scope looks like this:
var completeditemname = "item" + i;
$scope.completeditems[completeditemname] = {
id : "ID",
name : "Name for it",
start: "Start date"
}
Tried the following method, but it didn't work out:
$scope.delete = function(item) {
var index = $scope.completeditems.indexOf(item);
$scope.completeditems.splice(index,1);
//$scope.completeditems.remove(item); //tried this aswell
$scope.$apply() //i need this to update the view
}
You do not need the $scope.$apply() invocation. As you are making alterations to scope variables the digest cycle will be triggered anyhow and you will be encountering an error because of this I believe.
UPDATED:: You're working with an actual object by the looks of it so I've updated the code in the plunker to help you out. It means altering the ng-repeat to use both key and value.
Here is a simple plunkr showing a basic example of what you are trying to do with a one liner in the delete function http://plnkr.co/edit/NtQD....
<body ng-app="myApp">
<div ng-controller="myController as ctrl">
<ul ng-repeat="(key, value) in ctrl.items track by key">
<li ng-click="ctrl.delete(key)">{{value}}</li>
</ul>
</div>
</body>
var myApp = angular.module('myApp', [])
.controller('myController', [
'$scope',
function($scope) {
var self = this;
self.items = {
item1: {
id: 1,
name: 'a'
},
item2: {
id: 2,
name: 'b'
},
item3: {
id: 3,
name: 'c'
}
};
self.delete = function(key) {
delete self.items[key];
};
}
]);
Hope that helps you out!
$scope.$apply() should only be used when changes are coming in from outside the Angular framework. Since your delete() function is being called from an ng-click, it is already being managed by Angular and calling $apply() will raise a "$digest is already in progress" error (check your browser console). Removing that call will most likely get your code working.

Add dynamic model to newly created elements via directives in angularjs

Following is my PLNKR CODE which is working fine.
Problem - I need to add dynamic scope to these element so that I can grab the contact number + type.
I google the problem before asking but as I am new to directives in angular I am confused with the results, let me know what else I need to add to grab the result.
Following kind of result I am expecting -
contact: [
{number: 56432452, type: "Cell"},
{number: 67895644, type: "Work"},
{number: 78943245, type: "Cell"},
{number: 66793456, type: "Home"},
{number: 90546675, type: "Fax"},
];
Also, I need to use the same form in EDIT mode, let me know what are the extra things that I need to keep in mind while developing this functionality for the edit case.
Following is my directive code -
<div class="form-group">
<label class="col-sm-2 control-label">Contact Number<span class="asterisk">*</span></label>
<div class="col-sm-5">
<input type="text" class="form-control">
</div>
<div class="col-sm-2">
<select class="btn">
<option>Cell</option>
<option>Work</option>
<option>Home</option>
<option>Fax</option>
</select>
</div>
<div class="col-sm-1">
<img src="http://img.informer.com/icons/png/16/3225/3225535.png" class="remCls">
</div>
</div>
As you can see currently the select and input do not contain and ngModel. Let me know how do I introduce this to obtain the above mentioned result.
I'm not sure this is what you need but I think you could define your controller as:
myApp.controller("myCtrl", function($scope){
//Create and array of contacts in your model
$scope.contacts = [];
//Add a new contact to the model
$scope.addContact = function() {
var contacts = $scope.contacts;
contacts[contacts.length] = {};
}
//Remove a contact from the model based on its index
$scope.removeContact = function(index) {
$scope.contacts.splice(index, 1);
}
});
Then on your HTML, you leverage the Angular directives ng-repeat and ng-click:
<body ng-controller="myCtrl">
<button ng-click="addContact()"> Add Contact </button>
<div class="form-group" ng-repeat="contact in contacts">
<label>Contact Number</label>
<input type="text" ng-model="contact.contact">
<select ng-model="contact.type">
<option>Cell</option>
<option>Work</option>
<option>Home</option>
<option>Fax</option>
</select>
<button ng-click="removeContact($index)"> Remove Contact </button>
</div> <!-- Close Repeater -->
</body>
Here's your PLNKR link with the changes proposed:
http://plnkr.co/edit/VWCdXSnOsY18XoCKxO0t?p=preview
First of all I would like to thank ExpertSystem for suggesting me to think in Angular way. Then I would like to thank Foxandxss and medice from angular IRC for making the things right not by code but improving my concept and approach for angular.
This is the WORKING code, I came up with for the above problem.
Actually I don't need directive and managed things easily without it.
medice: directives are fine, but when you set up click events that
modify dom, it's gonna break
medice: angularjs can't bind directives properly
Following is my controller code -
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope){
$scope.cnctnum = [];
$scope.cncttype = [];
$scope.types = [
{name: "Cell", value: 1},
{name: "Work", value: 2},
{name: "Home", value: 3},
{name: "Fax", value: 4}
];
$scope.items = [];
var i =0;
$scope.addCnt = function(){
$scope.items.push(i);
i++;
};
$scope.remCl = function(index){
$scope.cnctnum.splice(index, 1);
$scope.cncttype.splice(index, 1);
$scope.items.splice(index, 1);
};
$scope.getval = function(){
console.log($scope.cnctnum);
console.log($scope.cncttype);
};
});

Angular ng-class performance issue when too many elements in DOM

I have been working on a complex angular page which has been causing performance issue. To highlight the problem I have created a fiddle http://jsfiddle.net/4ex2xgL1/3/ here.
Essentially the performance issue is being caused by ng-class statement which has a function in it.
<span class="done-{{todo.done}}" ng-class="myfunction()">{{todo.text}}</span>
The span is in an ng-repeat. On running the fiddle one can see that ng-class gets executed several times when the page loads and on each key up it gets called as many time as number of items in the TODO list.
This is a lot simpler case, in my case I have 780 items on my page and the function ends up being evaluated aroung 3000 times!
One of the solution we saw is to break up the scope but it will cause almost a rewrite of my app.
We also tried https://github.com/Pasvaz/bindonce but it doesn't seem to be working with highly dynamic content.
Any thoughts?
I built a tree with https://github.com/JimLiu/angular-ui-tree with almost 500 items to render, with quite a lot of listeners. It takes 5 seconds to render. Bindonce won't work there.
The only solution out there is make ng-repeat do less. Keep the list small with a pagination, search or anything. Its the best shot as far as I know.
Well here are my recommendations
use ng-change on the checkbox to manipulate dom or anything rather using ng-class, it will improve your performance drastically.
<li ng-repeat="todo in todos track by todo.id">
<input type="checkbox" ng-model="todo.done" ng-change="myfunction()">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
http://jsfiddle.net/4ex2xgL1/3/
use track by in ng-repeat if you have ids, more here http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/
dont show 780 items in a list. Use a searchbox to show some 100 or 50 or you know better
quick-ng-repeat not used yet, try testing it https://github.com/allaud/quick-ng-repeat
finally a few good http://tech.small-improvements.com/2013/09/10/angularjs-performance-with-large-lists/
Finally I found the solution and it will helps lot to improve performance in angular js.
If your model changes dynamically and if you have lots of data and then also it improve AngularJS pages rendering up to 1000% and more - no kidding !.
Fore more information you can visit : http://orangevolt.blogspot.in/2013/08/superspeed-your-angularjs-apps.html
Follow the steps:
download the library from the link:library
2.example without library:(check your console)
function MyController( $scope) {
var entries = [
{ label : 'one', value : 'first entry'},
{ label : 'two', value : 'second entry'},
{ label : 'three', value : 'third entry'}
];
$scope.label ="";
$scope.value ="";
$scope.order = 'label';
$scope.add = function() {
entries.push({
label : $scope.label,
value : $scope.value
});
};
$scope.getEntries = function() {
console && console.log( "getEntries() called");
return entries;
};
}
<script src="https://raw.githubusercontent.com/lodash/lodash/2.4.1/dist/lodash.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<form name="myform" ng-app ng-controller="MyController">
Label/Value :
<input type="text" required ng-model="label">
<input type="text" required ng-model="value">
<button
ng-disabled="!myform.$valid"
ng-click="add()"
>Add</button>
<fieldset>
<legend>
Entries sorted by
<select
ng-model="order"
ng-options="property for property in [ 'label', 'value']">
</select>
</legend>
<div ng-repeat="entry in getEntries() | orderBy:order">
{{entry.label}} = "{{entry.value}}"
</div>
</fieldset>
</form>
3.example with library:(check your console)
function MyController( $scope) {
var entries = [
{ label : 'one', value : 'first entry'},
{ label : 'two', value : 'second entry'},
{ label : 'three', value : 'third entry'}
];
$scope.label ="";
$scope.value ="";
$scope.order = 'label';
$scope.add = function() {
entries.push({
label : $scope.label,
value : $scope.value
});
// clear cache
$scope.getEntries.cache = {};
};
$scope.getEntries = _.memoize(
function() {
console && console.log( "getEntries() sorted by '" + $scope.order + " 'called");
// return entries sorted by value of $scope.order
return _.sortBy( entries, $scope.order);
},
function() {
// return the cache key for the current result to store
return $scope.order;
}
);
}
<script src="https://raw.githubusercontent.com/lodash/lodash/2.4.1/dist/lodash.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.20/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<form name="myform" ng-app ng-controller="MyController">
Label/Value :
<input type="text" required ng-model="label">
<input type="text" required ng-model="value">
<button
ng-disabled="!myform.$valid"
ng-click="add()"
>Add</button>
<fieldset>
<legend>
Entries sorted by
<select
ng-model="order"
ng-options="property for property in [ 'label', 'value']">
</select>
</legend>
<div ng-repeat="entry in getEntries()">
{{entry.label}} = "{{entry.value}}"
</div>
</fieldset>
</form>

Categories

Resources