I use select2 in my Angular project , Actually I have a problem that is I have no idea about how to set default value for select-option. Here is my code :
HTML :
<select-tag-manager parent-id="2" value="restaurant.type" ></select-tag-manager>
Angular :
app.directive('selectTagManager', function() {
return {
restrict: "E",
replace: true,
scope: {
parentId: '#',
value: '='
},
controller: function($rootScope, $scope, Gateway, toaster, $element, Tags) {
var element;
$scope.update = function () {
};
var makeStandardValue = function(value) {
var result = [];
angular.forEach(value , function(tag , key) {
if(result.indexOf(tag.tagId) < 0) {
result.push(tag.tagId);
}
});
return result;
};
var init = function () {
Gateway.get('', '/tag?' + 'parentId=' + $scope.parentId, function(response) {
$scope.allPossibleTags = response.data.result.tags;
});
element = $($element).children().find('select').select2();
console.log(element);
};
$scope.$watch('value', function(newval) {
if( newval ) {
$scope.standardValue = [];
angular.forEach(newval, function(val, key) {
$scope.standardValue.push(val.tagName);
});
console.log($scope.standardValue);
}
});
init();
},
templateUrl: 'selectTagManager.html'
}
});
selectTagManager.html:
<div class="row">
<div class="col-md-12">
{{ standardValue }}
<select class="select2" multiple="multiple" ng-model="standardValue" ng-change="update()">
<option ng-if="tag.tagId" ng-repeat="tag in allPossibleTags" data-id="{{tag.tagId}}" value="{{tag.tagId}}">{{ tag.tagName }}</option>
</select>
</div>
</div>
I got value
console.log($scope.standardValue);
result: ["lazzania", "pizza", "kebab"]
But I don't know how to set them as default value in select-option. Any suggestion?
EDITED :
I've just edited my question using Angular-ui/ui-select2. I changed my template :
<select ui-select2 = "{ allowClear : true }" ng-model="standardValue" multiple="multiple" >
<option value="standardId" ></option>
<option ng-repeat="tag in allPossibleTags" value="{{tag.tagId}}">{{tag.tagName}}</option>
</select>
And also my js:
$scope.$watch('value', function(newval) {
if( newval ) {
$scope.standardValue = [];
$scope.standardId = [];
// $scope.standardValue = makeStandardValue(newval);
console.log('----------------------------------------------------------------------');
angular.forEach(newval, function(val, key) {
$scope.standardValue.push(val.tagName);
$scope.standardId.push(val.tagId);
});
console.log($scope.standardValue);
console.log($scope.standardId);
}
});
Nevertheless , Still I can't set default value.
as demonstarted at http://select2.github.io/examples.html#programmatic, one can set default values for multiple select2 element as follows:
$exampleMulti.val(["CA", "AL"]).trigger("change");
so, in you case you have already element variable pointing to your select2:
element.val($scope.standardValue).trigger('change');
note, that this is jQuery approach of setting/changing values, angular approach would be to update values via ng model and its life cycle events
The IDs in your model need to match the IDs in your data source, so if your model is:
["lazzania", "pizza", "kebab"]
Then allPossibleTags needs to look like:
[{ tagId: "lazzania", tagName: "Lazzania" }, { tagId: "pizza" ...
Check out this plunk for a working example:
http://plnkr.co/edit/e4kJgrc69u6d3y2CbECp?p=preview
Related
I created a custom directive to handle select2 in VueJs. The code below works when I am binding a select to a data property in my viewmodel that is not a propert of an object within data.
Like this.userId but if it is bound to something like this.user.id, it would not update the value in my viewmodel data object.
Vue.directive('selected', {
bind: function (el, binding, vnode) {
var key = binding.expression;
var select = $(el);
select.select2();
vnode.context.$data[binding.expression] = select.val();
select.on('change', function () {
vnode.context.$data[binding.expression] = select.val();
});
},
update: function (el, binding, newVnode, oldVnode) {
var select = $(el);
select.val(binding.value).trigger('change');
}
});
<select v-selected="userEditor.Id">
<option v-for="user in users" v-bind:value="user.id" >
{{ user.fullName}}
</option>
</select>
Related fiddle:
https://jsfiddle.net/raime910/rHm4e/4/
When you using 1st level $data's-property, it accessing to $data object directly through []-brackets
But you want to pass to selected-directive the path to nested object, so you should do something like this:
// source: https://stackoverflow.com/a/6842900/8311719
function deepSet(obj, value, path) {
var i;
path = path.split('.');
for (i = 0; i < path.length - 1; i++)
obj = obj[path[i]];
obj[path[i]] = value;
}
Vue.directive('selected', {
bind: function (el, binding, vnode) {
var select = $(el);
select.select2();
deepSet(vnode.context.$data, select.val(), binding.expression);
select.on('change', function () {
deepSet(vnode.context.$data, select.val(), binding.expression);
});
},
update: function (el, binding, newVnode, oldVnode) {
var select = $(el);
select.val(binding.value).trigger('change');
}
});
<select v-selected="userEditor.Id">
<option v-for="user in users" v-bind:value="user.id" >
{{ user.fullName}}
</option>
</select>
Description:
Suppose we have two $data's props: valOrObjectWithoutNesting and objLvl1:
data: function(){
return{
valOrObjectWithoutNesting: 'let it be some string',
objLvl1:{
objLvl2:{
objLvl3:{
objField: 'primitive string'
}
}
}
}
}
Variant with 1st level $data's-property:
<select v-selected="valOrObjectWithoutNesting">
// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to:
vnode.context.$data['valOrObjectWithoutNesting'] = select.val();
Variant with 4th level $data's-property:
<select v-selected="objLvl1.objLvl2.objLvl3.objField">
// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to:
vnode.context.$data['objLvl1.objLvl2.objLvl3.objField'] = select.val(); // error here
So the deepSet function in my code above "converting" $data['objLvl1.objLvl2.objLvl3.objField'] to $data['objLvl1']['objLvl2']['objLvl3']['objField'].
As you see, as I mentioned in comments to your question, when you want make select2-wrapper more customisable, the directive-way much more complicated, than separate component-way. In component, you would pass as much configuration props and event subscriptions as you want, you would avoid doing side mutations like vnode.context.$data[binding.expression] and your code would become more understandable and simpler for further support.
A custom directive is perfectly fine, except use the insertedhook instead of bind. Adapted from Vue Wrapper Component Example.
To bind to an object property, the simplest way is to wrap it in a computed setter Computed Setter and bind to that.
Note, 'deep setting' does not appear to work. The problem is one of change detection, which the computed setter overcomes. (Note that the on('change' function is jQuery not Vue.)
console.clear()
Vue.directive('selected', {
inserted: function (el, binding, vnode) {
var select = $(el);
select
.select2()
.val(binding.value)
.trigger('change')
.on('change', function () {
if (vnode.context[binding.expression]) {
vnode.context[binding.expression] = select.val();
}
})
},
});
var vm = new Vue({
el: '#my-app',
computed: {
selectedValue: {
get: function() { return this.myObj.type },
set: function (value) { this.myObj.type = value }
}
},
data: {
selectedVal: 0,
myObj: { type: 3 },
opts: [{
id: 1,
text: 'Test 1'
}, {
id: 2,
text: 'Test 2'
}, {
id: 3,
text: 'Test 3'
}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.4/vue.js"></script>
<div id="my-app">
<div>
<label for="example">Test dropdown list ({{ myObj.type }})</label>
</div>
<div>
<select id="example" style="width: 300px" v-selected="selectedValue">
<option v-for="(opt,index) in opts" :value="opt.id" :key="index">
{{ opt.text }}
</option>
</select>
</div>
</div>
I don't get any error message, but while debugging Scope values are not binding it shows Undefined...
Controller:
$scope.companyModify = function () {
var param = {
companyId:$scope.companyId,
companyName:$scope.companyName,
billPrintLinesTop:$scope.billPrintLinesTop,
billPrintLinesBottom:$scope.billPrintLinesBottom,
isPrintHeader:$scope.isprintHeader,
billTypeId:$scope.billTypeId,
billColumnId :$scope.billColumnId,
noOfCopies: $scope.noOfCopies,
billHeaderAlignmentId: $scope.billHeaderAlignmentId,
billTitle: $scope.billTitle,
billSortOrderId:$scope.billSortOrderId,
posDefaultQty:$scope.posDefaultQty,
posTaxTypeId:$scope.posTaxTypeId,
isAllowNegativeStock:$scope.isAllowNegativeStock,
serviceTaxCalcTypeId : $scope.serviceTaxCalcTypeId,
wishMessage:$scope.wishMessage,
coinageBy:$scope.coinageBy,
isAutoGenerateProductCode:$scope.isAutoGenerateProductCode
};
console.log(param);
Calling the companyModify Function :
Open braces of companyModify closes in SocketService...
SocketService.post(apiManage.apiList['CompanyModify'].api,param).
then(function (resp) {
var data = resp.data.response;
if (data.status === true) {
angular.forEach($scope.companyList, function (value) {
if (value.companyId == $scope.companyId) {
value.$edit = false;
}
});
Notify.alert(data.message, {
status: 'success',
pos: 'top-right',
timeout: 5000
});
$scope.load();
}
else {
Notify.alert(data.message, {
status: 'danger',
pos: 'top-right',
timeout: 5000
});
}
});
};
Ensure $scope is properly injected in controller
someModule.controller('MyController', ['$scope', function($scope) {
...
$scope.aMethod = function() {
...
}
...
}]);
In your html code, input values are binded to company.XXX :
<input type="text" class="form-control input-sm" ng-model="company.posTaxTypeId" placeholder="Enter POSTaxCalculation" >
If you want your code to work is the controller, you must use the same binding and use posTaxTypeId: $scope.company.posTaxTypeId instead of posTaxTypeId: $scope.posTaxTypeId
or change your html code to :
<span data-ng-show="!company.isEdit" data-ng-bind="posTaxTypeId"></span>
<span data-ng-show="company.isEdit">
<input type="text" class="form-control input-sm" ng-model="posTaxTypeId" placeholder="Enter POSTaxCalculation" >
</span>
also ensure that binding are declared properly, without spaces :
bad
data-ng-bind="company.posTaxTypeId "
ng-model="company.posTaxTypeId "
good
data-ng-bind="company.posTaxTypeId"
ng-model="company.posTaxTypeId"
I've really hit a brick wall with this, and I know I'm probably missing something here, but I'm stuck and need help. What I'm trying to do is use a service to populate the options in an ng-options directive; however, the ng-options are inside of a custom directive, and I've tried everything from track by, to testing it outside of the directive, inside the directive, etc. Can someone please take a look at this code and see if you can spot what I'm missing? Any help is greatly appreciated. It WILL work as far as executing the update to the ng-model; however, at page landing and record selection, it will not initially select the proper option, but if I take the track by out, it will initialize with the proper selection, it just won't update ng-model when/if I do that.
angular
.module('app')
.controller('mainCtrl', ['acctList', 'CONSTANTS', 'FORMFIELDS', function(acctList, CONSTANTS, FORMFIELDS) {
var mainCtrl = this;
mainCtrl.form = {};
mainCtrl.formFields = FORMFIELDS;
mainCtrl.currentRecord = null;
mainCtrl.editedRecord = {};
mainCtrl.setCurrentRecord = function(value) {
mainCtrl.currentRecord = value;
mainCtrl.editedRecord = angular.copy(mainCtrl.currentRecord);
};
mainCtrl.statuses = CONSTANTS.statuses;
}])
.value('FORMFIELDS', [
{
key: 'active_flag',
inputtype: 'select',
type: 'text',
class: 'form-control',
id: 'activeFl',
name: 'activeFl',
placeholder: 'Active Flag',
required: true,
maxlength: 1,
disabled: false,
labelfor: 'inputActiveFl',
labeltext: 'Active Flag',
field: 'mainCtrl.editedRecord.ACTIVE_FL',
options: 'list as list.desc for list in mainCtrl.statuses track by list.value'
}
])
.value('CONSTANTS',
{
statuses: [
{
id: 1,
value: "Y",
desc: "Active"
},
{
id: 2,
value: "N",
desc: "Inactive"
}
]
}
)
.directive('formTemplate', ['$compile', function($compile) {
function linker(scope, element, attr) {
scope.$watch(attr.modeltemp, function(modeltemp) {
// if ngModel already equals modeltemp or modeltemp doesn't exist, return
if (attr.ngModel == modeltemp || !modeltemp) return;
// remove all attributes to prevent duplication
element.removeAttr('placeholder');
element.removeAttr('type');
element.removeAttr('class');
element.removeAttr('id');
element.removeAttr('name');
element.removeAttr('ng-required');
element.removeAttr('maxlength');
element.removeAttr('ng-disabled');
// add the ng-model attribute presently tied to modeltemp
element.attr('ng-model', modeltemp);
// if modeltemp is blank, then remove ng-model, as it would be null
if (modeltemp == '') {
element.removeAttr('ng-model');
}
// Unbind all previous event handlers, this is
// necessary to remove previously linked models.
element.off();
// run a compile on the element, injecting scope, to reconstruct the element
$compile(element)(scope);
});
console.log(scope.acctCtrl);
}
// dynamic templating function associated with the templateUrl in the DDO
function template (tElement, tAttrs) {
// set the type variable equal to the value from the tAttr for 'inputtype' coming from the view
var type = tAttrs['inputtype'];
// just declaring the return variable for cleanliness
var tpl;
// begin the switch-case statement for each inputtype, then set it's return variable equal to the respective url
switch(type) {
case 'input':
tpl = '/common/directives/formTemplate/formTemplate.template.html';
break;
case 'select':
tpl = '/common/directives/formTemplate/formTemplateSelect.template.html';
break;
default:
tpl = '/common/directives/formTemplate/formTemplate.template.html';
break;
}
return tpl;
}
return {
restrict: 'EA',
replace: true,
templateUrl: template,
link: linker
};
}])
<form class="form-horizontal" ng-submit="submit()" name="mainCtrl.form.newAcctForm">
<div class="col-lg-6 form-fields" ng-repeat="fields in mainCtrl.formFields" ng-class="{ 'has-error': mainCtrl.form.newAcctForm.{{fields.name}}.$dirty }">
<label class="control-label" for="{{fields.labelfor}}">{{fields.labeltext}}</label>
<div form-template modeltemp="fields.field" inputtype="{{fields.inputtype}}"></div>
</div>
</form>
<select class="{{fields.class}}" id="{{fields.id}}" name="{{fields.name}}" ng-options="{{fields.options}}" ng-required="{{fields.required}}" maxlength="{{fields.maxlength}}" ng-disabled="{{fields.disabled}}">
<option value="">Please select...</option>
</select>
While this does work, did you consider using lifecycle hooks instead, waiting until after the view has loaded/initialized? Your solution works, but it's a bit like using a rocket launcher on an ant hill.
I want to use Select2 in my AngularJS project, So I added an input like this :
<select class="find-neighborhood js-states form-control"
ng-model="regionIdentifier"
ng-init="">
</select>
Luckily , Whenever regionIdentifier is change, I find out. Actually I want to set initial value in my Select2. Here is my javascript code :
$(".find-neighborhood").select2({
dir: "rtl",
placeholder: "find neighborhood ...",
allowClear: true,
data: $scope.regions
});
My $scope.regions looks like :
[object,object,object]
Each object is like :
0 :Object
id:52623
regionSlug:yvuj
text:yvuj1
How to init value in my select2?
You should create a directive to make this work globally and fine. Here is a simple example which let you initialize a select2 set the default option values to your selection. You can find a working version on plnkr. While select2 depends on jQuery you may will look for an other lib to make it work. Some dev's preffer to have no jQuery included in AngularJS projects.
Controller
//option list
$scope.regions = {
'test1' : {
id: 1
},
'test2' : {
id: 2
},
'test3' : {
id: 3
},
'test4' : {
id: 4
},
'test5' : {
id: 5
}
};
//model & selected value setup
$scope.regionIdentifier = 3;
View
<select class="js-example-basic-single"
ng-model="regionIdentifier"
items="regions"
items-selected="regionIdentifier"
id="regionSelection"
select-two>
</select>
Directive
/**
* Simple select2 directive
*/
angular.module('app').directive('selectTwo', ['$timeout', function($timeout){
return {
restrict : 'EA',
transclude : true,
terminal: true,
templateUrl : 'views/directive/select2.html',
scope: {
items: "=",
itemsSelected: "="
},
link: function($scope, $element, $attrs, $controller){
//format options https://select2.github.io/select2/#documentation
function format(state) {
//init default state
var optionTemplate = state.text;
if (!state.id || state.id == 0) { //option group or no selection item
return optionTemplate;
}
return optionTemplate;
}
//init on load
$timeout(function(){
//if multiple options are possible, parse an comma seperated list into itemsSelected like 1,2,5,23,21
if (angular.isString($scope.itemsSelected) && $scope.itemsSelected.split(',').length > 1) {
$scope.itemsSelected = $scope.itemsSelected.split(',');
}
$($element[0]).select2({
formatResult: format,
formatSelection: format,
escapeMarkup: function(m) { return m; }
}).val($scope.itemsSelected == undefined ? [0]: [$scope.itemsSelected]).trigger("change");
});
}
};
}]);
Directive template views/directive/select2.html
<option ng-repeat="(name, item) in items track by $index"
value="{{ item.id }}">
{{ name }}
</option>
i'm really new to AngularJS and i like it very much.
But i'm experiencing a problem trying to initialize a prealoaded dropdown with a specific value.
The dropdown is initialized with values available from JSON array, but when i try to select a default value in this dropdown, i don't see that value selected but the ng-model variable is set correctly.
I created a plunker example here http://plnkr.co/edit/7su3Etr1JNYEz324CMy7?p=preview tryng to achieve what i want, but i can't get it to work. I tried with ng-repeat and ng-select, with no luck. Another try i did (in this example) is trying to set the ng-selected property.
This is a part of my html
<body ng-controller="MySampleController">
<select name="repeatSelect" id="repeatSelect" ng-model="SelectedStatus" ng-init="SelectedStatus">
<option ng-repeat="option in StatusList[0]" value="{{option.key}}" ng-selected="{{option.key==SelectedStatus}}">{{option.name}}</option>
</select>
<select name="repeatSelect" id="repeatSelect" ng-model="SelectedOrigin">
<option ng-repeat="option in OriginList[0]" value="{{option.key}}" ng-selected="{{option.key == SelectedOrigin}}">{{option.key}} - {{option.name}}</option>
</select>
<pre>Selected Value For Status: {{SelectedStatus}}</pre>
<pre>{{StatusList[0]}}</pre>
<pre>Selected Value For Origin: {{SelectedOrigin}}</pre>
<pre>{{OriginList[0]}}</pre>
</body>
And this is code from my controller
function MySampleController($scope) {
$scope.StatusList = [];
$scope.OriginList = [];
$scope.ServiceCall = {};
$scope.EntityList = [];
$scope.SelectedStatus = -3;
$scope.SelectedOrigin = 1;
var myList = [
{
item: 'Status',
values: [{ key: -3, name: 'Aperto' },
{ key: -1, name: 'Chiuso' }]
},
{
item: 'Origin',
values: [{ key: 1, name: 'Origin1' },
{ key: 2, name: 'Origin2' },
{ key: 3, name: 'Origin3' }]
}
];
$scope.documentsData = myList;
angular.forEach($scope.documentsData, function (value) {
$scope.EntityList.push(value);
switch ($scope.EntityList[0].item) {
case 'Status':
$scope.StatusList.push($scope.EntityList[0].values);
$scope.EntityList = [];
break;
case 'Origin':
$scope.OriginList.push($scope.EntityList[0].values);
$scope.EntityList = [];
break;
}
});
}
Any help would be appreciated!
Thanks in advance.
You can at least use ng-options instead of ng-repeat + option, in which case the default value works just fine.
<select name="repeatSelect" id="repeatSelect"
ng-options="opt.key as opt.key+'-'+opt.name for opt in StatusList[0]"
ng-model="SelectedStatus"></select>`
You can also make it a bit more readable by specifying the option label as a scope function.
HTML: ng-options="opt.key as getOptionLabel(opt) for opt in StatusList[0]"
Controller:
$scope.getOptionLabel = function(option) {
return option.key + " - " + option.name;
}
Plunker: http://plnkr.co/edit/7BcAuzX5JV7lCQh772oo?p=preview
Value of a select directive used without ngOptions is always a string.
Set as following and it would work
$scope.SelectedStatus = '-3';
$scope.SelectedOrigin = '1';
Read answer here in details ng-selected does not work with ng-repeat to set default value