I've created a custom directive as a replacement for the <select>
element (we have potentially millions of results, so I've split it into pages).
One of the uses for this is to assign a value to a dynamically created object, when assigning the value to the first item in this object it works great, however when I try assigning the second or more it just overwrites the first ngModel.
As the project is for work it is confidential so I will add the bare minimum code unless you require to see more (and of course it will all be redacted).
Thanks for any help
ks-select-modal
directive:app.controller('SelectModalCtrl', function($scope)
{
});
app.directive('ksSelectModal', function($parse)
{
var rtn =
{
restrict: 'E',
templateUrl: "/scripts/directives/ksSelectModal/ksSelectModalPrivate.html",
scope: {
mdNoFloat: '@',
ksPlaceholder: '@',
ksArray: '=',
ksDisplay: '=',
ngModel: '=',
ksTitle: '=',
ksNoMargin: '@',
ksInclude: '@',
ksInit: '=',
ksId: '@',
ksResults: '@',
ksChangePage: '&',
ksRequired: '@'
},
require: 'ngModel',
controller: function($scope)
{
if ($scope.selectModel === undefined)
{
$scope.selectModal = {
inScope: true
};
}
},
link: function($scope, element, attrs, ngModel)
{
var model = $parse(attrs.ngModel);
console.log(model);
$scope.$watch('ngModel', function(newValue, oldValue)
{
console.log(newValue);
});
if ($scope.ksRequired)
{
ngModel.$validators.ksRequired = function (modelValue, viewValue)
{
console.log('test');
if (viewValue)
{
ngModel.$setValidity('ksRequired', true);
}
else
{
ngModel.$setValidity('ksRequired', false);
}
console.log(ngModel.$valid);
return viewValue;
};
}
}
};
return rtn;
});
app.directive('ksSelectModalPrivate', function($parse, $q, $timeout, $compile, $http, $mdDialog, $document)
{
var rtn =
{
restrict: 'E',
templateUrl: "/scripts/directives/ksSelectModal/ksSelectModal.html",
scope: {
mdNoFloat: '=',
ngModel: '=',
ksResults: '=',
ksPlaceholder: '=',
ksArray: '=',
ksDisplay: '=',
ksTitle: '=',
ksNoMargin: '=',
ksInclude: '=',
ksId: '=',
ksChangePage: '=',
ksRequired: '='
},
require: 'ngModel',
link: function($scope, element, attrs, ngModel)
{
var model = $parse(attrs.ngModel);
$scope.$watch('filter', function(newValue, oldValue)
{
if ($scope.prevSearch)
{
$timeout.cancel($scope.prevSearch);
}
$scope.prevSearch = $timeout(function()
{
$scope.FirstPage();
}, 1000);
});
$scope.Open = function(ev)
{
$scope.$parent.selectModal.Open(ev);
};
$scope.OnSelect = function(value)
{
$scope.$parent.selectModal.OnSelect(value);
};
$scope.FirstPage = function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'block');
$scope.$parent.selectModal.page = 1;
$scope.ksChangePage({
page: $scope.$parent.selectModal.page,
results: $scope.ksResults,
filter: $scope.filter
}).then(function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'none');
});
}
$scope.NextPage = function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'block');
$scope.$parent.selectModal.page++;
$scope.ksChangePage({
page: $scope.$parent.selectModal.page,
results: $scope.ksResults,
filter: $scope.filter
}).then(function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'none');
});
}
$scope.PrevPage = function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'block');
$scope.$parent.selectModal.page--;
$scope.ksChangePage({
page: $scope.$parent.selectModal.page,
results: $scope.ksResults,
filter: $scope.filter
}).then(function()
{
angular.element('#' + $scope.ksId + ' .loading').css('display', 'none');
});
}
$scope.$parent.selectModal = {
isOpen: false,
element: element,
page: 1,
OnSelect: function(value)
{
$scope.$parent.selectModal.isOpen = false;
console.log($scope.ngModel);
ngModel.$viewModel = value;
model.assign($scope, value);
console.log(ngModel.$viewModel);
$mdDialog.hide();
$scope.$watch(ngModel.$viewModel, function(newValue, oldValue)
{
if (newValue == null)
{
angular.element($scope.$parent.selectModal.element).children('md-input-container').children('label').css({
display: 'block'
});
}
});
if ($scope.mdNoFloat !== undefined)
{
angular.element($scope.$parent.selectModal.element).children('md-input-container').children('label').css({
display: 'none'
});
}
else
{
angular.element($scope.$parent.selectModal.element).children('md-input-container').addClass('md-input-has-value');
angular.element($scope.$parent.selectModal.element).children('md-input-container').removeClass('md-input-focused');
}
},
Open: function(ev)
{
$scope.$parent.selectModal.isOpen = true;
if ($scope.mdNoFloat !== undefined)
{
}
else
{
angular.element($scope.$parent.selectModal.element).children('md-input-container').addClass('md-input-focused');
}
console.log($scope.ksId);
$mdDialog.show({
contentElement: '#' + $scope.ksId,
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true
}).finally(function()
{
$scope.$parent.selectModal.isOpen = false;
});
}
};
var elemClickHandler = function(e)
{
e.stopPropagation();
};
var docClickHandler = function()
{
if (!$scope.$parent.selectModal.isOpen)
{
angular.element($scope.$parent.selectModal.element).children('md-input-container').removeClass('md-input-focused');
}
};
element.on('click', elemClickHandler);
$document.on('click', docClickHandler);
}
};
return rtn;
});
ksSelectModal.html
<style>
ks-select-modal .md-button:hover:not([disabled])
{
background-color: transparent;
}
ks-select-modal .md-button:hover:not([disabled]) .md-list-item-inner
{
color:rgba(0, 0, 0, 0.87) !important;
}
ks-select-modal md-list-item
{
border-bottom:1px solid rgba(0,0,0,0.12);
}
ks-select-modal .md-input-focused md-list-item
{
border-bottom: 2px solid rgb(63,81,181);
}
</style>
<md-input-container ng-style="{ 'margin': (ksNoMargin ? '0px' : 'inherit') }">
<label style="bottom:77%; padding-left: 5px;">{{ ksPlaceholder }}</label>
<md-list>
<md-list-item md-no-ink ng-click="Open($event)">
<span style="margin-left: -15px;" ng-bind="ksDisplay"></span>
<md-icon class="md-secondary" md-svg-src="/v1/images/icons/ic_arrow_drop_down_black_24px.svg" style="margin-right: -20px;"></md-icon>
</md-list-item>
</md-list>
</md-input-container>
<div style="display: none" ng-init="FirstPage()">
<div class="md-dialog-container" id="{{ksId}}">
<md-dialog layout-padding>
<p>{{ ksTitle }}</p>
<div>
<md-button class="md-primary md-raised" ng-click="PrevPage()" ng-if="$parent.selectModal.page > 1">
<i class="mdi mdi-chevron-left"></i>
</md-button>
<md-button class="md-primary md-raised md-secondary" ng-click="NextPage()">
<i class="mdi mdi-chevron-right"></i>
</md-button>
<md-input-container>
<label>Filter</label>
<input type="text" ng-model="filter" />
</md-input-container>
<md-progress-circular style="position:absolute;top:30px;right:30px;" md-mode="indeterminate" class="md-secondary loading"></md-progress-circular>
</div>
<ng-include src="ksInclude"></ng-include>
</md-dialog>
</div>
</div>
ksSelectModalPrivate.html
<ks-select-modal-private md-no-float="mdNoFloat"
ng-model="ngModel"
ks-placeholder="ksPlaceholder"
ks-array="ksArray"
ks-display="ksDisplay"
ks-title="ksTitle"
ks-no-margin="ksNoMargin"
ks-id="ksId"
ks-required="ksRequired"
ks-change-page="ksChangePage"
ks-results="ksResults"
ks-include="ksInclude">
</ks-select-modal-private>
Here is where the directive is used with the ngRepeat:
<md-list flex="100" flex-sm="100" flex-xs="100">
<md-subheader class="md-no-sticky">Properties</md-subheader>
<md-list-item ng-repeat="item in selectedProperties" id="property-list">
<p>{{ item.DisplayText}}</p>
<md-content class="md-secondary">
<table>
<tr>
<td align="right" style="width:100%;">
<ks-select-modal style="width:100%"
ks-id="selectDataType"
ks-title="'Select Data Type'"
ks-array="dataTypes"
ks-placeholder="Select Data Type"
ng-model="item.DataType"
ks-display="item.DataType._DataType"
ks-results="21"
ks-required="true"
ks-change-page="GetDataTypes(page, results, filter)"
ks-include="/v1/views/pages/site/modals/select-data-type.html">
</ks-select-modal>
<div ng-messages="item.DataType.$error">
<div ng-message="ksRequired">required</div>
</div>
</td>
<td align="right">
<md-button class="md-icon-button" aria-label="Remove" ng-click="RemoveProperty(item.Id)">
<md-icon md-svg-icon="images/icons/ic_delete_black_24px.svg"></md-icon>
</md-button>
</td>
</tr>
</table>
</md-content>
</md-list-item>
<md-divider></md-divider>
<ks-select-modal style="width:100%"
ks-title="'Select Property'"
ks-array="propertyConcepts"
ks-id="selectProperty"
md-no-float
ks-no-margin
ks-placeholder="Select Property"
ng-model="lastProperty"
ks-display="lastProperty.DisplayText"
ks-results="21"
ks-change-page="GetProperties(page, results, filter)"
ks-include="/v1/views/pages/site/modals/select-concept.html"></ks-select-modal>
</md-list>
select-data-type.html
<md-list>
<md-list-item ng-repeat="dataType in ksArray" style="float:left; width:30%;min-width:300px;">
{{ dataType._DataType }}
<md-button class="md-secondary" ng-click="OnSelect(dataType)">Select</md-button>
</md-list-item>
</md-list>
The only other required piece of information is there is a $watch
on the lastProperty
scope variable which adds the value into the selectedProperties
array.
Any help would be greatly appreciated.
I've solved the issue.
The ID being passed into ksId
was the same for all elements created by the ngRepeat
. By changing the ksId
value to add {{$index}}
to the end of it solved the issue.