HTML:
<select class="form-control" ng-model="sortBy">
<option value="createdAt"><strong>Date</strong></option>
<option value="ProgressStateNumber"><strong>Progress</strong></option>
<option value="adminUsername"><strong>adminUsername</strong></option>
</select>
<tbody>
<tr
reportrowgroup
ng-repeat="report in reportTree track by $index | orderBy: sortBy"
report="report"
>
Directive:
.directive('reportrowgroup', function () {
return {
restrict: 'EA',
template:
'<td>{{report.createdAt | date:"yyyy-MM-dd HH:mm"}}</td>'+
'MORE HTML'
scope: {
report: '='
},
controller: function() {
},
link: function(scope,elem,attr,ctrl) {
}
}
}
})
Everything works, except the table doesn't sort by createdAt when I select createdAt in select.
What could be the problem?
Your reportrowgroup directive is creating a new scope and therefore missing the reference to the same sortBy variable. Try wrapping the sortyBy in an object.
<!-- markup -->
<select class="form-control" ng-model="input.sortBy">
// Code
angular.module('myApp').controller('MyController', function($scope) {
$scope.input = {
sortBy: 'default-value-goes-here'
};
});
See this plunker: http://plnkr.co/edit/MmjuaLfvVnOQb0Ngz0dI?p=preview
Related
I'm working with AngularJS and I have a form with checkboxes that I generate with ng-repeat. I want to generate the id of each checkbox dynamically, I tried to do it like this:
<label ng-repeat="x in placeTypes">
<input type="checkbox" id='{{x.value}}'>
{{x.display}}
<br>
</label>
But when I try to get the element by Id the element is null.
You can do this:
<label ng-repeat="x in placeTypes track by $index">
<input type="checkbox" id="checkbox{{$index}}">
{{x.display}}
<br>
</label>
This should suffice:
<label ng-repeat="x in placeTypes">
<input type="checkbox" id="{{$index}}">
{{x.display}}
<br>
</label>
You can read more about ngRepeat in the Angular docs: https://docs.angularjs.org/api/ng/directive/ngRepeat
Here is a working example, https://plnkr.co/edit/mMzNGsEajOxY7TyiHArL?p=preview
angular.module('app', [])
.component('app', {
template: `<label ng-repeat="x in $ctrl.placeTypes">
<input type="checkbox" id='input-{{x.value}}' component-did-mount>
{{x.display}}
<br>
</label>`,
controller: function($scope) {
this.$onInit = () => {
this.placeTypes = [{
value: '1',
display: 'hello'
}, {
value: '2',
display: 'hello world'
}]
$scope.$on('componentDidMount', () => {
const $el = document.getElementById('input-1');
console.log('$el >>', $el);
})
}
}
}).directive('componentDidMount', ['$timeout', ($timeout) => {
var def = {
restrict: 'A',
transclude: false,
link: (scope, element, attrs) => {
$timeout(() => {
scope.$emit('componentDidMount')
});
}
};
return def;
}]);
angular.element(document).ready(() => {
const bootstrapAngular = () => angular.bootstrap(document.body, ['app'], {});
bootstrapAngular();
});
I'm using BootGrid Data table and JSON file as my data source for the table.
Service
.service('datatableService', ['$resource', function($resource){
this.getDatatable = function(id, email, date) {
var datatableList = $resource("data/data-table.json");
return datatableList.get ({
id: id,
email: email,
date: date
})
}
}])
Controller
.controller('datatableCtrl', function($scope, datatableService){
//Get Data Table Data
$scope.id = datatableService.id;
$scope.email = datatableService.email;
$scope.date = datatableService.date;
$scope.dtResult = datatableService.getDatatable($scope.id, $scope.email, $scope.date);
})
Directive
.directive('bootgrid', ['$timeout', function($timeout){
return {
restrict: 'A',
link: function(scope, element, attr){
$('#data-table-basic').bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
}
}
}])
HTML
<div class="table-responsive" data-ng-controller="datatableCtrl">
<table id="data-table-basic" class="table table-striped" data-bootgrid>
<thead>
<tr>
<th data-column-id="id" data-type="numeric">ID</th>
<th data-column-id="sender">Sender</th>
<th data-column-id="received" data-order="desc">Received</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="w in dtResult.list">
<td>{{ w.id }}</td>
<td>{{ w.email }}</td>
<td>{{ w.date }}</td>
</tr>
</tbody>
</table>
</div>
When I run this, I'm getting no data inside <tbody> but when I remove the directive, I can see all the JSON data are rendered inside the table. I want both ng-repeat and and directive works together. I tried to set the priority in directive as,
...
return {
restrict: 'A',
priority: 1001,
...
But no luck. http://plnkr.co/edit/rWCVXTjxOGZ49CeyIn9d?p=preview
Please help me fix this. I would appropriate if you could fix the above pen.
Regards
Priority setting will not help here because it is used to regulate order of compilation of directives defined on the same element.
You can delay bootgrid directive initialization until very next digest cycle using $timeout service. You will also need to watch for data object changes since you are loading it with AJAX.
app.directive('bootgrid', function($timeout) {
return {
link: function(scope, element, attr) {
scope.$watch(attr.bootgrid + '.length', function(newVal, oldVal) {
if (newVal !== oldVal && newVal) {
$timeout(function() {
element.bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
});
}
});
}
}
});
Demo: http://plnkr.co/edit/ehbzoFGOqhQedchKv0ls?p=preview
How can I fill an input text field with a dropdown-menu ?
Text Input:
<input type="text" ng-model="storagePlaceModel" lms-dropdown class="form-control lms-input-text-disable lms-dropdown-toggle" id="storagePlace" placeholder="Standort" autocomplete="off" readonly>
Own written dropdown:
<div class="lms-dropdown">
<div class="lms-dropdown-scrollbar">
<li ng-repeat="data in data_input_fields.input_cat_music_book_storage_place">
<span>{{data.input_value}}</span>
<i ng-show="storagePlaceModel == data.input_value" class="glyphicon glyphicon-ok"></i>
</li>
</div>
</div>
If I select an li element I want to update the storagePlaceModel with the li value.
Updated question:
Because I have more than one of these text inputs with dropdowns I need a solution where the conroller/directive does not know the model name exactly.
Directive could look like:
lmsApp.directive('updateInputField', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
$(elem).click(function(e) {
// Read out the model name of the html text input field
});
}
};
});
Thank you for your help! I would appreciate every answer.
I've edited the entire question to create a directive to wrap your desired structure. You'll pass to the directive the model you want, and that way, each model will be independent on different directive usages:
myApp.directive('myDirective', function() {
return {
restrict: "E",
scope: {
model: "=",
datas: "="
},
templateUrl: "directive.html",
link: function(scope, element, attr) {
scope.updateValue = function(val) {
scope.model.storagePlaceModel = val;
}
}
}
});
The directive.html contains your text input and the dropdown.
Controller:
function MyCtrl($scope) {
$scope.wrapper = {};
$scope.wrapper2 = {};
$scope.wrapper3 = {};
$scope.datas = [
{ "input_value" : "1" },
{ "input_value" : "2" },
{ "input_value" : "3" },
{ "input_value" : "4" }
];
}
HTML usage:
<div ng-app="myApp" ng-controller="MyCtrl">
<my-directive model="wrapper" datas="datas"></my-directive>
<my-directive model="wrapper2" datas="datas"></my-directive>
<my-directive model="wrapper3" datas="datas"></my-directive>
</div>
Working Fiddle
I am creating a big Angular.JS application which uses some third party modules like ui-select and ui-bootstrap.
To avoid repeating myself I started to create directives which are wrapping for example ui-select code and the logic to retrieve / search for data.
Goal:
The goal was to create a directive which can be used in the template like that, without duplicating code in controllers:
<tq-car-select ng-model="model.car"></tq-car-select>
What I try to avoid:
<select ng-options="car.id as car.name for car in cars"></select>
and duplicate following code in all controllers which are using the select:
$scope.cars = carResource.query();
$scope.$watch('model'), function (newValue, oldValue) {
$scope.cars = carResource.query({model: $scope.model});
});
I created directives for that kind of select fields.
Actual Example with ui-select:
tq-lead-select.html:
<ui-select ng-model="$parent.tqModel" style="display: block">
<ui-select-match placeholder="tippen ...">{{$select.selected.bezeichnung}}</ui-select-match>
<ui-select-choices repeat="lead in leads | filter:{bezeichnung: $select.search}">
<div ng-bind-html="lead.bezeichnung | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
tqLeadSelect.ts (TypeScript):
export function tqLeadSelect(tqLeadSelects): ng.IDirective {
var dir: ng.IDirective = {};
dir.scope = {
tqModel: '=',
tqCompany: '='
};
dir.restrict = 'E';
dir.templateUrl = '/js/templates/leadApp/tq-lead-select.html';
dir.replace = false;
dir.controller = function ($scope: any) {
if (tqLeadSelects != null && $scope.tqCompany != null) {
$scope.leads = tqLeadSelects.getLeadsFromFirma({ id: $scope.tqCompany });
}
$scope.$watch('tqCompany', (newValue, oldValue) => {
if (newValue === oldValue) return;
$scope.leads = tqLeadSelects.getLeadsFromFirma({ id: $scope.tqCompany });
}, true);
}
return dir;
}
tqLeadSelect.$inject = ['tqLeadSelects'];
Problems:
I need isolated scope, because some views use multiple instances of one field.
I am using the isolated scope variable tqModel which is set by the ngModel of the ui-select directive
I would like to use ng-required without creating a tq-required scope variable on tqLeadSelect directive
Questions:
Am I doing it right? Are there better ways achieving my goals?
how are you defining select fields with supporting controller code for retrieving data and additional functions?
One solution would be to add a directive which extends the existing directive.
I created a Plunker with an example: http://plnkr.co/edit/9IZ0aW?p=preview
Following Code:
HTML:
<ui-select ng-model="address.selected" theme="bootstrap" ng-disabled="disabled" reset-search-input="false" style="width: 300px;">
<ui-select-match placeholder="Enter an address...">{{$select.selected.formatted_address}}</ui-select-match>
<ui-select-choices repeat="address in addresses track by $index" refresh="refreshAddresses($select.search)" refresh-delay="0">
<div ng-bind-html="address.formatted_address | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
Controller:
$scope.address = {};
$scope.refreshAddresses = function(address) {
var params = {
address: address,
sensor: false
};
return $http.get(
'http://maps.googleapis.com/maps/api/geocode/json', {
params: params
}
).then(function(response) {
$scope.addresses = response.data.results
});
};
can be simplified by using a configuration directive:
<ui-select ng-model="adress.selected" tq-select></ui-select>
Controller is now empty!
Directive:
app.directive("tqSelect", function($http) {
return {
restrict: "A", // Attribute
require: ["uiSelect", "ngModel"],
compile: function compile(tElement, tAttrs, transclude) {
// Add the inner content to the element
tElement.append('<ui-select-match placeholder="Enter an address...">{{$select.selected.formatted_address}}</ui-select-match>\
<ui-select-choices repeat="address in addresses track by $index" refresh="refreshAddresses($select.search)" refresh-delay="0">\
<div ng-bind-html="address.formatted_address | highlight: $select.search"></div>\
</ui-select-choices>');
return {
pre: function preLink(scope, iElement, iAttrs, controller) {},
post: function postLink(scope, iElement, iAttrs, controller) {
// logic from controller
scope.address = {};
scope.refreshAddresses = function(address) {
var params = {
address: address,
sensor: false
};
return $http.get(
'http://maps.googleapis.com/maps/api/geocode/json', {
params: params
}
).then(function(response) {
scope.addresses = response.data.results
});
};
}
}
}
}
});
The directive is the actual tricky part. I am using a not trivial logic in the compile function. First I add the required markup for the ui-select directive.
Then in the post-link function I added the logic which is normally in the controller (or in the link()-function).
I would like to create a directive with a dropdown template. I try to give the value for ng-options as a parameter of the directive but the dropdown only contains the default value.
Here is a Fiddle and my code in this post :
http://jsfiddle.net/wXV6Z/6/
var app = angular.module('app', []);
function Ctrl($scope){
$scope.countries = [
algeria={name:'ALGERIA', phoneCode:'213'},
andorra={name:'ANDORRA', phoneCode:'376'},
angola={name:'ANGOLA', phoneCode:'244'}
];
}
app.directive('skiTest', function() {
return {
'replace': true,
'restrict': 'E',
'scope': {
'data': '=',
},
link: function (scope, element, attrs) {
scope.options = attrs.selectOptions;
},
'template':'<div><select name="testSelect" ng-model="ngModel" ng-options="
{{options}} in data"><option value="">Code</option></select></div>'
}
});
Here is the HTML :
<div ng-controller="Ctrl">
<div>
<h2>Without directive</h2>
<select ng-model="code" ng-options="country.phoneCode as country.name for country in countries">
<option value="">Code</option>
</select>
</div>
<div>
<h2>With directives</h2>
<ski-test data="countries" select-options="country.phoneCode as country.name for country"></ski-test>
</div>
</div>
Thank you very much if somebody can point my mistake !
The template in the directive just needs to be this:
'template':'<div><select name="testSelect" ng-model="data" ng-options="c.name for c in data"><option value="">Code</option></select></div>'
I forked your fiddle with the change: http://jsfiddle.net/W5Ex3/