Selectpicker doesn't fill when using with AngularJS - angularjs

I'm trying to fill a select options (selectpicker), but without success. My competenceArray is filled correctly, but in my HTML never appears the options.
Someone can help me with the selectpicker ?
JS
(() => {
angular
.module('talent')
.controller('FunctionalityCreateCtrl', Create);
function Create($timeout, GatewayService) {
const vm = this;
vm.form = {};
vm.competencesArray = [];
getCompetences();
function getCompetences() {
GatewayService.get('/competences')
.then((data) => {
vm.competencesArray = data._embedded;
})
.catch(() => {
console.log('nope');
});
}
}
})();
HTML
<select
class="selectpicker"
data-style="select-with-transition"
title="Select a value"
data-size="7"
ng-model="functionality.form.comp"
ng-options="s.name for s in functionality.competencesArray">
<option value=""> Select</option>
<select>

You should refresh select picker after complete ajax request . From the docs :
To programmatically update a select with JavaScript, first manipulate
the select, then use the refresh method to update the UI to match the
new state. This is necessary when removing or adding options, or when
disabling/enabling a select via JavaScript.
When complete ajax request , you need refresh select , and put it in $timeout for angular re-render dom . Here is example :
function getCompetences() {
GatewayService.get('/competences')
.then((data) => {
vm.competencesArray = data._embedded;
$timeout(function(){
$('.selectpicker').selectpicker('refresh'); //put it in timeout for run digest cycle
},1)
})
.catch(() => {
console.log('nope');
});
}
Hope it help

You haven't posted the full context of your code so I'm assuming that you have defined the angular module "talent" and that the element is within the scope of the FunctionalityCreateCtrl. I have wrapped your example HTML in a div with ng-app and ng-controller attributes just so the example is functional.
The first error I see in the code example you posted is the element is not closed properly. The close tag is missing a forward slash: </select>.
The other error is you are setting the model of the select element to be form.comp but form.comp doesn't seem to be set to anything. I've added it in the code example below.
Also notice that I've added an id to the ng-options expression ng-options="s.id as s.name for s in functionality.competencesArray"> This is just so I can set the model (form.comp) to a value I know. Your data may be different.
Working html snippet:
<div ng-app="talent" ng-controller="FunctionalityCreateCtrl as functionality">
<select
class="selectpicker"
data-style="select-with-transition"
title="Select a value"
data-size="7"
ng-model="functionality.form.comp"
ng-options="s.id as s.name for s in functionality.competencesArray">
<option value=""> Select</option>
</select>
</div>
Working Javascript snippet (I've added the talent module definition and removed the api call just so the example is functional):
(() => {
angular.module('talent', []);
angular
.module('talent')
.controller('FunctionalityCreateCtrl', Create);
function Create() {
const vm = this;
vm.form = {};
vm.competencesArray = [];
getCompetences();
function getCompetences() {
vm.competencesArray = [
{ id: 1, name: 'test 1' },
{ id: 2, name: 'test 2' },
{ id: 3, name: 'test 3' }
];
vm.form.comp = 1;
}
}
})();
Here is a working JSFiddle using your example code: https://jsfiddle.net/g8r9ajtL/

Related

Angular 1.5 ng-change on select element in component triggering previous response

I'm just starting to play with components in Angular 1.5.x and have created one for a select box on a form - I pass in the field, list and label to the component and it creates the form 'part' consisting of the label and select box with the correct options - so far, so good.
Now I want to implement an ng-change on the select so I am passing in the function I want to use for this. The problem I have is that the function is triggering with the old value for the select not the new value. I can see that the function is triggering prior to the actual change. If I put the change function within the component change event it registers correctly, but not using a passed in function.
I have created a cut-down fiddle at https://jsfiddle.net/7gd3m2k0/
<div ng-app="demoApp">
<div ng-controller="RoleController as ctrl">
<nac-select
field="ctrl.MemberRole.record.member"
list="ctrl.Lists.Member"
label="Member"
on-change="ctrl.MemberRole.changeMember();"></nac-select>
<div ng-bind="ctrl.MemberRole.record.member.name"></div>
</div>
</div>
angular.module('demoApp', [])
.component('nacSelect', nacSelect)
.controller('RoleController', roleController)
var nacSelect = {
bindings: {
field: '=',
list: '<',
label: '#label',
onChange: '&'
},
controller: function () {
var ctrl = this;
ctrl.change = function () {
alert(ctrl.field.name); //gives new selection
ctrl.onChange(); //gives previous selection
}
},
template: `
<label>{{$ctrl.label}}</label>
<select ng-model="$ctrl.field" ng-options="r as r.name for r in $ctrl.list" ng-change="$ctrl.change();">
<option value="">(Please Select)</option>
</select>
`
};
var roleController = function(){
var ctrl = this;
ctrl.Lists = {
Member: [
{id: 1, name: 'Fred Smith'},
{id: 2, name: 'Jenny Jones'},
{id: 3, name: 'Jane Doe'}
]
};
ctrl.MemberRole = {
record: {
member: null
},
changeMember: function(){
alert(ctrl.MemberRole.record.member.name);
}
};
};
I guess I am missing something simple, but I can't figure it out. Thank you for your assistance
You need to pass in variable that you want to alert in the first controller, check this:
https://jsfiddle.net/pegla/7gd3m2k0/1/
so your function would look like this:
changeMember: function(name){
alert(name);
}
when you're using nac-select
<nac-select field="ctrl.MemberRole.record.member" list="ctrl.Lists.Member" label="Member" on-change="ctrl.MemberRole.changeMember(name);"></nac-select>
and in the end in the nac-select:
<select ng-model="$ctrl.field" ng-options="r as r.name for r in $ctrl.list | orderBy: 'name'" ng-required="$ctrl.req" ng-if="!$ctrl.list.processing" ng-change="$ctrl.onChange($ctrl.field)">
or if you want to pass in object:
<nac-select field="ctrl.MemberRole.record.member" list="ctrl.Lists.Member" label="Member" on-change="ctrl.MemberRole.changeMember({id, name});"></nac-select>
then your changeMember can look like this:
changeMember: function(obj){
alert(`${obj.name} ${obj.id}`);
}
New fiddle:
https://jsfiddle.net/pegla/7gd3m2k0/2/

Retain the dropdown value after the page is refresh or move to another page in angularjs

let me explain my scenario. I am having the dropdownlist in my master page. if changed dropdownlist the data are changed depend upon the dropdownlist value.
If i refreshed my page or moved to another page the dropdownlist will clear automatically.
I want to retain the dropdownlist value after refresh the page or moved to another page.
I tried like this but this is not help ful.
HTML
<select id="Facility_ID" required typeof="text" name="Facility Name" ng-options="Role.FacilityName for Role in Roles"
form="DistanceMatrixFormId" class="form-control" ng-model="Role._id" ng-selected="getFacilityID(Role._id.FacilityName)">
</select>
Controller.js
$scope.getFacilityID = function (data) {
localStorage.setItem("FacilityName", data);
var getfacility = localStorage.getItem("FacilityName");
}
i refered this link but it is not help ful
I don't know how to retain the value. can any one fixed me.
You cannot put an object into the local storage, you must store strings inside local storage.
If you want, you can have an implementation I did here : How to add local storage in angular?
For your current code, you don't need to use ng-selected. ng-model is enough.
<select id="Facility_ID" required typeof="text"
name="Facility Name"
ng-options="Role.FacilityName for Role in Roles"
form="DistanceMatrixFormId"
class="form-control"
ng-model="Role._id"
ng-change="updateLocalStorage()">
</select>
And inside your angular controller, what you can do is the following :
myApp.controller('controllerName', ['$scope', 'LocalStorageService',
function($scope, LocalStorageService){
// After initialization of your "Role" object
$scope.Role._id = LocalStorageService.retrieve('mySelectValue');
$scope.updateLocalStorage = function(){
LocalStorageService.store('mySelectValue', $scope.Role._id);
}
}])
Here is an example that uses a service to store the selected value. Unfortunately the embedded demo does not work because of the sandboxing, but works when served as an application.
angular.module(
"App", []
).factory("MyStorage", function() {
const key = "selected";
return {
getValue: function() {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : null;
},
setValue: function(value) {
localStorage.setItem(key, JSON.stringify(value));
}
};
}).controller("MyCtrl", function($scope, MyStorage) {
$scope.options = ["A", "B", "C"];
$scope.selected = MyStorage.getValue();
$scope.$watch("selected", newValue => {
MyStorage.setValue(newValue);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="App" ng-controller="MyCtrl">
<select ng-model="selected" ng-options="x for x in options">
</select>
</div>

select2, ng-model and angular

Using jquery-select2 (not ui-select) and angular, I'm trying to set the value to the ng-model.
I've tried using $watch and ng-change, but none seem to fire after selecting an item with select2.
Unfortunately, I am using a purchased template and cannot use angular-ui.
HTML:
<input type="hidden" class="form-control select2remote input-medium"
ng-model="contact.person.id"
value="{{ contact.person.id }}"
data-display-value="{{ contact.person.name }}"
data-remote-search-url="api_post_person_search"
data-remote-load-url="api_get_person"
ng-change="updatePerson(contact, contact.person)">
ClientController:
$scope.updatePerson = function (contact, person) {
console.log('ng change');
console.log(contact);
console.log(person);
} // not firing
$scope.$watch("client", function () {
console.log($scope.client);
}, true); // not firing either
JQuery integration:
var handleSelect2RemoteSelection = function () {
if ($().select2) {
var $elements = $('input[type=hidden].select2remote');
$elements.each(function(){
var $this = $(this);
if ($this.data('remote-search-url') && $this.data('remote-load-url')) {
$this.select2({
placeholder: "Select",
allowClear: true,
minimumInputLength: 1,
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
url: Routing.generate($this.data('remote-search-url'), {'_format': 'json'}),
type: 'post',
dataType: 'json',
delay: 250,
data: function (term, page) {
return {
query: term, // search term
};
},
results: function (data, page) { // parse the results into the format expected by Select2.
return {
results: $.map(data, function (datum) {
var result = {
'id': datum.id,
'text': datum.name
};
for (var prop in datum) {
if (datum.hasOwnProperty(prop)) {
result['data-' + prop] = datum[prop];
}
}
return result;
})
}
}
},
initSelection: function (element, callback) {
// the input tag has a value attribute preloaded that points to a preselected movie's id
// this function resolves that id attribute to an object that select2 can render
// using its formatResult renderer - that way the movie name is shown preselected
var id = $(element).val(),
displayValue = $(element).data('display-value');
if (id && id !== "") {
if (displayValue && displayValue !== "") {
callback({'id': $(element).val(), 'text': $(element).data('display-value')});
} else {
$.ajax(Routing.generate($this.data('remote-load-url'), {'id': id, '_format': 'json'}), {
dataType: "json"
}).done(function (data) {
callback({'id': data.id, 'text': data.name});
});
}
}
},
});
}
});
}
};
Any advice would be greatly appreciated! :)
UPDATE
I've managed to put together a plunk which seems to similarly reproduce the problem - it now appears as if the ng-watch and the $watch events are fired only when first changing the value.
Nevertheless, in my code (and when adding further complexity like dynamically adding and removing from the collection), it doesn't even seem to fire once.
Again, pointers in the right direction (or in any direction really) would be greatly appreciated!
There are a number of issues with your example. I'm not sure I am going to be able to provide an "answer", but hopefully the following suggestions and explanations will help you out.
First, you are "mixing" jQuery and Angular. In general, this really doesn't work. For example:
In script.js, you run
$(document).ready(function() {
var $elements = $('input[type=hidden].select2remote');
$elements.each(function() {
//...
});
});
This code is going to run once, when the DOM is initially ready. It will select hidden input elements with the select2remote class that are currently in the DOM and initialized the select2 plugin on them.
The problem is that any new input[type=hidden].select2remote elements added after this function is run will not be initialized at all. This would happen if you are loading data asynchronously and populating an ng-repeat, for example.
The fix is to move the select2 initialization code to a directive, and place this directive on each input element. Abridged, this directive might look like:
.directive('select2', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModel) {
//$this becomes element
element.select2({
//options removed for clarity
});
element.on('change', function() {
console.log('on change event');
var val = $(this).value;
scope.$apply(function(){
//will cause the ng-model to be updated.
ngModel.setViewValue(val);
});
});
ngModel.$render = function() {
//if this is called, the model was changed outside of select, and we need to set the value
//not sure what the select2 api is, but something like:
element.value = ngModel.$viewValue;
}
}
}
});
I apologize that I'm not familiar enough with select2 to know the API for getting and setting the current value of the control. If you provide that to me in a comment I can modify the example.
Your markup would change to:
<input select2 type="hidden" class="form-control select2remote input-medium"
ng-model="contact.person.id"
value="{{ contact.person.id }}"
data-display-value="{{ contact.person.name }}"
data-remote-search-url="api_post_person_search"
data-remote-load-url="api_get_person"
ng-change="updatePerson(contact, contact.person)">
After implementing this directive, you could remove the entirety of script.js.
In your controller you have the following:
$('.select2remote').on('change', function () {
console.log('change');
var value = $(this).value;
$scope.$apply(function () {
$scope.contact.person.id = value;
});
});
There are two problems here:
First, you are using jQuery in a controller, which you really shouldn't do.
Second, this line of code is going to fire a change event on every element with the select2remote class in the entire application that was in the DOM when the controller was instatiated.
It is likely that elements added by Angular (i.e through ng-repeat) will not have the change listener registered on them because they will be added to the DOM after the controller is instantiated (at the next digest cycle).
Also, elements outside the scope of the controller that have change events will modify the state of the controller's $scope. The solution to this, again, is to move this functionality into the directive and rely on ng-model functionality.
Remember that anytime you leave Angular's context (i.e if you are using jQuery's $.ajax functionality), you have to use scope.$apply() to reenter Angular's execution context.
I hope these suggestions help you out.

How to listen on select2 events in AngularJS

select2 provides some custom events and i want to be able to listen to them, particularly the 'select2-removed' or better yet its custom 'change' event, and I’ve been searching the internet for some example, but with no luck.
here's what I’ve done so far:
HTML:
<input type="hidden" class="form-control" id="tags" ui-select2="modal.tags" data-placeholder="Available Tags" ng-model="form.tags">
JavaScript (Angular)
$scope.form = {tags: []};
postalTags = [
{
id: 1,
text: 'Permanent Address'
},
{
id: 2,
text: 'Present Address'
}
];
$scope.modal {
tags: {
'data': postalTags,
'multiple': true
}
};
// I doubt this is going to work, since i think this is only going to
// listen on events emitted by $emit and $broadcast.
$scope.$on('select2-removed', function(event) {
console.log(event);
});
// I can do this, but then i will not know which was removed and added
$scope.$watch('form.tags', function(data) {
console.log(data);
});
What the user actually is doing here is edit the tags tagged to his/her address, and by edit I mean the user can tag new tags to his/her address or remove previously tagged tags. That's why I need to track which tags where added and which are removed.
UPDATE
I saw a plausible solution on this discussion, but i cant make the provided code to work with mine, so i did some workaround, and this is what i did.
so instead of adding the,
scope.$emit('select2:change', a);
somewhere around here,
elm.select2(opts);
// Set initial value - I'm not sure about this but it seems to need to be there
elm.val(controller.$viewValue)
I put it here,
if (!isSelect) {
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
elm.bind("change", function (e) {
// custom added.
scope.$emit('select2:change', e);
e.stopImmediatePropagation();
then i did the usual on my controller,
$scope.$on('select2:change', function(event, element) {
if(element.added) console.log(element.added);
if(element.removed) console.log(element.removed);
}
and works just fine.
But i doubt this is very good idea, and i'm still hoping for a better solution.
I used a directive to encapsulate the select and then in the link function I just retrieve the select element and at the event handler.
Markup:
<select name="id" data-ng-model="tagsSelection" ui-select2="select2Options">
<option value="{{user.id}}" data-ng-repeat="user in users">{{user.name}}</option>
</select>
Javascript:
link: function (scope, element, attributes) {
var select = element.find('select')[0];
$(select).on("change", function(evt) {
if (evt.added) {
// Do something
} else if (evt.removed) {
// Do something.
}
});
$scope.select2Options = {
multiple: true
}
}

AngularJS Checkbox not working

I have a class called Case that contains a list of executionSteps. Each executionStep has a boolean property called enabled. I am trying to set in on the HTML side but it never gets updated on the JS side.
HTML side
<td>
<input type="checkbox"
ng-checked="acase.executionSteps[0].enabled"
ng-model="aa" ng-change="updateCaseExecutionStep('{{study.id}}','{{acase.id}}','{{acase.executionSteps[0].id}}','{{acase.executionSteps[0]}}')"/>
</td>`
On the controller side I have the function
updateCaseExecutionStep
defined as shown below
$scope.updateCaseExecutionStep = function(studyId,caseId,executionStepId,executionStep){
...
...
}
Problem is when I update my checkbox or even manually update the enabled property of the executionStep
$scope.updateCaseExecutionStep = function(studyId,caseId,executionStepId,executionStep){
executionStep.enabled = true;
...
}
I don't see any change. The enabled property of executionStep passed in the JS does not change. Please help.
Do I have to modify somehow on the The HTML side ?
You are trying to force too complex solution. To start with, you do not need ng-checked nor ng-change when you are using ng-model.
Let's say you have the following controller
app.controller('MainCtrl', function($scope) {
$scope.case = {
caseId: 0,
steps: [
{ id: 1, name: 'First step', enabled: true },
{ id: 2, name: 'Second step', enabled: false },
{ id: 2, name: 'Third step', enabled: false }]
};
});
And related HTML
<div ng-repeat="step in case.steps">
<input type="checkbox" ng-model="step.enabled"> {{ step.name }}
</div>
That's all it takes!
Example Plunk here http://plnkr.co/edit/QjD91l
Edit:
If you need to do some processing based on selection, then yes, you could add ng-change to input control. Then HTML becomes
<input type="checkbox" ng-model="step.enabled" ng-change="stateChanged(step)"> {{ step.name }}
And in controller
$scope.stateChanged = function(step){
console.log('Changed step id:' + step.id + ' enabled state to ' + step.enabled;
};
I had to abandon ng-model for my checkbox as it was not picking up the initial value that was set in the model (in response to a service call). All of the other input controls were responding correctly to the model (and, interestingly, were correctly enabled/disabled based on the value backing the checkbox).
Instead, I used the 'checked' attibute and ng-click, as so:
<input type="text" ng-disabled="!myModel.isFruit" ng-model="myModel.seedCount">
<input type="checkbox" checked="{{myModel.isFruit}}" ng-click="onFruitClicked()"> Is Fruit
In my controller:
$scope.myModel = {
isFruit : myServices.getIsFruit(),
seedCount : myServices.getSeedCount()
};
$scope.onFruitClicked = function() {
// toggle the value
$scope.myModel.isFruit = !$scope.myModel.isFruit;
// save the new value
myServices.setIsFruit($scope.myModel.isFruit);
};

Resources