AngularJs Assign Json value to multiple scope - angularjs

Hi I have two form in one page one for reference of previous data and one is a actual form. So i have to assign same json(which actually are come from database) to two different form in a page. I have a problem when I change the option value in main form the reference form value also change. What I want is even the main form change value, reference form should retain old value. please check my code.
https://jsfiddle.net/sanuman/kts7je89/24/
thank you for your any help and suggestions.
var app = angular.module('myApp',[])
.controller('MyCtrl', function($scope, $http) {
$scope.muni=[
{
"id": 24001,
"VDC_Muni_Code": "24001",
"VDC_Muni_Name_Eng": "Anaikot",
"tbl_district_id": 24
},
{
"id": 24002,
"VDC_Muni_Code": "24002",
"VDC_Muni_Name_Eng": "Baldthali",
"tbl_district_id": 24
},
{
"id": 24003,
"VDC_Muni_Code": "24003",
"VDC_Muni_Name_Eng": "Balting",
"tbl_district_id": 24
},
{
"id": 24004,
"VDC_Muni_Code": "24004",
"VDC_Muni_Name_Eng": "Baluwapati",
"tbl_district_id": 24
}
];
$scope.service_data=[
{
"tbl_vdc_municipality_id": 24001
},
{
"tbl_vdc_municipality_id": 24004
},
{
"tbl_vdc_municipality_id": 24002
},
{
"tbl_vdc_municipality_id": 24003
}
];
$scope.municipalities_ref = $scope.muni;
$scope.municipalities = $scope.muni;
$scope.wspVdcMuniTbls = $scope.service_data;
$scope.wspVdcMuniTblsRef = $scope.service_data;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h2>
Main Form
</h2>
<div ng-repeat="wspVdcMuniTblRef in wspVdcMuniTblsRef">
<select
ng-model="wspVdcMuniTblRef.tbl_vdc_municipality_id"
options="municipalities_ref"
ng-options="municipality_ref.id as municipality_ref.VDC_Muni_Name_Eng for municipality_ref in municipalities_ref">
</select>
</div>
<h2>
Reference Form
</h2>
<div ng-repeat="wspVdcMuniTbl in wspVdcMuniTbls">
<select
ng-model="wspVdcMuniTbl.tbl_vdc_municipality_id"
options="municipalities"
ng-options="municipality.id as municipality.VDC_Muni_Name_Eng for municipality in municipalities">
</select>
</div>
</div>
</div>

The example you've provided work as expected. The thing is that both $scope.municipalities and $scope.municipalities_ref points to the same object (same for $scope.wspVdcMuniTbls and $scope.wspVdcMuniTblsRef) when this assigment is made:
$scope.municipalities = $scope.muni;
$scope.municipalities_ref = $scope.muni;
$scope.wspVdcMuniTbls = $scope.service_data;
$scope.wspVdcMuniTblsRef = $scope.service_data;
You should create a copy of $scope.muni and $scope.service_data like this:
$scope.municipalities_ref = angular.copy($scope.muni);
$scope.wspVdcMuniTblsRef = angular.copy($scope.service_data);
The documentation of angular.copy(source, [destination]); can be find there.

Related

Remove empty option in the select using ng-model

I am new to angular js. In my code user changes the value of radio buttons. And depending on the value of the selected radio button, a piece of code is loaded from the ng-switch
HTML:
<body ng-app="">
<div ng-repeat="button in modes">
<label>
<input type="radio" ng-model="data.mode" value="{{button.value}}" ng-click="clearObjectIdModal()" name="e_modes">
{button.label}}
</label>
</div>
<div ng-switch on="data.mode">
<div ng-switch-when="client">
<label for="e_selected_object_item_id">Select Client name: </label>
<select id="e_selected_object_item_id" name="e_selected_object_item_id" ng-model="currentDataItem.object_id" required>
<option ng-repeat="item in customersListArr" value="{{ item.id }}">{{ item.Name }}</option>
</select>
</div>
<div ng-switch-when="agent">
// This part is similar to the previous one
</div>
</div>
</body>
Controller part:
$scope.data = {};
$scope.setFile = function () {
if ($scope.data.mode == 'client')
return 'client';
else if ($scope.data.mode == 'agent')
return 'agent';
$scope.modes = [{
value: 'client',
label: 'Client'
},{
value: 'agent',
label: 'Agent'
}];
$scope.currentDataItem = data; // data is preloaded from inputs in form
There is also a ng-click="clearObjectIdModal()" that clears the model when switching radio buttons:
$scope.clearObjectIdModal = function() {
$scope.currentDataItem = "";
}
The problem is that every time when the radio button is switched to the select value, which dynamically changes, the value of the first option in it becomes equal to undefined. Because in the array from where these options are built there is no such object_id (This is the id that is not there, so an empty field is drawn).
That is, there are all works. But the first option in the select(after switching to another radio button) is rendered as an empty string.
There are thoughts, how it can be fixed?
I'm not sure if I understand you problem correctly but I would suggest a few improvements.
change your setFile function to as follows
$scope.setFile = function (){return $scope.data.mode;}
I also do not see the closing brackets for your function in your code. Besides if your function will only return the data.mode then why need the function?
I would suggest initialize your data object properly like:
$scope.data = {mode:'client'};
Change your clearObjectIdModal function as:
$scope.clearObjectIdModal = function(mode)
{
$scope.currentDataItem = "";
$scope.data.mode=mode;
}
and in your HTML use it as ng-click="clearObjectIdModal(button.mode)"
So in function clearObjectIdModal() I wrote:
$scope.clearObjectIdModal = function() {
if ($scope.e_data["mode"] == 'client') {
if ($scope.customersListArr.length > 0) {
$scope.currentDataItem.object_id = $scope.customersListArr[0]['id'];
}
}
else if ($scope.e_data["mode"] == 'agent') {
if ($scope.agentsListArr.length > 0) {
$scope.currentDataItem.object_id = $scope.agentsListArr[0]['id'];
}
}
}
And after this when I change radio buttons the first option in current select(which every time is changed) will be not empty.
Also the problem with an additional empty option is possible to solve when you add a title as the first item in the list:
<option value="" disabled>Select</option>

angular ng-model updates parent object after submission and stay bounded

I have a form and a display fields above it. I want to update display values only after submission. This works fine for the first submit. Until I submit it, values don't change, and once I click collect it updates values, and then, it seems that ng-model somehow binds and stay bounded to upper object, since when I continue to type on input fields values above update automatically. For me this is a strange behaviour, I want them to update only after I submit them. Any ideas?
and here is the code:
function Ctrl($scope) {
$scope.customFields = ["Age", "Weight", "Ethnicity"];
$scope.person = {
customfields: {
"Age": 0,
"Weight": 0,
"Ethnicity": 0
}
};
$scope.submited = {
"person" : {
"customfields" : {
"Age" : 0,
"Weight" : 0,
"Ethnicity" : 0
}
}
};
$scope.collectData = function () {
$scope.submited.person.customfields = $scope.person.customfields;
console.log($scope.person.customfields);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="Ctrl">
<div ng-repeat="fields in submited.person.customfields">
{{fields}}
</div>
<div class="control-group" ng-repeat="field in customFields">
<label class="control-label">{{field}}</label>
<div class="controls">
<input type="text" ng-model="person.customfields[field]" />
</div>
</div>
<button ng-click="collectData()">Collect</button>
</div>
Change this line
$scope.submited.person.customfields = $scope.person.customfields;
to
$scope.submited.person.customfields = angular.copy($scope.person.customfields);
When you use:
$scope.submited.person.customfields = $scope.person.customfields;
the variables become clones of each other - it's a property in JS. Hence, when you bind using that object, the values stay bound. You're basically just creating another reference for an already existing object.
angular.copy copies only the structure and data of the object onto another. Hence, cloning takes place rather than creating a reference of the object.
Hence, change it to:
$scope.submited.person.customfields = angular.copy($scope.person.customfields);

Toggle true/false through radio buttons in AngularJS (inside of ng-repeat)

Hej, I've got an "almost" working fiddle. I have a list of items and I want to change their value if their radiobutton is selected. Here's the code:
CodePen:
http://codepen.io/anon/pen/MyvQoP
Html:
<div ng-app="myapp" ng-controller="myController">
<div ng-repeat="food in foodList">
<span>{{food.name}}</span>
<input type="radio" ng-model="food.selected" name="radiofood" ng-value="true">
</div>
</div>
JS:
angular.module('myapp', []).controller("myController", myController)
function myController($scope) {
$scope.foodList = [
{
name: 'banana',
selected: 'false'
},
{
name: 'orange',
selected: 'false'
},
{
name: 'apple',
selected: 'false'
}
]
}
The problem:
A radiobutton once clicked, changes it's value to true but clicking another one does not change the previous one to false. So if you click each one of them, one by one, all of them will be true. I only want one to have the true value.
Thanks
--- Edit 2016-03-31 ---
I was looking for a solution without writing a custom fuction but it turns out this can't be done. I've marked #Ankit Pundhir answer as the best one but it wasn't exaclty what i was aiming for.
Add method to controller file:
$scope.selectFood = function(selectedFood){
angular.forEach($scope.foodList,function(food){
if(food != selectedFood){
food.selected = false;
}
})
};
and add ng-change="selectFood(food)" to radio button.
The most simple solution is to add ng-change event on the input and then write a function which takes selectedFood as param and do the following:
Iterates through foodList and changes every value to false
Toggle status of selectedFood (if true set false end vice versa)
Something like this:
$scope.toggleParam = function(selectedFood){
loopThroughAndSetToFalse();
findAndSetReverseValue(selectedFood);
}
function loopThroughAndSetToFalse(){
for(var i=0; i<$scope.foodList.length; i++){
$scope.foodList[i].selected = false;
}
}
function findAndSetReverseValue(selectedFood){
for(var i=0; i<$scope.foodList.length; i++){
if($scope.foodList[i].name === selectedFood.name){
$scope.foodList[i].selected = !(selectedFood.selected);
}
}
}
And your html now will look like this:
<div ng-app="myapp" ng-controller="myController">
<div ng-repeat="food in foodList">
<span>{{food.name}}</span>
<input type="radio" ng-model="food.selected" name="radiofood" ng-change="toggleParam(food)" ng-value="true">
</div>
<br><br>
{{foodList[0]}}<br>
{{foodList[1]}}<br>
{{foodList[2]}}
</div>

Edit object inside array - AngularJS

http://jsfiddle.net/cnnMQ/2/
You can see here I have a pretty nice functioning Add/Remove/Edit functionality for removing objects from an array.
What I am struggling with is
Editing inline and pushing the changes back into the array.
Adding new input fields to the DOM in order to push new objects into the array.
http://jsfiddle.net/cnnMQ/2/
app = angular.module("sparta", []);
window.CompetitionController = function($scope) {
$scope.activities = [{
id: 6431,
name: "Meeting",
points: 20
}, {
id: 6432,
name: "Deal",
points: 100
}];
$scope.addNewActivity = function() {
//This function should create 2 new input fields
//The user should input the name and points
//We can ignore the id for now
//Then the object should be craeted and pushed in as you see below with the mock data.
var updatedActivities = {
id: 6433,
name: "Call",
points: 5
};
$scope.activities.push(updatedActivities);
}
$scope.editActivity = function(activity) {
var selectedActivity = activity;
console.log(selectedActivity);
}
$scope.removeActivity = function(activity) {
activityId = activity.id; //the activity id
var i = 0;
for (var item in $scope.activities) {
if ($scope.activities[item].id == activityId)
break;
i++;
}
$scope.activities.splice(i, 1);
}
}
The HTML is as follows:
<body ng-app="sparta">
<div class="container" ng-controller="CompetitionController">
<div ng-repeat="activity in activities">
{{activity.name}} - {{activity.points}}
<button ng-click="editActivity(activity)">Edit</button>
<button ng-click="removeActivity(activity)">Remove</button>
</div>
<div class="addNew">
<button ng-click="addNewActivity()">Add New</button>
</div>
</div>
</body>
I've tried to give as much as possible in the fiddle - what I would love is some guidance on the addNewActivity() function and the editActivity() function and how to inline edit the two input fields and save the changes back into the array.
Thanks in advance!
You can change your html from:
{{activity.name}} - {{activity.points}}
To:
<input type="text" ng-model="activity.name"/> - <input type="text" ng-model="activity.points"/>
So you get 2-way binding.
Working example: http://jsfiddle.net/CFx7m/
Here's another simple example: http://jsfiddle.net/A5xZ9/2/
Basically you hide input field until activity edit button is clicked, in which case you show input field and hide text:
<div ng-show="activity.isEdited">
<input type="text" ng-model="activity.name"/> - <input type="text" ng-model="activity.points"/>
<button ng-click="activity.isEdited = false">Ok</button>
</div>
<div ng-hide="activity.isEdited">
{{activity.name}} - {{activity.points}}
<button ng-click="activity.isEdited = true">Edit</button>
<button ng-click="removeActivity(activity)">Remove</button>
</div>
There's a lot of improvement possible, for example editing local copy of the activity and updating original attributes only when user presses Ok, and providing Cancel button as well.

AngularJS: list all form errors

Background:
I am currently working on an application with tabs; and I'd like to list the fields / sections that fail validation, to direct the user to look for errors in the right tab.
So I tried to leverage form.$error to do so; yet I don't fully get it working.
If validation errors occur inside a ng-repeat, e.g.:
<div ng-repeat="url in urls" ng-form="form">
<input name="inumber" required ng-model="url" />
<br />
</div>
Empty values result in form.$error containing the following:
{ "required": [
{
"inumber": {}
},
{
"inumber": {}
}
] }
On the other hand, if validation errors occur outside this ng-repeat:
<input ng-model="name" name="iname" required="true" />
The form.$error object contains the following:
{ "required": [ {} ] }
yet, I'd expect the following:
{ "required": [ {'iname': {} } ] }
Any ideas on why the name of the element is missing?
A running plunkr can be found here:
http://plnkr.co/x6wQMp
As #c0bra pointed out in the comments the form.$error object is populated, it just doesn't like being dumped out as JSON.
Looping through form.$errors and it's nested objects will get the desired result however.
<ul>
<li ng-repeat="(key, errors) in form.$error track by $index"> <strong>{{ key }}</strong> errors
<ul>
<li ng-repeat="e in errors">{{ e.$name }} has an error: <strong>{{ key }}</strong>.</li>
</ul>
</li>
</ul>
All the credit goes to c0bra on this.
Another option is to use one of the solutions from this question to assign unique names to the dynamically created inputs.
I made a function that you pass the form to. If there are form errors it will display them in the console. It shows the objects so you can take a look. I put this in my save function.
function formErrors(form){
var errors = [];
for(var key in form.$error){
errors.push(key + "=" + form.$error);
}
if(errors.length > 0){
console.log("Form Has Errors");
console.log(form.$error);
}
};
Brett DeWoody's answer is correct. I wanted to do the logic in my controller though. So I wrote the below, which is based off of the answer user5045936 gave. This may also help some of you who want to go the controller route. By the way Im using the toaster directive to show my users validation messages.
if (!vm.myForm.$valid) {
var errors = [];
for (var key in vm.myForm.$error) {
for (var index = 0; index < vm.myForm.$error[key].length; index++) {
errors.push(vm.myForm.$error[key][index].$name + ' is required.');
}
}
toaster.pop('warning', 'Information Missing', 'The ' + errors[0]);
return;
}
If you have nested forms then you will find this helpful:
function touchErrorFields(form) {
angular.forEach(form.$error, function (field) {
angular.forEach(field, function(errorField) {
if (!errorField.hasOwnProperty('$setTouched')) {
touchErrorFields(errorField);
} else {
errorField.$setTouched();
}
})
});
}

Resources