How can I reset the value of an ng-model in the controller after an ngChange without using a directive
<div ng-repeat="i in items">
<!-- Some DOM comes here -->
<select ng-model="i.avail" ng-change="changeAvail(i.id, i.avail)">
<option value="true">Available</option>
<option value="false">Unavailable</option>
</select>
<!-- More DOM follows -->
</div>
The Javascript in the controller is as follows
$scope.changeAvail = function(itemId, value){
if(confirm("You cannot undo this action")){
//Send an ajax request to backend for an irreversible action
}
else{
//Restore input to initial value;
}
}
I wouldn't want to implement a directive just for this single occurence
You should Ideally store old value of items inside scope & do use them later to revert back to original one.
$scope.loadItem = function(){
$http.get('/api/getitems').then(function(response){
$scope.items = response.data;
$scope.oldCopy = angular.copy($scope.items); //do it where ever you are setting items
});
}
Then do send whole item to ng-change method like ng-change="changeAvail(i)"
$scope.changeAvail = function(item){
if(confirm("You cannot undo this action")){
//save object
$http.post('/api/data/save', item).then(function(){
//alert('Date saved successfully.');
$scope.loadItem(); //to update items from DB, to make sure items would be updated.
})
}
else{
//get current old object based on itemId, & then do revert it.
var oldItem = $filter('filter')($scope.oldCopy, {itemId: item.itemId}, true)
if(oldItem && oldItem.length){
item = oldItem[0]; //filters return array, so take 1st one while replacing it.
}
}
}
Related
I want to revert a select tag value that is inside a ng-repeat after cancelling a confirm dialog.
Here is what I have so far:
Relevant HTML:
<table>
<tbody>
<tr ng-repeat="application in rows">
<td>
<select
ng-model="application.selectedVersion"
ng-options="apk.versionName for apk in application.versions | orderBy : 'id' : true"
ng-init="application.selectedVersion=application.versions.filter(currentVersion, application.apkUpdates)[0]"
ng-change="selectionChanged(application, '{{application.selectedVersion}}')"
style="padding:0 1em;" />
</td>
</tr>
</tbody>
</table>
Javascript logic:
$scope.selectionChanged = function(application, previousVersion) {
var dialog = confirm('Change version?');
if (dialog) {
console.log('change version confirmed');
} else {
application.selectedVersion = previousVersion;
}
};
Passing '{{application.selectedVersion}}' to the function instead of application.selectedVersion passes the previously selected value instead of the current (explained here: https://stackoverflow.com/a/45051464/2596580).
When I change the select value, perform the dialog interaction and cancel it I try to revert the value by setting application.selectedVersion = angular.copy(previousVersion);. I can see the value is correct by debugging the javascript but the select input is set to blank instead of the actual value.
What am I doing wrong?
JSFiddle demo: https://jsfiddle.net/yt4ufsnh/
You have to correct couple of things in your implementation
When you pass '{{application.selectedVersion}}' to selectionChanged method, it becomes raw string. When you re assigns back to the application.selectedVersion you have to first parse that previousVersion to JSON using JSON.parse method
Use track by apk.id on ng-options collection. This is needed because the JSON parsed object is not recognized as the same instance of the object used to build the select, so this works as if overriding an intrinsic equals function to use only its id property
Final Version
ng-options="apk.versionName for apk in (application.versions |
orderBy : 'id' : true) track by apk.id"
Code
$scope.selectionChanged = function(application, previousVersion) {
var dialog = confirm('Change version?');
if (dialog) {
console.log('change version confirmed');
} else {
application.selectedVersion = previousVersion ? JSON.parse(previousVersion) : null;
}
};
Updated Fiddle
if you just remove the else condition from your javascript code, you can reach the behaviour you need.
Your final code should be:
$scope.selectionChanged = function(application, previousVersion) {
var dialog = confirm('Change version?');
if (dialog) {
console.log('change version confirmed');
}
};
I just followed this
JSFiddle example to create a little search box from an array object in my javascript. Now after some tweaking and research on search.object and filter:search:strict. Now that I have it filtering correctly, I modified a checkbox in the template that is checked upon loading the document and switched on or off based on a custom data attribute in the html that is updated by the json array.
How do I run this check once someone clears the search and the old items show back up again?
In the HTML I have this template
<div ng-repeat="o in objects | filter:search:strict | orderBy:'obj_name'" class="objectContainer">
<h3>{{o.obj_name}}</h3>
<label class="userToggleSwitch" >
<input class="userToggleInput" data-isactive="{{o.obj_isactive}}" type="checkbox">
<div class="slider round"></div>
</label>
</div>
In the JS
angular.element(document).ready(function(){
angular.module('searchApp',[])
.controller('searchCtrl', ['$scope','objHolder',function ($scope,objHolder) {
$scope.search = '';
$scope.objects = [];
$scope.objects = objHolder.getobjects();
}])
// fake service, substitute with your server call ($http)
.factory('objHolder',function(){
var objects = objList;
return {
getobjects : function(){
return objects;
}
};
});
});
The JS that modifies the items on document load is using jquery like this
$(document).ready(function(){
//console.log($('.userToggleInput'));
$.each($('.userToggleInput'), function(){
if($(this).data("isactive") == 1){$(this).attr('checked', true);}
if($(this).data("isactive") == 0){$(this).attr('checked', false);}
})
$('.userToggleInput').click(function(){
if ($(this).attr('checked') == undefined) {
// THIS IS WHERE WE WILL MAKE AN AJAX CALL TO A PHP CLASS
// TO REQUEST IF THE USER CAN BE TURNED ON - DUE TO LIC RESTRICTIONS
$(this).attr('checked',true);
} else {
$(this).attr('checked',false);
}
//console.log($(this).attr('checked'));
});
});
Created JS Fiddle to assist in Helping in this manner
I have a cross platform app that uses Onsen UI, Monaca and AngularJS.
I have a page that contains a number of checkboxes that are populated at runtime. I have a method that checks which checkbox is selected, and then returns that checkbox ID as was assigned to it on a database.
The view to build the list looks as follows:
<ul class="list">
<li class="list__item" ng-repeat="checkItemDescription in data">
{{checkItemDescription.checkitemdesc}}
<label class="switch switch--list-item">
<input type="checkbox"
class="switch__input"
ng-click="toggleSelection(checkItemDescription.fleetcheckitemid)">
<div class="switch__toggle"></div>
</label>
</li>
</ul>
The list is built and I can detect when a switch is toggled with the following method in my views controller
app.js
app.controller("VehicleCheckController", function($scope, $http, sharedProperties)
{
// Get the list of checkboxes from Database here...
// Initialising the selected items Array
$scope.selection = [];
// Toggle selection for a given Check Item by name
$scope.toggleSelection = function toggleSelection(fleetCheckItemID)
{
var idx = $scope.selection.indexOf(fleetCheckItemID);
// Is currently selected
if (idx > -1)
{
$scope.selection.splice(idx, 1);
console.log("$scope.selection: " + $scope.selection);
// Set the Fleet Checked Selection ID to the new value
//setFleetCheckedSelectionID($scope.selection);
// NOT WORKING HERE
}
// Is newly selected
else
{
$scope.selection.push(fleetCheckItemID);
console.log("$scope.selection: " + $scope.selection);
// Set the Fleet Checked Selection ID to the new value
//setFleetCheckedSelectionID($scope.selection);
// NOT WORKING HERE
}
}
});
Then I have a service that handles the getters and setters for all the values to be shared between my various controllers. But when I try to set the values for the $scope.selection array in my service as below from my controller, I get an error saying the array is undefined.
// Stores the varibale $scope.fleetIDSearch form the VehicleIDController to be passed to the API URL call in VehicleCheckController
app.service('sharedProperties', function()
{
// Initialise the Variables
var fleetCheckedSelectionID = [];
return {
// Get and set the Fleet Checked Selected ID number
getFleetCheckedSelectionID: function()
{
return fleetCheckedSelectionID;
},
setFleetCheckedSelectionID: function(value)
{
fleetCheckedSelectionID = value;
}
};
});
How do I set the values that the $scope.toggleSelection method produces so I can send them to the next screen?
I am new at angularjs and getting data from api like this:
function response(data){
$scope.data = data
}
<<<< data format is ilke this >>>>>
[
{"id":"1", "name":"item1"},
{"id":"2", "name":"item2"},
{"id":"3", "name":"item3"}
];
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>
Using this in view page with ng-repeat:
<button ng-repeat="item in data">{{item.name}}</button>
I will change selected item button color. But I need selected property on items. But it does not comes from database.
How can I add property named selected to items? in view or in controller?
I think you can use ng-init in repeat as,
<button ng-repeat="item in data" ng-init="item.selected = false">{{item.name}}</button>
this will add a selected property for each and every repeating object with the value of false.
Did you try a simple loop ?
$.each($scope.data, function( index, item ) {
item.selected = false;
});
You can initialize the selected attribute for all data items:
for (var i in data){
data[i].selected = false;
}
and then change it on click:
<button ng-repeat="item in data" ng-click="item.selected=true">{{item.name}}</button>
There are two attributes selCountry and searchText. There is a watch that monitors these two variables. The 1st one is bound to a select element, other is a input text field.
The behavior I expect is: If I change the dropdown value, textbox should clear out, and vice versa. However, due to the way I have written the watch, the first ever key press (post interacting with select element) swallows the keypress.
There must be some angular way of telling angular not to process the variable changes happening to those variables; yet still allow their changes to propagate to the view...?
$scope.$watchCollection('[selCountry, searchText]', function(newValues, oldValues, scope){
console.log(newValues, oldValues, scope.selCountry, scope.searchText);
var newVal;
if(newValues[0] !== oldValues[0]) {
console.log('1');
newVal = newValues[0];
scope.searchText = '';
}
else if(newValues[1] !== oldValues[1]) {
console.log('2');
newVal = newValues[1];
scope.selCountry = '';
}
$scope.search = newVal;
var count = 0;
if(records)
records.forEach(function(o){
if(o.Country.toLowerCase().indexOf(newVal.toLowerCase())) count++;
});
$scope.matches = count;
});
Plunk
I think the problem you are encountering is that you capture a watch event correctly, but when you change the value of the second variable, it is also captured by the watchCollection handler and clears out that value as well. For instance:
selCountry = 'Mexico'
You then change
selText = 'City'
The code captures the selText change as you'd expect. It continues to clear out selCountry. But since you change the value of selCountry on the scope object, doing that also invokes watchCollection which then says "okay I need to now clear out searchText".
You should be able to fix this by capturing changes using onChange event handlers using ng-change directive. Try the following
// Comment out/remove current watchCollection handler.
// Add the following in JS file
$scope.searchTextChange = function(){
$scope.selCountry = '';
$scope.search = $scope.searchText;
search($scope.search);
};
$scope.selectCountryChange = function(){
$scope.searchText = '';
$scope.search = $scope.selCountry;
search($scope.search);
};
function search(value){
var count = 0;
if(records)
records.forEach(function(o){
if(o.Country.toLowerCase().indexOf(value.toLowerCase())) count++;
});
$scope.matches = count;
}
And in your HTML file
<!-- Add ng-change to each element as I have below -->
<select ng-options="country for country in countries" ng-model="selCountry" ng-change="selectCountryChange()">
<option value="">--select--</option>
</select>
<input type="text" ng-model="searchText" ng-change="searchTextChange()"/>
New plunker: http://plnkr.co/edit/xCWxSM3RxsfZiQBY76L6?p=preview
I think you are pushing it too hard, so to speak. You'd do just fine with less complexity and watches.
I'd suggest you utilize some 3rd party library such as lodash the make array/object manipulation easier. Try this plunker http://plnkr.co/edit/YcYh8M, I think it does what you are looking for.
It'll clear the search text every time country item is selected but also filters the options automatically to match the search text when something is typed in.
HTML template
<div ng-controller="MainCtrl">
<select ng-options="country for country in countries"
ng-model="selected"
ng-change="search = null; searched();">
<option value="">--select--</option>
</select>
<input type="text"
placeholder="search here"
ng-model="search"
ng-change="selected = null; searched();">
<br>
<p>
searched: {{ search || 'null' }},
matches : {{ search ? countries.length : 'null' }}
</p>
</div>
JavaScript
angular.module('myapp',[])
.controller('MainCtrl', function($scope, $http) {
$http.get('http://www.w3schools.com/angular/customers.php').then(function(response){
$scope.allCountries = _.uniq(_.pluck(_.sortBy(response.data.records, 'Country'), 'Country'));
$scope.countries = $scope.allCountries;
});
$scope.searched = function() {
$scope.countries = $scope.allCountries;
if ($scope.search) {
var result = _.filter($scope.countries, function(country) {
return country.toLowerCase().indexOf($scope.search.toLowerCase()) != -1;
});
$scope.countries = result;
}
};
});