I am trying to move items between two select lists using the code below, but items are not moved from the availableClients list to selectedClients lists, so can someone please check the code below and let me know what I am missing here? Thanks
<div ng-app>
<div ng-controller="testCtrl">
<label for="aclients">Available Clients</label>
<select size="5" multiple ng-model="available" ng-options="client.id as client.Name for client in clientsList" style="width: 400px"></select>
<input id="moveright" type="button" value="Add Client" ng-click="moveItem(available[0], availableclients,selectedclients)" />
<input id="moverightall" type="button" value="Add All Clients" ng-click="moveAll(availableclients,selectedclients)" />
<input id="move left" type="button" value="Remove Client" ng-click="moveItem(selected[0], selectedclients,availableclients)" />
<input id="moveleftall" type="button" value="Remove All Clients" ng-click="moveAll(availableclients,selectedclients)" />
<label for="sclients">Selected Clients</label>
<select size="5" multiple ng-model="selected" ng-options="client.id as client.Name for client in selectedclients" style="width: 400px"></select>
<div>Selected Clients IDs: {{selectedclients}}</div>
</div>
</div>
Controller:
app.controller('testCtrl',
function testCtrl($scope, clientsService){
$scope.clientsList = clientsService.getClientsList().then(
function(response){
$scope.clientsList = response;
},
function(status){
console.log(status);
}
);
$scope.moveItem = function(item, from, to) {
console.log('Move item Item: '+item+' From:: '+from+' To:: '+to);
//Here from is returned as blank and to as undefined
var idx=from.indexOf(item);
if (idx != -1) {
from.splice(idx, 1);
to.push(item);
}
};
$scope.moveAll = function(from, to) {
console.log('Move all From:: '+from+' To:: '+to);
//Here from is returned as blank and to as undefined
angular.forEach(from, function(item) {
to.push(item);
});
from.length = 0;
};
$scope.availableclients = [];
$scope.selectedclients = [];
});
There are several small problems in your template:
You're moving objects from availableclients to selectedclients, but the first select displays options from clientsList, and not from availableclients
You're moving IDs rather than objects. Your ng-options should simply be
client as client.name for client in availableclients
Your remove all button moves from available to selected, instead of moving from selected to available.
Here is a working plunkr: http://plnkr.co/edit/RYEmpkBjQStoCfgpWPEK?p=preview
<label for="aclients">Available Clients</label>
<select size="5" multiple ng-model="available" ng-options="client as client.name for client in availableclients" style="width: 400px"></select>
<input id="moveright" type="button" value="Add Client" ng-click="moveItem(available[0], availableclients,selectedclients)" />
<input id="moverightall" type="button" value="Add All Clients" ng-click="moveAll(availableclients,selectedclients)" />
<input id="move left" type="button" value="Remove Client" ng-click="moveItem(selected[0], selectedclients,availableclients)" />
<input id="moveleftall" type="button" value="Remove All Clients" ng-click="moveAll(selectedclients,availableclients)" />
<label for="sclients">Selected Clients</label>
<select size="5" multiple ng-model="selected" ng-options="client as client.name for client in selectedclients" style="width: 400px"></select>
In regard to my comment/question. I actually found an answer.
So for those that come here and have the same issue, here is what I found.
When moving a item from one SELECT list to another SELECT list, the angular model on the source list can get "lost".
To avoid this, the changed to each list must be done in separate $apply function call. Here is a cut down example from within in the event handler
onClickRight = function (item, from, to) {
var self = this;
var selecteditem = angular.copy(item);
self.$timeout(function () {
self.scope.$apply(function () {
for (var idx = 0; idx < from.length; idx++) {
if (from[idx].value == item.value && from[idx].displayValue == item.displayValue) {
item.length = 0;
from.splice(idx, 1);
break;
}
};
});
}, 200);
self.$timeout(function () {
self.scope.$apply(function () {
to.push(selecteditem);
});
}, 300);
};
The 'item' is cloned with angular.copy so it can be used in the second angular $apply
My options have a 2 properties: value and displayValue
I also assign $timeout and $scope to the 'self' variable in the directive's constructor.
Hope this helps
Related
I have a text box that show when I click on checkbox and I need to make an event on it that can make me bind it to object from DB
HTML:
<div ng-repeat="feture in featureEnumArray">
<input id="txtchkFet" type="checkbox" ng-model="feture.IsChecked" />
{{feture.DisplayText}}
<div ng-show="feture.IsChecked" >
<input class="form-control" type="text" ng-model="feture.Value" ng-change="getFeatureID(feture)" id="txtValue" placeholder="Type Feature Value" />
</div>
<div class="label label-danger" ng-show="feture.IsChecked && !feture.Value">
Type Value
</div>
</div>
And in Angular controller I did Like this :
$scope.getFeatureID = function (feture) {
if (feture.IsChecked) {
$scope.Planfeature = angular.copy(feture);
$scope.PlanFeatureTemp.FeatureID = $scope.Planfeature.ID;
$scope.PlanFeatureTemp.Value = $scope.Planfeature.Value;
$scope.Planfeature = [];
}
else {
var index = $scope.JAdminPlan.PlanFeatureValues.indexOf(feture);
$scope.JAdminPlan.PlanFeatureValues.splice(index, 1);
}
if (!$scope.$$phase) $scope.$apply();
};
<div class="info-block" ng-app="">
<div ng-controller="Note">
<div class="checkbox">
<label>
<p><b>Primary Publication: </b>
{{ form_widget(form.input_ppubs, { 'attr': {'class': 'valOption'}}) }}
</p>
</label>
</div>
<div ng-repeat="item in items">
<input type="text" placeholder="new input" ng-model="item.primaryPub">
</div>
<button type="button" ng-click="add()">New Item</button>
</div>
I am trying to retrieve the value of the html input field and remove it from input upon a button click
I am able to get the code, but I don't know how to remove the code.
var Note = function($scope){
$scope.items = [];
$scope.add = function () {
//angular way of implementing document.getElementByID();
pub1 = angular.element('#form_input_ppubs').val();
$scope.items.push({
primaryPub: pub1
});
};
}
You don't have to retrieve your items like this. It's ugly and not the angular way. angular.element('#form_input_ppubs').val();
Instead, simply reference it in your input using ngModel.
Declare it in your scope.
$scope.inputItem = null;
HTML
<input ng-model="inputItem " />
Use it in your function:
$scope.addItem = function(item) {
$scope.items.push(item);
//reset the model
$scope.inputItem = null;
}
Call it using ng-click
<button type="button" ng-click="addItem(inputItem)">Add Item</button>
If you do:
console.log($scope.items);
You should see an entry for primaryPub, the model for your input. Then you can target it by nulling the model, so:
$scope.items.primaryPub = null;
However you're using this inside an ng-repeat:
<div ng-repeat="(i, item) in items">
<input type="text" placeholder="new input" ng-model="items[i].primaryPub">
</div>
So your console.log (if you have more than one item in 'items') should show an array-like structure for primaryPub.
I'm very new to angular so I may be going about this all wrong but here goes. I have a form
<form name="search_form" novalidate ng-submit="searchForm(search_form.$valid)" >
<div class="maincontainer">
<div class="formcontainer">What to eat?</div>
<div class="formcontainer"><input type="text" name="food_type" ng-model="food_type" placeholder="Enter a search term" required></div>
<div class="formcontainer">Where to look?</div>
<div class="formcontainer"> <input type="text" name="cityname" ng-model="trader.cityname" value="cityname" googleplace="" placeholder="Enter a location" required>
</div>
<div class="formcontainer">
<button type="submit" class="btn-main2" >Submit</button>
</div>
</form>
that when I submit I want to grab the results based on the location I get from google and display them in a new view
myControllers.controller('SearchCtrl',['$scope','Search','$location', function ($scope,Search,$location) {
$scope.setSearchLocation = function(place){
$scope.lat = place.geometry.location.lat();
$scope.lng = place.geometry.location.lng();
}
$scope.searchForm = function() {
// check to make sure the form is valid
if (!$scope.search_form.$valid) {
alert('Please fill out all fields');
}
else{
$scope.results = Search.do_search($scope.lat,$scope.lng);
$location.path('search-results');
}
};
}])
.directive('googleplace', function() {
return {
require : 'ngModel',
link : function(scope, element, attrs, model) {
var options = {
types : [],
};
scope.gPlace = new google.maps.places.Autocomplete(element[0],options);
google.maps.event.addListener(scope.gPlace, 'place_changed',function() {
var place = scope.gPlace.getPlace();
scope.setSearchLocation(place);
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
},
};
});
everything works as expected except the view does not update in the results view. If I set the $scope.results out side the searchForm() function everything renders properly. I realize this is because it exists before the page renders, just saying that part works.
when I try $scope.$apply() it says already in progress
<div id="results-container" ng-repeat="result in results">
<div id="picbox"><img src="../images/test.jpg" alt="" "/></div>
<div id="addressinfo">
<h4>John's Restaurant </h4>
<p>123 York Street, Toronto ON <br>
<span id="type">#
Burgers, #Poutine</span></p>
</div>
<div id="location">4.2m<br>
<img src="../images/heart.png" width="86" height="76" alt=""/><br>
</div>
</div>
</div>
When you call $location.path(...), $scope object of controller is always initialized.
My suggestion is ...
write the element of div#results-container on the same template where form[name=search_form] exists.
remove $location.path('search-results');
I hope this could help you.
I am making app with angular js. It goes like this.
User creates groups and adds group names
User creates 'websites' and for each website he can check groups that are created in previous step
Problem is that all groups checkboxes get checked when he checks just one.
Here is the code that generates the checkboxes:
<p>Groups:
<label ng-repeat='group in groups'>
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="{{group.id}}"/>
{{group.name}}</label></p>
Here is the code that is outputed:
<label ng-repeat="group in groups" class="ng-scope ng-binding">
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="0" class="ng-valid ng-dirty">
first group</label>
<label ng-repeat="group in groups" class="ng-scope ng-binding">
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="1" class="ng-pristine ng-valid">
second group</label>
Thanks!
edit: here is the plunker link http://beta.plnkr.co/edit/OVBoTDY2YmXgSy8TAbIW
This (plunker) is how I would do it. The idea is to create a model to keep track of checked groups.
JS
app.controller("WebsitesCtrl", function($scope) {
$scope.newSite = {};
$scope.newGroup = {};
$scope.checkedGroupIds = {};
$scope.sites = [];
var groupMap = {};
$scope.groups = [];
var siteIdSeq = 0;
function createSite(newSite, groups) {
$scope.sites.push(newSite);
newSite.id = siteIdSeq;
newSite.groups = groups;
siteIdSeq++;
return newSite;
}
var groupIdSeq = 0;
function createGroup(newGroup) {
$scope.groups.push(newGroup);
newGroup.id = groupIdSeq;
groupMap[newGroup.id] = newGroup;
groupIdSeq++;
return newGroup;
}
$scope.submitSite = function() {
var groups = [];
angular.forEach($scope.checkedGroupIds, function(checked, id) {
if(checked) {
groups.push(groupMap[id]);
}
});
createSite($scope.newSite, groups);
$scope.newSite = {};
$scope.checkedGroupIds = {};
};
$scope.submitGroup = function() {
createGroup($scope.newGroup);
$scope.newGroup = {};
};
//test data
$scope.newSite.url = 'http://www.my-site.com';
var all = createGroup({name:'All'});
var home = createGroup({name:'Home'});
var fav = createGroup({name:'Fav'});
createSite({url:'http://www.stackoverflow.com'}, [all, fav]);
createSite({url:'http://www.google.com'}, [fav]);
createSite({url:'http://www.plnkr.co'}, [home]);
});
HTML
<div id="website-form">
Sites:
<ul>
<li ng-repeat="site in sites">{{site}}</li>
</ul>
<form ng-submit="submitSite()">
<label>Site url: <input type="url" ng-model="newSite.url" /></label>
<p>Groups:
<label ng-repeat='group in groups'>
<input type="checkbox" name="group-check" value="{{group.name}}" id="{{group.id}}"
ng-model="checkedGroupIds[group.id]" />
{{group.name}}
</label>
</p>
<input type="submit" id="submitWebsite" value="Save Changes" ng-disabled="!newSite.url" />
</form><!-- end submitSite() -->
</div>
<div id="group-form">
<form ng-submit="submitGroup()">
<label>Name of the group: <input ng-model="newGroup.name" /></label>
<input type="submit" class="btn btn-primary" id="submitGroup" value="Save Changes"
ng-disabled="!newGroup.name"/>
</form><!-- end submitGroup() -->
</div>
HTML:
<ul>
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" value="{{ selectedUser.firstname }}" ng-model="selectedUser.firstname">
<input type="text" value="{{ selectedUser.lastname }}" ng-model="selectedUser.lastname">
</form>
I am dealing with user objects fetched from my REST API. So basically there is a list of users. On click the above form is revealed.
function UserController($scope, User){
$scope.users = User.query();
$scope.selectedUser = null;
$scope.select = function(user){
$scope.selectedUser = user;
}
}
I want to display the save link only when form values have changed. Any ideas how to do this with angular.js?
Give your form a name, such as:
<form name="dataForm">
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
The form will now be a named model in your scope and you can hide/show the save button based on whether the form is pristine:
<ul ng-hide="dataForm.$pristine">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
This approach has the advantage of showing the save button if you change any of the form elements inside the form and the drawback of not checking the input values against their original values, just the fact that they have been edited.
Here is an example of showing your element only when both fields have data:
<div ng-controller="TestCtrl" ng-app>
<ul ng-show="enableSave(data)">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
</div>
And here is your controller:
function TestCtrl($scope) {
$scope.data = {firstname: "", lastname: ""};
$scope.enableSave = function(data) {
return data.firstname.length > 1 && data.lastname.length > 1;
};
}
You can put any logic you want into enableSave. I've chosen to require that they both have at least two characters... you can do whatever you need.
Here is a jsFiddle that illustrates it: http://jsfiddle.net/nDCXY/1/
EDIT by OP: my solutions
$scope.enableSave = function(user) {
if(!angular.equals(user, oldUser)){
return true
}else{
return false;
}
};