Lose the focus after inserting a letter - angularjs

I have written a script which represent a json data in 2 ways: JSBin
<!DOCTYPE html>
<html>
<head>
<script src="https://handsontable.github.io/ngHandsontable/node_modules/angular/angular.js"></script>
</head>
<body ng-app="app">
<div ng-controller="Ctrl">
GUI:
<div ng-repeat="item in data">
<input ng-model="item.val">
</div>
<br><br><br>
Textarea:<br>
<textarea rows=10 cols=20 ng-model="dataString"></textarea>
</div>
<script>
var app = angular.module('app', []);
app.controller('Ctrl', ['$scope', '$filter', function($scope, $filter) {
$scope.data = [{val: "1"}, {val: "2"}];
$scope.$watch('data', function(data_new) {
$scope.dataString = $filter('json')(data_new);
}, true);
$scope.$watch('dataString', function(dataString_new) {
$scope.data = JSON.parse(dataString_new);
}, true);
}]);
</script>
</body>
</html>
Thus, modifying the value in GUI will change the string in the textarea (because of $watch('data'); and modifying the string in the textarea will change the GUI (because of $watch('dataString')).
However, the problem is that when we change the value in GUI, we lose the focus after inserting a letter.
Does anyone know how to amend this?

So the problem is that you are iterating over an array (ng-repeat) and changeing the items of the array. The items are removed from the DOM and new one are inserted because they are strings and thereby compared by value. This makes you loose focus.
It's pretty simple to fix though. Just track by index as the objects are in identical order.
Change:
<div ng-repeat="item in data">
To:
<div ng-repeat="item in data track by $index">

Related

How to push an object array into an scop array?

In angular js How can I push an object array into an array.
Here is my code
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<h1 ng-repeat="x in records">{{x.Class}}
</h1>
<div ng-repeat="s in records.students">{{s.name}}</div>
<input ng-model="formdata.name" type="text" />
<input type="button" value="Save" ng-click="saveName(formdata)">
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.records = [{"Class":"Class 8"}];
$scope.saveName = function(name)
{
$scope.records.students.push({"name":name});
}
});
</script>
</body>
</html>
how can I push into $scope.records.students here $scope.records is an array.
what mistake I am doing I am getting "Cannot read property 'push' of undefined"
Your records property on $scope has no property called students. Therefore, $scope.records.students is undefined. Undefined doesn't have any methods available. You should make sure students exists before pushing.
Also, be aware the ngModel passes by reference. In your original code, the {{s.name}} list will only ever show the text in the input because each element in students points towards the same underlying reference. You should dereference the string that saveName receives. I've done so in the code below, but try playing with it so you can see how it works.
Updated code:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<h1 ng-repeat="x in records">{{x.Class}}
</h1>
<div ng-repeat="s in records.students">{{s.name}}</div>
<input ng-model="formdata.name" type="text" />
<input type="button" value="Save" ng-click="saveName(formdata)">
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.records = [{"Class":"Class 8"}];
$scope.saveName = function(name)
{
// Also, you need to derefence the object
// this is a quick and dirty way, try removing it and see
// what happens
var deRefName = JSON.stringify(name);
// Checks for the existence of students, if it doesn't exist
// sets students to an empty array
if (!($scope.records.students)) {
$scope.records.students = [];
}
$scope.records.students.push({"name":deRefName});
}
});
</script>
</body>
</html>
As the error denotes, there is no way you can access records.students since its an array, not an object.
You need to have records as either an object and inside it student as an array.
then you should be able to access $scope.records.students.
or use $scope.records as array and use index to access the particular object of it.

ng-repeat directive sort disable when using (key, value) for multidimensional array

I have a multidimensional array, where I keep filter name and its list.
like the example below
{"rel":["aa","cc","bb"],"cst":["ff","ee","gg"],"mtm":["hh","jj","ii"]}
"rel", "cst" and "mtm" is used as a filter name and each filter will have its own list to be displayed.
Now my problem is when I use ng-repeat in angular js, it sorts the filter in alphabetical order which I dont wanted.
So I found a workaround to solve this problem.
My Solution:
Add integer to the filter(key) like the below example
{"1rel":["aa","cc","bb"],"2cst":["ff","ee","gg"],"3mtm":["hh","jj","ii"]}
while printing the key just use a function to remove first character and then print.
<script>
angular.module('ngrepeat-sort-remove', []).controller('ngrepeat-sort', function($scope) {
$scope.ngtestrepeat = {"1rel":["aa","cc","bb"],"2cst":["ff","ee","gg"],"3mm":["hh","jj","ii"]};
$scope.removenum =function(key){
return key? key.substr(1) : '';
};
});
</script>
<body ng-app="ngrepeat-sort-remove" ng-controller="ngrepeat-sort">
<div class="ngtest" ng-repeat="(key, value) in ngtestrepeat ">
{{removenum(key)+'-Loop start'}}
<ul>
<li ng-repeat="row in value">{{row}} </li>
</ul>
</div>
</body>
If you want to create this more generic way you can add number with underscore and remove them using a function while display.
My angular version: v1.3.14
If anybody has a better solution to this kindly post here.
Can you check out this plunkr solution with angular v 1.3.14.
In Template:
<div class="ngtest" ng-repeat="key in objectKeys(ngtestrepeat)" ng-init="value = values[key]">
{{key}}<ul><li ng-repeat="row in objectValues(ngtestrepeat[key])">{{row}}</li></ul>
</div>
In Controller:
$scope.ngtestrepeat = {"rel":["aa","cc","bb"],"cst":["ff","ee","gg"],"mm":["hh","jj","ii"]};
$scope.objectKeys = function(obj){
return Object.keys(obj);
}
$scope.objectValues = function(obj){
return Object.values(obj);
}
<script>
angular.module('ngrepeat-sort-remove', []).controller('ngrepeat-sort', function($scope) {
$scope.ngtestrepeat = {"rel":["aa","cc","bb"],"cst":["ff","ee","gg"],"mm":["hh","jj","ii"]};
$scope.keyArray=Object.keys($scope.ngtestrepeat);
});
</script>
<body ng-app="ngrepeat-sort-remove" ng-controller="ngrepeat-sort">
<div class="ngtest" ng-repeat="key in keyArray">
{{key+'-Loop start'}}
<ul>
<li ng-repeat="row in ngtestrepeat[key]">{{row}} </li>
</ul>
</div>
</body>
AngularJS versions up to and including 1.3 sort the keys alphabetically.
So to as an option you can try to update to AngularJS 1.4+. If this is not unacceptable for you, then the recommended workaround is to convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. Using a filter like toArrayFilter can help you with this.
Example:
angular.module('plunker', [])
.controller('MainCtrl', function($scope, $filter) {
var ngtestrepeat = {"rel":["aa","cc","bb"],"cst":["ff","ee","gg"],"mm":["hh","jj","ii"]};
$scope.ngtestrepeat = convertObjToArray(ngtestrepeat);
function convertObjToArray (obj) {
if (!angular.isObject(obj)) return obj;
return Object.keys(obj).map(function (key) {
var value = obj[key];
return angular.isObject(value) ?
Object.defineProperty(value, '$key', { enumerable: false, value: key}) :
{ $key: key, $value: value };
});
}
});
<script src="https://code.angularjs.org/1.3.12/angular.js" ></script>
<html ng-app="plunker">
<body ng-controller="MainCtrl">
<div class="ngtest" ng-repeat="item in ngtestrepeat">
{{item.$key}}<ul><li ng-repeat="row in item">{{row}}</li></ul>
</div>
</body>
</html>
UPDATE: Using functions in bindings like {{removenum(key)+'-Loop start'}} will make the function removenum to call on each $digest cycle and could make it slow in case of long arrays/heavy calculations, so this is not recommended if you care about performance.

ng-click not sending updated value

I have following code:
<div class="input-field col s3" ng-repeat="data in content.data">
<input class="updatable mcq"
ng-disabled="!viewMode"
type="checkbox"
id="mcq{{data.surveyPropertyId}}"
ng-click="updateMCQ(data.surveyPropertyId,data)"
ng-model="data.value"/>
<label for="mcq{{data.surveyPropertyId}}">{{data.name}}{{data.value}}</label>
$scope.updateMCQ = function(surveyPropertyId,newValue){
console.log(newValue);
}
problem is that it in updateMCQ function it shows right value of data.value
but if i trigger a click event on this input element it shows previously selected value. for example if checkbox is checked it would send false.
Also {{data.name}}{{data.value}} is evaluating and changing correctly as check / uncheck this element
What can be the problem
UPDATE:
I have found the problem and its a silly one. when I call $('.updatable.mcq.ng-dirty').trigger('click') or for change, it actually changes and now the element value has been changed.!!!.
Now can anyone guide me on how to create a custom directive which would act like an event same as click or change in angularjs?
You should use ng-change like:
ng-change="updateMCQ(data.surveyPropertyId,data)"
You can use ng-change and it will work for you as said by #Jenny.
Here is the code,
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<div class="input-field col s3" ng-repeat="data in content">
<input type="checkbox" ng-model="data.value" ng-change="updateMCQ('data.surveyPropertyId',data)"> click and check console
{{data}}
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.firstName = "John";
$scope.content =[{ value: false },{ value: false },{ value: true }]
$scope.updateMCQ = function(surveyPropertyId,newValue){
console.log(newValue);
}
});
</script>
</body>
</html>
Please run the above code.
Check this demo
I have found the problem and its a silly one. when I call $('.updatable.mcq.ng-dirty').trigger('click') or for change, it actually changes and now the element value has been changed.!!!.

angularJS what's a good way to put dictionary in input

I have a dictionary of data in the controller and I'm displaying it using ng-repeat. The key is the Title and the value is placed as the value field of an input. I want the user to be able to edit the values and then submit the form. What's the best way I can handle all the input? I've tried ng-model but I can't change the values of the dictionary directly so I'm leaning towards making another dictionary to store the new data. That doesn't seem very efficient though so I'm wondering if there's a better way.
edit: I have this interface and add some values.
export interface Iint {
[title: string] : string;
}
this is in the constructor
this.hashMap : Iint = {};
this.hashMap["Next Title"] = "data";
this.hashMap["Next Value"] = "more data;
In the html I want each of the values (data, more data) to appear in it's own input text box where the user can edit and change the values in the dictionary. I need validation and other things before the user can save and update the data so I'm unsure of if I should be making a duplicate array.
Check this example out. I implemented it in Angular v1 before you edited your answer.
https://plnkr.co/edit/9Y33BDQTngPZx2Vpx5Zz?p=preview
script.js
var app = angular.module('myApp',[]);
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.dict = {
"Title1" : "Hello World !",
"Title2" : "Beautiful day",
"Title3" : "How about that!?"
};
$scope.submitForm = function() {
console.log($scope.dict);
alert(JSON.stringify($scope.dict));
};
}]);
index.html:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<h1>Dictionary Inputs</h1>
<div ng-controller="MyCtrl">
<form name="myForm" ng-submit="submitForm()">
<ul>
<li ng-repeat="(key,val) in dict">
{{key}} : <input type="text" ng-model="$parent.dict[key]">
</li>
</ul>
<button type="submit"> Submit</button>
</form>
<br/>
<div>
$scope.dict : {{dict}}
</div>
</div>
</body>
</html>
Implementing it in Angular v2 might be on similar lines.
It is possible to get ngRepeat to iterate over the properties of an object using the following syntax:
<div ng-repeat="(key, value) in myObj"> ... </div>
Reference: https://docs.angularjs.org/api/ng/directive/ngRepeat#iterating-over-object-properties

How to show the ID of array objects?

I was wondering how to print ids of items inside an array.
I have and array called localData, with a list of objects inside. Every object is a mini array of 3 strings.
In my ng-repeat when i set {{items in array}} it prints the content and not the id. How can i print only ids?
localData = {"-KRFLxEmRoS7M9gKDXVE":{"postBody":"1) remove lag 2) add animations",
"postTitle":"Top Title $$$","userName":"[Admin]"},
"-KRFM6Jm2wQemtl878Ur":"postBody":"Annanana","postTitle":"Ananaj",
"userName":"[Admin]"},"-KRFM7rcEe5K5PXkb29v":{"postBody":"Abshhsua","postTitle":"Ababjsjs","userName":"[Admin]"},
"-KRFM96LtmaXRTnUXJoV":{"postBody":"Gabshsysus","postTitle":"Bshshshshs","userName":"[Admin]"},
"-KRFMAnqecr85xUcOCuw":{"postBody":"Sbsbshshsusudu","postTitle":"Ushhshshs","userName":"[Admin]"},
"-KRFMCkO3JdhA_0MlwwM":{"postBody":"Hshshshs","postTitle":"Sjjsjsjs","userName":"[Admin]"},
"-KRFMLtDJsO0fGYA9JEO":{"postBody":"Fake",
"postTitle":"OMG EPICCCCCCOOOO","userName":"[Admin]"},
"-KRFMQBwIbK6s5lVMlbW":{"postBody":"Asdrobololo","postTitle":"Asdrubale","userName":"[Admin]"},
"-KRI7TVGM0U5emvwD0i7":{"postBody":"Htrsdvgh","postTitle":"Uutfcbuj","userName":"[Admin]"},"-KRITPhL8m-qCCO9y4vY":
{"postBody":"Iiiiiiiwwwwww","postTitle":"Jjjdhd","userName":"[Admin]"}}
angular.module('myapp', [])
.controller('PostsCtrl', function($scope) {
var object={"nm_questionario":{"isEmpty":"MSGE1 - Nome do Questionário"},"ds_questionario":{"isEmpty":"MSGE1 - Descrição do Questionário"},"dt_inicio_vigencia":{"isEmpty":"MSGE1 - Data de Vigência"}};
$scope.items = [];
angular.forEach(object, function (value, key) {
$scope.items.push(key);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="PostsCtrl">
<div ng-repeat="item in items">
{{item}}
</div>
</div>
Store the local data like below to get the id easily. Try this below.
angular.module('myapp', [])
.controller('PostsCtrl', function($scope) {
var accountservice=[{"id":"1","title":"Savings Account","services":[{"types":"ATM card request"},{"types":"loan request"}]},
{"id":"2","title":"Current Account","services":[{"types":"cheque book request"},{"types":"ATM card request"}]},
{"id":"3","title":"Demat Account","services":[{"types":"loan request"},{"types":"ATM card request"}]}];
$scope.accountservices = accountservice;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="PostsCtrl">
<div ng-repeat="(key,value) in accountservices">
<p>{{value.id}}</p>
<ul><li ng-repeat="account in value.services">{{account.types}}</li></ul>
</div>
</div>

Resources