AngularJS edit JSON array by reference - angularjs

I am giving AngularJS a bash and am trying a small test app where I have a list on the left and based on what is selected, update a form on the right to allow editing of parameters. This is the HTML
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="tstApp" ng-controller="tstCtrl">
<div>
<select size="2" style="width: 100px"
ng-model="selected"
ng-options="i as i.name for i in items track by i.name"
/>
</div>
<div>
<input type="text" name="descr" value="{{selected.descr}}"/><br/>
<input type="text" name="ref" value="{{selected.ref}}"/><br/>
</div>
<script src="test.js"></script>
</body>
</html>
and this is the JavaScript
var tstapp = angular.module('tstApp', [])
tstapp.controller('tstCtrl', function($scope) {
$scope.items = [
{
"name" : "ITEM1",
"descr" : "This is item1",
"ref" : "Reference1"
},
{
"name" : "ITEM2",
"descr" : "This is item2",
"ref" : "Reference2"
}
];
$scope.selected = $scope.items[0];
});
The problem with this is that when you actually change the description in the edit field, it remains like that and don't reflect the value of the array item any-more. I think the reason is probably obvious in that 'selected' is a copy of the array item and not the item it self. I can't seem to figure out how to edit the array item directly though that is currently selected.

You have to bind it as ng-model in order to get changes
Like this
<input type="text" name="descr" ng-model="selected.descr"/>
JSFIDDLE

Related

Angularjs Ng-init shows the same retrieved form values when passed through ng-repeat

Am having an issues with angularjs ng-init(). In my table called post, I have 3 rows inserted. For example
insert into post(id,title)values(1,'title1');
insert into post(id,title)values(2,'title2');
insert into post(id,title)values(3,'title3');
Now I have retrieved the records and pass it through ng-repeat and everything works fine.
My problem is the ng-init() function. when I passed the database values into a form inputs with ng-init initialized within the ng-repeat, its shows the last database values all through in the form inputs (Eg. 3, title3)
<input type='text' ng-model='title' ng-model='$parent.title' ng-init="$parent.title=post.title" >
if I add value attribute to the form (Eg. value={{post.id}}) it will show the correct form values on each row but when I click on submit button, the value attribute will not be submitted but it will rather pass the value to ng-init which keeps submitting only the last database records (Eg. 3, title3) irrespective of the form button on each row that was clicked.
I have attached the screenshot that shows how the ng-init displays only the last record in a form values instead of showing all records corresponding to each form rows
but I was expecting to have is the form below or a work around within the angularjs controller or equivalents
below is the code
<!doctype html>
<html>
<head>
</head>
<body ng-app='myapp'>
<div class="content" ng-controller='fetchCtrl'>
<div class="post" ng-repeat='post in posts'>
<form bind-to-scope='$parent' >
post Id<br>
<input type='text' ng-model='pid' ng-model='$parent.pid' ng-init="$parent.pid=post.id" >
<br>
post Title<br>
<input type='text' ng-model='title' ng-model='$parent.title' ng-init="$parent.title=post.title" >
<br>
<input type='button' id='but_save' value='Save' ng-click="submitButton()" >
</form>
</div>
</div>
</div>
<!-- Script -->
<script src="angular.min.js"></script>
<script>
var fetch = angular.module('myapp', []);
fetch.controller('userCtrl', ['$scope', '$http', function ($scope, $http) {
// Add new record
$scope.submitButton = function(){
var pid=$scope.pid;
//alert variables values that is all I want
alert(pid);
// do http thing here if you like
$http({
});
}
// Fetch post data scope goes here
</script>
</body>
</html>
You have few issues in your code: Here are few issues in your question
$parent is not required to bind the data
You have taken ng-model twice in each input which is wrong
Also, ng-model binds the data automatically, ng-init is not required.
Here is the solution:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="post in posts">
<form>
post Id<br>
<input type='text' ng-model='post.pid' >
<br> post Title<br>
<input type='text' ng-model='post.title'>
<br>
<input type='button' id='but_save' value='Save' ng-click="submitButton()">
</form>
</div>
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.posts = [
{
"pid" : "1",
"title" : "title1"
},
{
"pid" : "2",
"title" : "title2"
},
{
"pid" : "3",
"title" : "title3"
},
]
$scope.submitButton = function(){
alert(angular.toJson($scope.posts))
}
});
</script>
</body>
</html>
Please run the above snippet
Here is a working DEMO

AngularJS Select Default Option from Select when user input changes

This question is specific to AngularJS 1.6.5 and above and does not apply to any previous versions.
I have a form with some ng-options selects and some text inputs. When a user selects the None / Custom option, a text input appears to the right. Once the user starts typing in that input, the None / Custom option is deselected and a new unknown option is added.
This is the result of a bug fix introduced in AngularJS 1.6.5. Previously when the user typed in the input, the None / Custom select remained selected.
My question is: how can I recreate the original pre-1.6.5 behavior?
To see an example of what I am talking about, check out these two fiddles. The first fiddle demonstrates the original behavior, the second demonstrates the current (undesired) behavior. Simply select the None / Custom option and type something in the adjacent text input.
Example Fiddles
AngularJS 1.6.4 example (good)
AngularJS 1.6.5 example (bad)
HTML code:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js" type="text/javascript"></script>
</head>
<body ng-app="myapp">
<div class="wrapper" ng-controller="someCtrl">
<div class="row" ng-repeat="t in utmTags">
<div class="col-xs-1"><label style="color:#666;">{{t}}</label></div>
<div class="col-xs-2">
<select class="form-control flat inline-dropdown skinny" ng-options="o.value as o.key for o in utmOptions" ng-model="company[t]">
<option ng-value="company[t]" label="None / Custom"></option>
</select>
</div>
<div class="col-xs-2">
<input type="text" class="form-control flat skinny" style="width:180px;" placeholder="Add tag, without spaces" ng-if="!utmVariables.includes(company[t])" ng-model="company[t]" ng-pattern="utmRegex" />
<div ng-if="utmVariables.includes(company[t])">e.g. {{utmOptionsExamples[company[t]]}}</div>
</div>
</div>
</div>
</body>
</html>
Javascript Code:
var app = angular.module('myapp', ['ui.bootstrap']);
app.controller('someCtrl', ['$scope', function($scope) {
$scope.utmOptions = [
{ key: '{AmbassadorFirstNameLastInitialID}', value: '{AmbassadorFirstNameLastInitialID}' },
{ key: '{AdminFirstNameLastInitial}', value: '{AdminFirstNameLastInitial}' },
{ key: '{ContentDomain}', value: '{ContentDomain}' },
{ key: '{ShareDate}', value: '{ShareDate}' },
{ key: '{ShareID}', value: '{ShareID}' },
{ key: '{SocialPlatform}', value: '{SocialPlatform}' }
];
$scope.utmOptionsExamples = {
'{AmbassadorFirstNameLastInitialID}': 'TracJ-1318',
'{AdminFirstNameLastInitial}': 'ShanA',
'{ContentDomain}': 'entrepreneur.com',
'{ShareDate}': '2017-08-23',
'{ShareID}': '79131',
'{SocialPlatform}': 'Twitter'
};
$scope.utmRegex = /^[a-z0-9\-\._]*[\{\}]{0}$|^{[a-z0-9\-\._]*}$/i;
$scope.utmTags = ['utm_medium', 'utm_source', 'utm_campaign', 'utm_term', 'utm_content'];
$scope.utmVariables = ['{AmbassadorFirstNameLastInitialID}', '{AdminFirstNameLastInitial}', '{ContentDomain}', '{ShareDate}', '{ShareID}', '{SocialPlatform}'];
$scope.company = {
utm_medium: undefined,
utm_source: undefined,
utm_campaign: undefined,
utm_term: undefined,
utm_content: undefined
}
}]);
app.run();

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

angular material md-select using track by but still getting $$hashKey

I'm trying to get rid of the $$hashKey value that angular adds to your model value. According to most sources implementing a track by should solve this issue but I'm doing something wrong.
The vm.productTypes is any array of objects with id properties that are GUIDs.
Resulting model value...
$$hashKey: "object:445"
id: "9e695340-d10a-40ca-9cff-e9a93388912a"
name: "Medical"
type: 1
typeString: "ProductTypes"
HTML Code :
<md-select id="type" ng-model="vm.currentProduct.productType" name="type"
ng-model-options="{trackBy: '$value.id'}"
required>
<md-option ng-repeat="pt in vm.productTypes track by pt.id" ng-value="pt">
{{pt.name}}
</md-option>
</md-select>
Where am I going wrong?
Update:
Seems that the name attribute is causing this strange behavior. Bug?
http://codepen.io/anon/pen/LNpMYJ
Use ng-model-options="{ trackBy: '$value.id' }".
If you are getting list data through $http call, First prepare model object and then load list data.
Or prepare model object and put into an object which is holding hole form data
Link.
<html>
<head>
<title>$$HaskKey Remover</title>
<script src="https://code.angularjs.org/1.3.8/angular.min.js></script>
<script>
var myApp= angular.module('MyApp', []);
myApp.controller('MainCtrl', ['$scope',
function($scope) {
$scope.list = [
{key: "1", name: "Rose"},
{key: {id:2}, name: "Sachin"},
{key: {id:3}, name: "Sandy"}
];
console.log($scope.list);
}
]);
</script>
<head>
<title>Removing $$hashKey when using ng-options</title>
</head>
<body ng-app='MyApp'>
<div ng-controller='MainCtrl'>
<form>
<label for="Select Box">Make a choice of Players:</label>
<select name="selectBx" id="selectBx" ng-model="optionsData"
ng-options="item.name for item in list track by item.key">
</select>
</form>
</div>
</body>
</html>

Is this a bug? Angular removes object params when corresponding input is not valid

Please see this:
http://plnkr.co/edit/soubKCayeLAqgDOel8AL?p=preview
<!DOCTYPE html>
<html ng-app="testApp">
<head>
<script data-require="angular.js#1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="testCtlr">
<pre>{{contacts | json}}</pre>
<div ng-repeat="contact in contacts">
<div ng-repeat="email in contact.emails">
<input type="email" name="email" ng-model="email.email">
<input type="text" name="text" ng-model="email.text">
</div>
</div>
</body>
</html>
var app = angular.module('testApp', []);
app.controller('testCtlr', ["$scope", function ($scope) {
$scope.contacts = [
{
id:'',
name:'',
emails:[
{
email: 'e1',
text: 'fghfgh'
},
{
email: 'e2',
text: 'hjkhjk'
}
],
}
];
}]);
you see if you change the text in the email text box it is removed from the obj unless it is a valid email...
Its not bug, its desired behaviourof angular, when value is not valid , it is not binded to ngModelController ,
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
But there is possibility to attach custom email validator ($parsers)
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$parsers
You can push parser to array of parser so it will be executed as last, and return modelValue so you will "override" angular validator and bind invalidValue to ngModel.
It is not a bug, this is by design. The input type email uses regex to validate email address inputs. If the input is not valid it is removed from the model.
On another note - if you wrap your stuff in a form - you can determine the validation state of the inputs. Also, as you are using a repeat, you will need an index.
Here is an example of what I mean:
<form name="myForm">
<div ng-repeat="contact in contacts">
<div ng-repeat="email in contact.emails track by $index">
<div ng-show="myForm.email{{$index}}.$invalid">Invalid email.</div>
<input type="email" name="email{{$index}}" ng-model="email.email">
<input type="text" name="text" ng-model="email.text">
</div>
</div>
</form>
I have updated your Plunker here.
These are the validation states you can use:
//[formName].[inputFieldName].property
myForm.email1.$pristine;
// Boolean. True if the user has not yet modified the form.
myForm.email1.$dirty
// Boolean. True if the user has already modified the form.
myForm.email1.$valid
// Boolean.True if the the form passes the validation.
myForm.email1.$invalid
// Boolean. True if the the form doesn't pass the validation.
myForm.email1.$error

Resources