Angularjs Transclude element with ng-options - angularjs

I got a directive with transclude: element and i want to apply a template to a select element with ng-options, to wrap the select with the given template.
app.directive("ngSelect", function(){
return {
restrict: "A",
replace:true,
transclude: 'element',
templateUrl: function(elem,attrs) {
return 'select.html';
},
...
compile: function compile(cElement, cAttrs, cTransclude) {
return{
pre: function preLink(scope, aElement, aAttrs, aController){
},
post: function postLink(scope, aElement, aAttrs, aController){
}
}
Usage:
<select ng-select ng-label="Select 2" ng-model="data.select2" required ng-options="item.id as item.value for item in list"></select>
But for some reason what's beeing wrapped are the options from the ng-options and not the select itself, as you can see in the final DOM here.
<div class="col-xs-12 col-sm-6 col-md-3 col-lg-2 ...">
<option value="?"selected="selected"></option>
<option value="number:1" label="value1">value 1</option>
<option value="number:2" label="value 2">value2</option>
<option value="number:3" label="value 3">value</option>
</div>
I think the select element is removed at some point of the transclusion, but i'm not realy sure. All i found about ngOptions with tranclude: element was a bit confusing.
How can i get this select to work properly with tranclude: element and ngOptions? Can i do something in the compile/pre/post link functions?
Plunker.

Related

AngularJS parent ng-model is not binding with custom directive when using ng-if in the template [duplicate]

This question already has answers here:
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
(3 answers)
Closed 4 years ago.
I am trying to create a custom directive to render dropdown(select).
app.directive("uiDropdown", function () {
return {
restrict: "E",
replace: true,
scope: {
'model': '=ngModel',
'readOnly':'=?'
},
templateUrl : 'template/dropdownTemplate.html',
link: function (scope, elem, attrs) {
}
};
});
the template is
<span ng-if="!readOnly">
<select ng-model="model" >
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
Html code to use the directive is
<ui-dropdown ng-model="region" read-only='readOnly'>
The plunker code is plunker
If I remove the code 'ng-if="!readOnly"' from the template file, it is working as expected. If I change from "ng-if" to "ng-show" it is working as well.
Am I missing something here? Actually, the directive is supposed to do much more functionality than the one shown in this example. I perfer to use ng-if instead of ng-show. Please help in resolving this issue.
It has to do with the fact that ng-if creates its own child scope and then you're using a primitive directly. ng-if will actually create a local model boolean that has no relation to the parent. This would be an issue with ng-if even if it weren't being used via a directive, too.
You can work around this by passing an object and reading/setting a value on that object. Here's a simple example showing that your ng-if issue is not related to the directive and then how you can fix this using an object:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.readOnly = false;
$scope.primitive = "1";
$scope.object = {
selectedValue: "1"
};
})
.directive('uiDropdown', function() {
return {
restrict: 'E',
templateUrl: 'dropdownTemplate.html',
scope: {
model: '=ngModel',
fieldName: '#',
readOnly: '=?'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div>
<label>Readonly: <input type="checkbox" ng-model="readOnly" /></label>
</div>
<div>
<h1>ng-if with primitive - no directive</h1>
<h2>This will not work</h2>
<div>
Value: {{ primitive }}
</div>
<div ng-if="!readOnly">
<select ng-model="primitive">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
</div>
<div>
<h1>ng-if with object - directive</h1>
<h2>This will work</h2>
<div>
Value: {{ object.selectedValue }}
</div>
<ui-dropdown ng-model="object" read-only="readOnly" field-name="selectedValue"></ui-dropdown>
</div>
<script type="text/ng-template" id="dropdownTemplate.html">
<div ng-if="!readOnly">
<select ng-model="model[fieldName]">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
</script>
</div>
ng-if contains a statement that's either true or false
Try
<ui-dropdown ng-model="region" read-only='true'>

How to add conditional add html attribute?

How to add conditionally attribute in angularjs?
For example I only want to set the multiple attribute on a <select> if my component has a binding set to true. This means if the binding is not given the multiple attribute should not exist on my <select>.
The only solution I found was with ng-if.
You can achieve this by implementing a directive (aka ng-multiple) to handle the property multiple of the select element. The following snippet implements this solution. However, you may want to control the state of your model inside this directive, once the multiple prop will produce an array of selected values, the non-multiple will produce a single object, so this may cause an issue when switching between multiple and non-multiple.
angular.module('myApp', [])
.directive('ngMultiple', function () {
return {
require: ['select', '?ngModel'],
restrict: 'A',
scope: {
multiple: '=ngMultiple'
},
link: function (scope, element, attrs, ctrls) {
element.prop('multiple', scope.multiple);
scope.$watch('multiple', function (multiple) {
if(element.prop('multiple') != multiple){
// may be would be convenient change the ngModel
// to [] or {} depending on the scenario
element.prop('multiple', multiple);
}
});
}
};
})
.controller('myController', function ($scope) {
$scope.condition = true;
$scope.myOptions = [];
});
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-controller="myController">
<label>
<input type="checkbox" ng-model="condition" /> multiple?
</label>
<br>
<select ng-multiple="condition" ng-model="myOptions">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</select>
<br>
<tt>myOptions: {{ myOptions }}</tt>
</div>
if boolean condition is true then multiple, else not
<select ng-if="condition" ng-model="some.model" multiple></select>
<select ng-if="!condition" ng-model="some.model"></select>
<select ng-show="condition" ng-model="some.model" multiple></select>
<select ng-hide="condition" ng-model="some.model"></select>
controller,
$scope.condition = true

Reusing a directive template for multiple forms with isolated scope

I'm working on a project where the user needs to be able to create many instances of the same form. As of now the user can click a button to create one or more forms. The problem I'm having is that by isolating the scope, as I think I should be doing given that I'm reusing the same directive, my ng-models can't communicate with the parent controller.
My directive for <rule-form></rule-form>..
(function(){
'use strict';
var ruleForm = function(){
return{
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'edit/rule-create/ruleForm.html',
link: function(scope, element, attrs){
scope.length = document.forms.length;
}
}
}
angular.module('ganeshaApp')
.directive('ruleForm', ruleForm)
})();
And my template...
<form class="edit__div--rule-form" name="form_{{length}}">
<input type="text" placeholder="Rule Title" ng-model="rcCtrl.ruleTitle">
<div class="edit__div--rc-toolbar">
<select class="edit__btn--rc-select" ng-model="rcCtrl.select" apply-statement-type>
<option value="obligation statement">obligation statement</option>
<option value="prohibition statement">prohibition statement</option>
<option value="permission statement">restricted permission statement</option>
</select>
<div class="edit__btn--rc-noun">
Add noun/verb
</div>
<div class="edit__btn--rc-save" ng-click="rcCtrl.saveRule()">
<span class="glyphicon glyphicon-floppy-saved"></span>Save
</div>
<div class="edit__btn--rc-cancel">
<span class="glyphicon glyphicon-remove"></span>
Cancel
</div>
</div>
<div class="edit__select--statement-type"></div>
<div ng-show="rcCtrl.showTextEdit" class="edit__div--rule-form-text" contenteditable="true" ng-model="rcCtrl.ruleText"></div>
I tried using $parent , (e.g. $parent.rcCtrl.ruleText), but then I'm back to the problem of not having isolated scopes and each form updates the others. I'm a bit confused about this really. Does anyone know a solution to this problem, or is it just a problem with my code?
Add a controller to your directive.
angular.module('ganeshaApp').directive('ruleForm', function(){
return {
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'edit/rule-create/ruleForm.html',
controller: "rulesFormController as rcCtrl",
link: function(scope, element, attrs){
scope.length = document.forms.length;
}
}
});
The AngularJS $compile service will then create an instance of the controller for each instance of the directive and attach it to each isolate scope.
For more information, see the AngularJS Comprehensive Directive API Reference.

Getting selected model in angular select directive

I've created a directive to display dropdowns (form selects).
However I cannot find a way to mark the selected option.
html form
<div content-selects ng-model="ctrl.contentSelects.riskStatus" selection="oneWMS.riskStatusId"></div> <!-- oneWMS.riskStatusId -->
directive
function contentSelects(){
return {
restrict: 'AE',
templateUrl: '/app/Directives/contentSelects.tpl.html',
replace:true,
scope: {
ngModel: '=',
selection: '='
},
controller:function($scope){
},
link: function (scope, element, attrs) {
scope.selectedModel = scope.ngModel[attrs.selection];
scope.isChanged = function () {
//console.log("changed");
}
element.removeAttr('id');
}
};
}// end function contentSelects
This is where I don't understand: the directive template
<div class="input-group">
<select id="{{id}}">
<option value="model.refId" ng-repeat="model in ngModel track by model.refId" ng-model="ngModel[selection]" >{{model.value}} *** {{selection}} *** {{ngModel[selection]}}</option>
</select>
</div>
In the actual value, {{ngModel[selection]}} gives me exactly what I want (the target model row), but when tied to ng-model it doesn't retrieve anything :/
ng-model="ngModel[selection]"
What is wrong with it? Using curly brackets break it of course...
Your problem is because of ngModel included in option element. You should move it to select element.
Demo
<div class="input-group">
<select ng-model="selectedModel" >
<option ng-value="model" ng-repeat="model in ngModel">{{model}}
</option>
</select>
</div>
And also look at https://docs.angularjs.org/api/ng/directive/ngOptions

Angular directive ng-repeat to generate pair of elements input+span

Here is my request, using directive I try to generate this structure above :
<div class='myClass'>
<input id="id0" name="name0" type="radio" checked>
<label for="id0" ng-click="myAction('0')"> 0</label>
<input id="id1" name="name1" type="radio">
<label for="id1" ng-click="myAction('1')"> 1</label>
<input id="id2" name="name2" type="radio">
<label for="id2" ng-click="myAction('2')"> 2</label>
...3,4,5...
span(class="endSpanClass")
Not very trivial:
I have tried using directive in many ways, ng-repeat but no success to get this pair of input/label together, I mean input+label in same order as in this example.
Then on first element, I expect to get "checked" attribute.
The last span has to be set at the end also.
That kind of try
.directive('myDirective', ['$timeout', function(timeout) {
return {
restrict: 'A',
controller: 'MyController',
scope: {myList: '='},
template: '<input id="id0" name="view" type="radio">' +
'<label for="id0" ng-click="myAction(\'day\'> 0 </label>',
transclude: false,
replace: true,
link: function($scope, element, attr, ctrl) {
}
};
}])
And my directive call
div(class='myClass')
input(my-directive, my-list='list', ng-repeat="item in list", id="0", name="name0", type="radio")
Gives nothing,
If you can help and gives me some advices, thanks.
J.
Following is what I would do:
<label ng-repeat="name in names">
<input name="{{name}}" type="radio"
ng-checked="$index==0"
ng-click="myAction($index)"/>
{{$index}}
</label>
wrapping form control by label which would simplify the code
also make very explicit what label control what control.
(just a standard html technique.)
use ng-checked to control checked state.
I would avoid using directive for this short code unless
it is used many times in many places.
if you use directive, template has to have single root element.
(so you can actually resort to technique [1] above.)
Here is a simple directive definition:
app.directive('myDirective', ['$timeout', function(timeout) {
return {
restrict: 'E',
scope: {id: '='},
template: '<div><input id="id{{id}}" name="view" type="radio">' +
'<label for="id{{id}}" ng-click="myAction(\'{{id}}\')">{{id}}</label></div>',
replace: true
};
}])
Along with a working example: http://plnkr.co/edit/spQRs5xs43P1FOX7YQzH?p=preview

Resources