ng-model saving with a recursive display - angularjs

Setup
I have a frontend that displays a JSON. One can edit the values in the JSON via an ng-model textarea with a save button.
It does this by recursively calling an object like this:
In tree-object-edit.html:
...
<h4 class="capitalize">{{ item_key }}: </h4>
<textarea rows="2" cols="60" ng-model="item"></textarea>
<div ng-if="item.properties">
<ul>
<li ng-repeat="(item_key, item) in item.properties" ng-include="'html/tree-object.html'"></li>
</ul>
</div>
This is called by this my-definition.html
<form name="form"
role="form"
novalidate
class="ng-scope ng-invalid ng-invalid-required ng-dirty ng-valid-minlength"
ng-controller="MyDefinitionDetailController">
<h4>My Definition Edit: {{myDefinition.name}}</h4>
<ul ng-repeat="(item_key,item) in myDefinition" ng-include="'html/tree-object-edit.html'"></ul>
<button type="submit" ng-click="create()">
<span>Save</span>
</button>
</form>
My my-defintion-detail.controller.js contains
$scope.create = function () {
MyDefinition.save($scope.myDefinition,
function () {
});
};
Problem
Clicking save does not save the myDefintion because myDefiniton has not been changed. Only item has been changed.
Question
Is there a way to make the myDefiniton change when the item "within it" changes?
UPDATE 1
I have found that:
<textarea rows="2" cols="60" ng-model="myDefinition[item_key]"></textarea>
Does work for the first level of the recursion

You correctly noted, that ng-model="myDefinition[item_key]"(parent[key] at my example) works, - changes will reflect into desired model, if you will make them on property level not on direct value. So, besides passing value and key to nested template, you also should pass parent object, that way chain of references from actual value to "head" will be created:
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.value = {
identity: {
name: 'Max',
surName:'Smith',
},
birthDay: {
year: 1990,
month: 2,
day: {
weekDay: 'friday',
number: '13'
}
},
firstLevel: 'abc'
}
$scope.isObject = function(value){
return angular.isObject(value);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller="ctrl">
<script type="text/ng-template" id="template">
{{key}}
<ul ng-init='temp=value'>
<input ng-if='!isObject(value)' type='text' ng-model='parent[key]'/>
<li ng-if='isObject(temp)' ng-repeat='(key, value) in temp' ng-init='parent=temp' ng-include="'template'"></li>
</ul>
</script>
<ul ng-init='parent=value'>
<li ng-repeat='(key, value) in value' ng-include="'template'"></li>
</ul>
{{value | json}}
</div>

Just to add a perhaps more elegant solution: (Note if you update angular recursive directives also becomes a thing)
angular.module('app', ['dotjem.angular.tree']).controller('ctrl', ['$scope', function($scope) {
$scope.value = {
identity: {
name: 'Max',
surName:'Smith',
},
birthDay: {
year: 1990,
month: 2,
day: {
weekDay: 'friday',
number: '13'
}
},
firstLevel: 'abc'
}
$scope.isObject = function(value){
return angular.isObject(value);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://rawgithub.com/dotJEM/angular-tree-bower/master/dotjem-angular-tree.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul dx-start-with="value as parent">
<li ng-repeat="(key, value) in parent">
{{key}}
<input ng-if="!isObject(value)" type="text" ng-model="parent[key]"/>
<ul ng-if="isObject(value)" dx-connect="value"></ul>
</li>
</ul>
<pre>{{value | json}}
</pre>
</div>

Related

Binding data to a dropdown using angularjs

I have a typescript controller with an init method below. I am able to get all the data I want in the messageresult
which is good.
init() {
this.accountLocationService.getAccountLocationCollection(this.customerNumber)
.success(messageresult => {
this.acctLocationCollection = messageresult;
//alert(this.acctLocationCollection.accountlocation[0].aliasName);
})
.error(error => {
alert(error);
});
}
The acctLocationCollection can have two or more objects and I need to bind all the values of the
aliasName in a dropdown below.
How can I achive this ? This is my first AngularJs project.
<div id="acctDetail">
<p>Select an Account:</p>
<div class="selectAccount">
<select class="selAccounts" name="DeliverToCustomerNumber" ng-change="" id="DeliverToCustomerNumber"
ng-options="ac for ac in alc.acctLocationCollection.aliasName">
</select>
</div>
<div id="address">
<dl>
<dd class="acctName">{{alc.acctLocationCollection.DeliverToCompanyName}}</dd>
<dd class="acctNo">{{alc.acctLocationCollection.DeliverToCustomerNumber}}</dd>
</dl>
</div>
</div>
Data looks like this in console.log
object
accountlocation:Array[2]
0:object
aliasName:"1234"
deliverToCustomerNumber: "25235"
1:object
aliasName:"2345"
deliverToCustomerNumber: "23523"
You don't have an ng-model on your select so I will just assume you want to store the object from your acctLocationCollection. To do this you simply need to structure your ng-options as follows:
<select class="selAccounts"
name="DeliverToCustomerNumber"
id="DeliverToCustomerNumber"
ng-model="alc.selectedAcctLocation"
ng-options="ac as ac.aliasName for ac in alc.acctLocationCollection">
</select>
angular.module('app', [])
.controller('ctrl', function() {
this.selectedAcctLocation = {};
this.acctLocation = [{
aliasName: "1234",
deliverToCustomerNumber: "25235"
}, {
aliasName: "2345",
deliverToCustomerNumber: "23523"
}];
console.log(this.acctLocation);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl as alc">
By object: <select ng-model="alc.selectedAcctLocation" ng-options="ac as ac.aliasName for ac in alc.acctLocation"></select><br/>
By (key,value): <select ng-model="alc.selectedAcctLocation" ng-options="value as value.aliasName for (key,value) in alc.acctLocation"></select>
<div style="margin-top:10px;">
alc.acctLocation:
<ul>
<li ng-repeat="(key,value) in alc.acctLocation">{{key}}: {{value}}</li>
</ul>
</div>
<div>selectedAcctLocation: {{alc.selectedAcctLocation | json}}</div>
</div>

AngularJS: uncheck checkboxes with no ng-model attribute

I want to uncheck all checkboxes when press some button. I need to do this on AngularJS (no jQuery). My checkboxes don't have ng-model attribute. How I can uncheck them?
My HTML structure:
<li ng-repeat="channel in channelsList">
<div class="checkbox">
<label><input type="checkbox" value="" ng-click="isChecked(channel.id)">
<img src="images/checkbox-unchecked.png" alt="" class="unchecked">
<img src="images/checkbox.png" alt="" class="checked"><span>{{channel.name}}</span>
</label>
</div>
</li>
My channelsList is only an array of objects with 2 properties: id and name.
Thanks for helping!
Two ng-ifs
If you really want to avoid ng-model you could achieve the same effect with... notice the checked attribute
<li ng-repeat="channel in channelsList">
<div class="checkbox">
<label>
<input ng-if="isChecked(channel.id);" type="checkbox" checked ng-click="check(channel.id);">
<input ng-if="!isChecked(channel.id);" type="checkbox" ng-click="check(channel.id);">
<img src="images/checkbox-unchecked.png" alt="" class="unchecked">
<img src="images/checkbox.png" alt="" class="checked"><span>{{channel.name}}</span>
</label>
</div>
</li>
Bind checkbox ng-model to a _selected property.
Please note that I use _selected and not selected in case your API would in a near future return a selected property that would collide with this one.
index.html
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="SampleController">
<ul>
<li ng-repeat="channel in channelsList">
<input type="checkbox" ng-model="channel._selected" /> {{channel.name}} (id={{channel.id}})
</li>
</ul>
<button ng-click="uncheckAll()">Uncheck all</button>
</body>
</html>
script.js
angular.module('app', []);
angular.module('app').controller('SampleController', function ($scope) {
$scope.channelsList = [
{id: 'c1', name: 'CNN'},
{id: 'c2', name: 'BBC'},
{id: 'c3', name: 'Discovery Channel'}
];
$scope.uncheckAll = function() {
angular.forEach($scope.channelsList, function (channel) {
channel._selected = false;
});
};
});
Here is the plunker : http://plnkr.co/edit/VPOjKMUVyrMpPfT9jLz3
I don't know exactly what you need but supposing you have an array of ids telling the ids checked like this:
$scope.channelsList = [
{'id' : 1, 'name' : 'foo' },
{'id' : 2, 'name' : 'bar' },
{'id' : 3, 'name' : 'foo' }
];
var checked = [1, 3]
$scope.isChecked = function(id){
return checked.indexOf(id) >= 0;
};
You can put a button like:
<button ng-click="uncheckAll()">click</button>
And use it to clear your array:
$scope.uncheckAll = function(){
checked = [];
}

Angular is not allowing me do the integar comparison if value is type casted in string in controller. Please have a look at this code snippet

When i add dynamic text box and compare this value to orignal prize than the result are untrue. try to add a text box and insert 111 for instance.
<div ng-app="myApp" ng-controller="nCtrl">
<h1>{{challenge.prize}}</h1>
<a ng-click="addBoxes()"> Add Box </a>
<div ng-repeat="data in challenge.boxes track by $index">
why its not working right {{challenge.boxes[$index].prize > challenge.prize}}
<input type="text" ng-model="challenge.boxes[$index].prize">
</div>
<script>
var app = angular.module('myApp', []);
app.controller('nCtrl', function ($scope) {
$scope.challenge = {
prize: '12',
boxes: []
};
$scope.addBoxes = function () {
$scope.challenge.boxes.push({prize: ''})
};
});
</script>
<p>The prize is written as a string, but formatted as a number.</p>
</body>
</html>
Set input type number in your html and it working fine.
Or use custom filter in place of angular number filter
app.filter('num', function() {
return function(input) {
return parseFloat(input, 10);
};
});
I have change the value of prize string to int,then getting correct output as mention below :
$scope.challenge = {
prize: 12,
boxes: []
};
see demo on fiddler link
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="nCtrl">
<h1>{{challenge.prize}}</h1>
<a ng-click="addBoxes()"> Click here to Add Box </a>
</br>
</br>
<div>
Now Its Working...
</div>
<div ng-repeat="data in challenge.boxes track by $index">
Conditional based Output :<strong> {{challenge.boxes[$index].prize > challenge.prize}} </strong>
<input type="text" ng-model="challenge.boxes[$index].prize">
</div>
<script>
var app = angular.module('myApp', []);
app.controller('nCtrl', function($scope) {
$scope.challenge = {
prize: 12,
boxes: []
};
$scope.addBoxes = function() {
$scope.challenge.boxes.push({
prize: ''
})
console.log($scope.challenge.boxes)
};
});
</script>
<p>The prize is written as a string, but formatted as a number.</p>

Filter data on click function in AngularJS

I have data on some bikes in my HTML page. I have to filter that data via an on click function. I have used a filter in the text box area, but I want the same functionality via an on click function.
So how can I bind the filter with the click function?
http://jsfiddle.net/3G7Kd/114/
<div ng-app='app' class="filters_ct">
<ul class="nav" ng-controller="selectFilter">
<li ng-repeat="filter in filters" ng-click="select($index)" ng-class="{sel: $index == selected}">
<span class="filters_ct_status"></span>
{{filter.name}}
<ul class="subul" ng-if=filter.lists.length>
<li ng-repeat="list in filter.lists" ng-click=" $event.stopPropagation()">
<input type="checkbox"> {{list}}
</li>
</ul>
</li>
</ul>
<input type="text" ng-model="search">
<div ng-controller="listctrl">
<div class="list" ng-repeat="list in lists | filter:{brand:search}">
{{list.brand}}
{{list.year}}
</div>
</div>
</div>
Angular
var app = angular.module('app', []);
app.controller('selectFilter', function($scope) {
$scope.filters = [
{
"name": "brand",
'lists': ['yamaha','ducati','KTM','honda']
},
{
'name': "year",
'lists': [2012,2014,2015]
}
];
$scope.selected = 0;
$scope.select= function(index) {
if ($scope.selected === index)
$scope.selected = null
else
$scope.selected = index;
};
});
app.controller('listctrl', function($scope) {
$scope.lists = [
{
"brand": "ducati",
'year': 2012
},
{
'brand': "honda",
'year': 2014
},
{
'brand': "yamaha",
'year': 2015
},
{
'brand': "KTM",
'year': 2012
}
];
});
You already knew how to use the filter when given an object within the partial. I moved one of your controllers so that you have an outer and an inner controller.
<div ng-app='app'ng-controller="MainCtrl as mainCtrl">
<div ng-controller="listCtrl">
<!-- your filter object is now accessible here -->
</div>
</div>
I added a scope variable to the outer controller $scope.activeFilters (filling this you should be able to do on your own, see the plunker for one possible solution.
This object is now changed when clicking on the checkboxes. As $scope.activeFilters is now accessible from the inner controller we can pass it to the filter as before:
<div class="list" ng-repeat="list in lists | filter:activeFilters">
{{list.brand}}
{{list.year}}
</div>
Note that there are probably nicer solutions (using the checkbox with a model among other things).
Working plunker:
http://jsfiddle.net/ywfrbgoq/

Apply logic to every element in an ng-repeat upon ng-click (AngularJS)

I'm trying to build a media library in which only one image can be selected at a time. To do this, I have a list generated with AngularJS's ng-repeat function. So far, when I click on an element, it adds a highlighting class to a child div and toggles the visibility of a child form element. How can I make the click do the opposite for every other element in the ng-repeat?
<li ng-click="show=!show" ng-repeat="media in media">
<div ng-if="show" class="selected">
<input class="bulk-check" type="hidden" name="ids[]" value="#include('_helpers.media_id')">
</div>
</li>
You could just save displayed media in a temp variable in your controller and have 2 functions 1) set the media object on click of item 2) can show function which checks for object equality.
Controller:
var shownMedia = $scope.medias[0]; //Set you default media
$scope.selectMedia = function(media) {
shownMedia = media; //on click, set the new media as selected media
}
$scope.canShow = function(media) {
return angular.equals(shownMedia, media); //Check if this is the displayed media
}
View:
<li ng-click="selectMedia(media)" ng-repeat="media in medias">
{{media.name}}
<div ng-if="canShow(media)" ng-class="{selected : canShow(media)}">
{{media.description}}
<input class="bulk-check" type="hidden" name="ids[]" value="#include('_helpers.media_id')">
</div>
</li>
Example implementation Demo
angular.module('app', []).controller('ctrl', function($scope) {
$scope.medias = [{
name: 'media1',
description: "media1"
}, {
name: 'media2',
description: "media2"
}, {
name: 'media3',
description: "media3"
}];
var shownMedia = $scope.medias[0];
$scope.selectMedia = function(media) {
shownMedia = media;
}
$scope.canShow = function(media) {
return angular.equals(shownMedia, media);
}
});
.selected{
color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-click="selectMedia(media)" ng-repeat="media in medias">
<span ng-class="{selected : canShow(media)}">{{media.name}}</span>
<div ng-if="canShow(media)">
{{media.description}}
<input class="bulk-check" type="hidden" name="ids[]" value="#include('_helpers.media_id')">
</div>
</li>
</ul>
</div>
This should work
<li ng-repeat="media in mediaList" ng-click="media.show=!media.show" >
<div ng-if="media.show" class="selected">
<input class="bulk-check" type="hidden" name="ids[]" value="#include('_helpers.media_id')">
</div>
</li>

Resources