Semantic UI dropdown with angular not clear selected value - javascript

I've created a small sample of what is happening.
http://plnkr.co/edit/py9T0g2aGhTXFnjvlCLF
Basically, the HTML is:
<div data-ng-app="app" data-ng-controller="main">
<select class="ui dropdown" id="ddlState" data-ng-options="s.name for s in states track by s.id" data-ng-model="selectedState"></select>
<select class="ui dropdown" id="ddlCity" data-ng-options="c.name for c in cities track by c.id" data-ng-model="selectedCity"></select>
</div>
And the javascript is:
angular.module("app", [])
.controller("main", function($scope, $timeout) {
$scope.selectedState = {id:1,name:"A"};
$scope.selectedCity = {id:1,name:"A.1",stateId:1};
$scope.states = [{id:1,name:"A"},{id:2,name:"B"},{id:3,name:"C"}];
var fakeDataSource = [
{id:1,name:"A.1",stateId:1},
{id:2,name:"A.2",stateId:1},
{id:3,name:"A.3",stateId:1},
{id:4,name:"B.1",stateId:2},
{id:5,name:"B.2",stateId:2},
{id:6,name:"B.3",stateId:2},
{id:7,name:"C.1",stateId:3},
{id:8,name:"C.2",stateId:3},
{id:9,name:"C.3",stateId:3}
];
$scope.$watch("selectedState", function(n,o){
if (n !== o)
$scope.selectedCity = null;
$scope.cities = fakeDataSource.filter(function(x){
return n.id === x.stateId;
});
$timeout(function(){
$(".ui.dropdown").dropdown().dropdown("refresh");
});
})
$timeout(function(){
$(".ui.dropdown").dropdown();
})
})
The problem is when I change the first dropdown to value 'B' or 'C', the value of second dropdown does not change, even it is changed in angular model.
You guys can notice that I've the line $(".ui.dropdown").dropdown().dropdown("refresh") to refresh the values but does not work.
I tried destroy and recreate using $(".ui.dropdown").dropdown("destroy").dropdown() but still does not work.
Any help?

Simply using ngModel won't make the values change dynamically. Take a look at the documentation here: https://docs.angularjs.org/api/ng/directive/ngModel
You can bind the values using ngBind or what I have done is do an onChange to then check the value and change your second drop down accordingly. Something like:
$("#ddlState").on("change", function(e) {
//check $scope.selectedState for it's value, and change #ddlCity/$scope.selectedCity accordingly
});

Related

AngularJS- Autofill Dropdown with Information from Previous Page

I have searched quite throughly on StackOverFlow, but did not find my answer, so I will ask below.
I have a form with a dropdown menu. And if the user clicks a button, it will take it to a new html page with more information to fill out. But how do I pass my data from the dropdown menu to the next page and autofill the same dropdown menu with the option selected if I am using the same controller for the form for both pages?
var app = angular.module('myApp', []);
var currentEquipmentType = "";
app.controller('myController', ['$scope', function($scope) {
$scope.addEquipment.type = "";
$scope.addEquipment.name = "";
$scope.typeList = ['A', 'B', 'C', 'D'];
//trying to get this info passed onto the next page.
if (sessionStorage.type) {
currentEquipmentType = sessionStorage.type;
}
$scope.getEquipmentInfo = function() {
if ($scope.addEquipment.name !== undefined) {
sessionStorage.name = $scope.addEquipment.name;
sessionStorage.type = $scope.addEquipment.type;
} else {
// warning message
}
}
}]);
<input type="text" ng-model="addEquiment.name">
<select ng-model="addEquipment.type" ng-options="type for type in typeList">
<option value="" selected="selected">Please select a type.</option>
</select>
<!-- on the next page (different html file, but uses the same controller as the previous page) -->
<!-- more form inputs here -->
<select ng-model="addEquipment.type" ng-options="type for type in typeList">
<option>NEED THE OPTION SELECTED FROM PREVIOUS PAGE</option>
</select>
You can good use directive and set the same template for both dropdowns like in example, every element has attribute select-box will filled this template:
var app=angular.module('myApp', []);
app.directive("selectBox", function() {
return {
restrict: 'A',
template :'<select ng-model="addEquipment" ng-options="type for type in typeList"><option value="">Please select a type.</option></select>',
link: function(scope, elem, attrs) {
scope.typeList = ['A','B','C','D'];
}
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" >
<div select-box></div>
</div>
SERVICES SOLUTION:
One way to persist data in AngularJS apps would be in Services (or Factories). Services are singletons so they will persist data as long as you don't refresh the page.
app.service('YourService', function() {
let yourDataFromPreviousPage = {
page: 1,
// currentEquipmentType: '',
// properties can be added later from controllers when you have them resolved.
};
function yourData() {
return ['A','B','C','D'];
}
angular.extend(this,{
yourData,
yourDataFromPreviousPage
});
});
Then just inject the service in your directive, and you can get/set variables on service from there.
More info about services:
https://docs.angularjs.org/guide/services
Really good comparison between services and factories:
https://toddmotto.com/factory-versus-service
UI-ROUTER SOLUTION
Another way would be to use ui-router to keep track of your routes through states - I would recommend this approach since it's de-facto standard nowdays for SPA applications. You can set up routes for different pages and when you switch to different page-state you load different parameters.
You can pass objects between states using:
$state.go('yourstate', {
// your object properties
});
For more info about ui-router:
https://github.com/angular-ui/ui-router/wiki/Quick-Reference
LOCAL STORAGE
You got that covered in previous comments.

Display checkboxes in with AngularJS based on two object arrays

I know there are several similar topics already but I found none that really matches my problem.
When opening my AngularJS app/website I am loading two arrays of objects (both are of the same type). One list contains all possible values (called sources here) and the other list is a selection of elements from the first.
My goal is display all sources as checkboxes. The ones from the second list have to be preselected, the rest not. Now the user can select/deselect checkboxes. If he does so I need to inform the server (with the source.id).
What I got so far:
exampleApp.controller('testController', [ '$scope', '$http', function($scope, $http) {
$scope.sources = [];
$scope.selectedSources = [];
$scope.changeSource = function(source) {...};
})
and
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="??"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
What I can't figure out is how I can get ng-model to preselect the right checkboxes and how to get the new (and old) values to changeSource(). Is there an elegant way of doing that?
Example (Pseudo code only):
Sources = [{id=1, name=test1},{id=2, name=test2}, ...]
SelectedSources = [{id=2, name=test2}]
Now what I need are checkboxes like this:
[ ] test1 [x] test2
where all elements from sources are checkboxes and the ones from selectedsources are preselected. Changes of the selection can be stored in selected sources (as objects) and have to trigger my changeSource() function so that I can inform my server.
Set the selected/unselected state in a property inside each of the objects in Sources array(initialize it based on whats present in selectedArray)
$scope.sources.forEach(function(source) {
source.selected = isSelected(source);
})
function isSelected(selectedSource) {
return !!$scope.selectedSources.find(function(s) {
return s === selectedSource || s.id == selectedSource.id;
})
}
Here's a working plunker link
I didn't understood your question very well, but if i'm not mistaken, you want to fill the second collection only with the selected items from the first one, right? If it's the case, you could turn your second collection into a returning function with a filter of the first inside, as follows:
In your controller:
exampleApp.controller('testController', [ '$scope', '$http', function ($scope, $http) {
$scope.sources = [];
/* ... */
$scope.getSelectedSources = function () {
return $scope.sources.filter(function (val) {
return val.selected;
});
};
})
In your template:
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
<div ng-repeat="source in getSelectedSources()">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
Hi this may be help you to get new & old value.
$scope.$watch('sources', function (oldval, newval) {
console.log(oldval + newval);
});

Setting selected items in angular-bootstrap-duallistbox, using $http

With the angular-bootstrap-duallistbox component, documented here, and using the $http service, How do I set the selected elements on it?
Here is the used code:
HTML:
<select ng-options="obj as obj.name for obj in authorizations"
multiple
ng-model="selections"
bs-duallistbox
></select>
Javascript:
angular.module('demoApp', ['frapontillo.bootstrap-duallistbox'])
.controller('ProfileAutAsigController',
function ($scope, $http, Authorization) {
$scope.authorizations = [];
$scope.selections = [];
Authorization.query().$promise.then(function (response) {
$scope.authorizations = response;
return $http.get("api/profileAut/1/authorizations");
}).then(function (payload) {
//This doesn't set the selected items. Specifying an
//array manually doesn't work either.
//$scope.selections = [{id:1, name:'Text'},{id:3, name:'Photos'}];
$scope.selections = payload.data;
}, function (payload) {
//Error happened
console.log(payload.error);
});
});
Thanks.
To populate angular bootstrap dual list box you should use track by instead of as like this:
You have to use track by instead as in ng-options
Instead this:
<select ng-model="selected" ng-options="av as av.name for av in available" multiple bs-duallistbox></select>
Uses:
<select ng-model="selected" ng-options="av.name for av in available track by av.id" multiple bs-duallistbox></select>
At last I couldn't make work angular-bootstrap-duallistbox with $http service. I used instead this angular directive, which worked with $http:
https://github.com/alexklibisz/angular-dual-multiselect-directive
Be sure of following the items json format. Read the example.

Angular Custom Filtering by Date Quarter

I've got an array of objects, ng-repeated on the page. I've also got a list of date quarters arrayed in a dropdown select box. If the quarter selected in the dropdown comes later than an item in the ng-repeated list, that item should be filtered out.
Unfortunately, I can't get it to work. Here's my HTML:
<div ng-app="programApp" ng-controller="programController">
<select ng-model="advancedFiltersy">
<option ng-repeat="indexitem in quarters">{{indexitem}}</option>
</select>
<div ng-repeat="item in listing | advFilterName:advancedFiltersy:quarters">
{{item.name}}
</div>
</div>
Here's my Angular Script:
angular.module('programApp', ['programApp.controllers','programApp.filters']);
angular.module('programApp.controllers', [])
.controller('programController', ['$scope', '$filter',
function($scope, $filter){
$scope.advancedFiltersy = 'zeroeth';
$scope.quarters = ['zeroeth','first','second','third','fourth','fifth','sixth'];
$scope.listing = [{'name':'aa','aacApprovalIconQuarter':'zeroeth'
},{'name':'ab','aacApprovalIconQuarter':'first'
},{'name':'ac','aacApprovalIconQuarter':'second'
},{'name':'ad','aacApprovalIconQuarter':'third'
},{'name':'ae','aacApprovalIconQuarter':'fourth'
},{'name':'af','aacApprovalIconQuarter':'fifth'
},{'name':'ag','aacApprovalIconQuarter':'sixth'}];
}]);
angular.module('programApp.filters', []).filter('advFilterName', function(){
return function(entries, advancedFiltersy, quarters){
var advFiltered = [];
angular.forEach(entries, function (entry){
if(quarters.indexOf(advancedFiltersy) > quarters.indexOf(entry.aacApprovalIconQuarter)){
}else{
advFiltered.push(entry);
};
});
};
});
None of the ng-repeated items are ever showing, so the filter isn't working properly. What do I need to do to get the filter working?
Here's a Codepen of it: http://codepen.io/trueScript/pen/MwbVpO
You still need to return the value of the filtered results:
angular.module('programApp.filters', []).filter('advFilterName', function(){
return function(entries, advancedFiltersy, quarters){
var advFiltered = [];
angular.forEach(entries, function (entry){
if(quarters.indexOf(advancedFiltersy) > quarters.indexOf(entry.aacApprovalIconQuarter)){
}else{
advFiltered.push(entry);
};
});
// Fix
return advFiltered;
};
});
Fixed here: http://codepen.io/anon/pen/LVbdQo?editors=101

watchcollection swallowing an attribute being watched

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;
}
};
});

Categories

Resources