How-to build a directive to customize labelized radio buttons - javascript

Here is a custom radio buttons:
The angular HTML code:
<div ng-app="app">
<div ng-controller="ctrl" class="sm-box">
<p>Please select a mode:
<sm-radio model="mode" value="MODE1" label="One" ng-click='mode="MODE1"'></sm-radio>
<sm-radio model="mode" value="MODE2" label="Two"></sm-radio>
<sm-radio model="mode" value="MODE3" label="Three"></sm-radio></p>
<p>Selected mode is: {{mode}}</p>
</div>
</div>
The corresponding JavaScript:
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.mode = 'MODE1';
}]);
app.directive('smRadio', ['$compile', function($compile) {
return {
restrict: 'E',
template: function( tElt, tAttrs ) {
tElt.addClass( 'sm-radio' );
tElt.append(
'<input type="radio"' +
' name="' + tAttrs.model + '"' +
' ng-model="' + tAttrs.model + '"' +
' value="' + tAttrs.value + '"' +
' /><label>' + tAttrs.label + '</label>'
);
if( ! tElt.attr('ng-click')) {
// This is a first try
tElt.attr('ng-click', 'mode="' + tAttrs.value + '"' );
// this is a variant
tElt.ngClick = 'mode="' + tAttrs.value + '"';
// None has effect...
$compile(tElt); // compilation here had twice elements!!!
}
}
};
}]);
The problem is "One" is clickable and sets the model "mode" to "MODE1" but "Two" and "Three" are not, only the radio buttons may select "MODE2" or "MODE3".
As you can see in the HTML source, the first have "ng-click" attribute when the other have not. I'm trying to add "ng-click" attribute in my directive, by code but I'm unsuccessful.
How can I add "ng-click" attribute in a directive?
EDIT: Answers guide to not use ng-click at all, so title has been updated

It is considered best practice to pass values to directive from "outside world" by using isolated scope. As you can see, it makes it easier too:
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.mode = 'MODE1';
}]);
app.directive('smRadio', function($compile) {
return {
restrict: 'E',
scope: {
model: '=',
value: '#',
label: '#'
},
template: '' +
'<input type="radio" name="name" ng-model="model" value="value"' +
' ng-checked="model==value" ng-click="model=value">' +
'<label ng-click="model=value">{{label}}</label>',
compile: function(tElt) {
tElt.addClass( 'sm-radio' );
}
};
});
Here's working example: http://jsbin.com/fejori/3/edit?js,output
(In case of this much code in 'template' attribute of a directive, I'd move it to outside html file and link it instead by using templateUrl attribute)

Here's doing it without ng-click. fiddle: http://jsfiddle.net/HB7LU/14244/
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.modes = [
{label: 'MODE 1', value: 1 },
{label: 'MODE 2', value: 2},
{label: 'MODE 3', value: 3}
];
$scope.selected = 2;
}]);
app.directive('smRadio', function() {
return {
restrict: 'E',
scope: {model: '=', selected: '='},
template: function( tElt, tAttrs ) {
return '<label ng-repeat="radio in model"><input type="radio" ng-value="radio.value" name="modes" ng-model="selected" ng-change="change(radio.value)">{{radio.label}}</label>';
},
link: function(scope, elem, attrs){
scope.change = function(val){
scope.selected = val;
}
}
};
});

Here is exactly what I want, largely inspired by tomek answer, simplified and the idea of label in place of div of Ervald answer.
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.xmode = 'MODE2';
$scope.ymode = 'MODE_B';
}]);
app.directive('smRadio', function() {
return {
restrict: 'E',
scope : {
model: '=',
value: '#',
label: '#'
},
template:
'<label class="sm-radio"' +
' ng-model="model">' +
'<input type="radio" name="{{model}}"' +
' ng-model="model"' +
' ng-value="value"' +
' ng-checked="model==value"> {{label}}</label>'
};
});
.sm-box {
background-color: lightgray;
border : solid 1px;
padding : 4px;
width : 500px;
}
.sm-radio {
background-color: lightblue;
color : #fff;
display : inline-block;
border : 1px solid darkgray;
border-radius : 8px;
padding : 8px;
}
.sm-radio>input {
width : 24px;
height : 24px;
vertical-align: text-bottom;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" class="sm-box">
<p>Please select a Xmode:
<sm-radio model="xmode" value="MODE1" label="One"></sm-radio>
<sm-radio model="xmode" value="MODE2" label="Two"></sm-radio>
<sm-radio model="xmode" value="MODE3" label="Three"></sm-radio></p>
<p>Selected mode is: {{xmode}}</p>
<p>Please select a Ymode:
<sm-radio model="ymode" value="MODE_A" label="A"></sm-radio>
<sm-radio model="ymode" value="MODE_B" label="B"></sm-radio>
<sm-radio model="ymode" value="MODE_C" label="C"></sm-radio></p>
<p>Selected mode is: {{ymode}}</p>
</div>
</div>

Related

how can get ng-model value within ng-repeat on directive?

I want's to get the value of ng-model within the the ng-repeat on my own directive and use the value of that ng-model in the directive for a fuction.
here is a plunker to see code in action :
https://plnkr.co/edit/MaI8DOtdc4yucXgNo05p?p=preview
here is my directive code :
(function() {
'use strict';
angular.module('barname.rtEditor', [])
.directive('rtEditor', rtEditor);
function rtEditor() {
return {
restrict: 'E',
replace: true,
templateUrl: 'rt-ht.html',
compile: compile
};
function compile() {
return {
post: function(scope, element, attrs) {
scope.com = {
inp: [{
name: 'video',
model: scope.vimodel,
command: 'insertHTML',
modelcommand: scope.vimodel
}, {
name: 'pic',
model: scope.pimodel,
command: 'insertImage',
modelcommand: scope.pimodel
}, {
name: 'link',
model: scope.linkmodel,
command: 'createLink',
modelcommand: scope.linkmodel
}]
};
scope.$watch('vimodel', function(newValue, oldValue) {
console.log('vim model watch :-> ', newValue);
});
scope.$watch('texmodel', function(newValue, oldValue) {
console.log('text model watch :-> ', newValue);
});
scope.execIn = function(cmd, val) {
console.log('cmd val :->', cmd + ' | ' + val);
scope.vimodel = '';
scope.texmodel = '';
};
}
};
}
}
})();
and here is the html code :
<div>
<div ng-repeat="inp in com.inp" id="{{inp.name}}">
<label>{{inp.name}} :</label>
<input type="text" ng-model="inp.model" />
<span ng-click="execIn(inp.command, inp.modelcommand)">doIt!</span>
</div>
<br>
<hr>
<br>
<div>
<label>widout ng repeat:</label>
<input type="text" ng-model="texmodel" />
<span ng-click="execIn('textIn', texmodel)">doIt!</span>
</div>
</div>
I can easily get the value of the ng-model without ng-repeat

Output function ('&') in AngularJS .component() not working

I am working with angular 1.5.6 component. I am trying to use the output binding ('&') but impossible to get it work. I have plunkered my issue.
Code for index.html :
<body ng-app="MyApp">
<my-view></my-view>
</body>
Code for the component for which I want to use output binding :
app.component('myInput', {
template: [
'<div class="form-group">',
'<label>{{$ctrl.label}}</label>',
'<input placeholder="{{$ctrl.placeholder}}" class="form-control" ng-model="$ctrl.fieldValue"/>',
'</div>'
].join(''),
controller: function() {},
bindings: {
label: '#',
placeholder: '#',
fieldValue: '=',
onUpdate: '&'
}
});
Code for the parent component (output binding is done with the attribute on-update):
app.component('myView', {
template: [
'<div class="container">',
' <h2>My form with component</h2>',
' <form role="form">',
' <my-input on-update="$ctrl.updateParam()" label="Firstname" placeholder="Enter first name" field-value=$ctrl.intermediaryData.firstName ></my-input>',
' </form>'
].join(''),
controller: function() {
var ctrl = this;
ctrl.userData = {
firstName: 'Xavier',
lastName: 'Dupont',
age: 25
};
ctrl.intermediaryData = {
firstName: ctrl.userData.firstName,
lastName: ctrl.userData.lastName,
age: 25
};
function updateParam(){
console.log("I have updated the component");
}
}
});
My bad, I have forgotten to put ng-change in input component. I managed to solve the issue like this :
app.component('myInput', {
template: [
'<div class="form-group">',
'<label>{{$ctrl.label}}</label>',
'<input ng-change="$ctrl.change()" placeholder="{{$ctrl.placeholder}}" class="form-control" ng-model="$ctrl.fieldValue"/>',
'</div>'
].join(''),
controller: function() {
var ctrl = this;
ctrl.change = function(){
ctrl.onUpdate();
}
},
bindings: {
label: '#',
placeholder: '#',
parentParam: '#',
fieldValue: '=',
onUpdate: '&'
}
});

Recursion in Angular [duplicate]

This question already has answers here:
Recursion in Angular directives
(9 answers)
Closed 7 years ago.
Edit: Just came across this SO post. It best answers my questions.
How would you implement the following recursively/better:
HTML
<div ng-controller='MainController as vm'>
<div ng-repeat='tango in vm.tangos'>
<p>{{ tango.text }}</p>
<div ng-repeat='subtango in tango.children'>
<p>{{ subtango.text }}</p>
<div ng-repeat='subsubtango in subtango.children'>
<p>{{ subsubtango.text }}</p>
<!-- ... -->
</div>
</div>
</div>
</div>
JS
angular
.module('app', [])
.controller('MainController', MainController)
;
function MainController() {
var vm = this;
vm.foo = 'bar';
vm.tangos = [{
text: '1',
children: [
{
text: '1a',
children: []
},
{
text: '1b',
children: [
{
text: '1bi',
children: []
}
]
}
]
}];
}
Using a directive doesn't seem to work (plnkr):
HTML
<div ng-controller='MainController as vm'>
<div ng-repeat='tango in vm.tangos'>
<tango tango='tango'></tango>
</div>
</div>
tango.html
<p>{{ tango.text }}</p>
<div ng-repeat='tango in tango.children'>
<tango tango='tango'></tango>
</div>
directive
function tango() {
return {
restrict: 'E',
scope: {
tango: '&'
},
templateUrl: './tango.html'
};
}
I assume that it's instantiating(probably not the right word) the directive even when tango.children is empty. If so, I'm not sure how to prevent that.
Check this demo: Plunker.
Define a template and use $compile to do the trick:
.directive('item', ['$compile', function( $compile ) {
var itemTemplate =
'<div>{{ text }} # of children: {{children.length}}</p>' +
'<div ng-show="children.length > 0">' +
'<div ng-repeat="i in children">' +
'<div item children="i.children" text="{{i.text}}">{{ i | json }}</div>' +
'</div>' +
'</div>' +
'</div>';
return {
restrict: 'AE',
scope: {
children: '=',
text: '#'
},
// template: itemTemplate
link: function (scope, element) {
element.html('').append( $compile( itemTemplate )( scope ) );
}
}
}]);

AngularJS - Fill input text field with dropdown menu

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

AngularJS Change a single scope item

A little difficult to explain,
we have worked through this sample
https://egghead.io/lessons/angularjs-understanding-isolate-scope
but its not quite matching what we are trying to achieve.
we have built a controller which simply sets myData
ClinicalNotesCtrl.controller('notesController', function notesController($scope, $http, $modal, $log) {
$scope.myData = { name: "Moroni", age: 50, result: "Sodium" };
$scope.changename = function (h) {
h.name = "Simon";
h.age = "34";
h.result = "This is a test";
}
});
Below is the directive called kid. This just simply prints out the value
Results.directive("kid", function () {
return {
restrict: "E",
// scope: { myValue: '=simon' },
// scope:{},
template: '<input type="text" ng-model="myData.name">{{myData.name}}' // '<div>{{$scope.timelineactivitydata}}</div>',
};
});
and finally this is the HTML page,
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
What we are trying to achieve is to somehow relate a particular label to a kid directive, so that when clicking on the label, only the related kid will change its name, rather than all of them.
Hope that makes sense,
As requested in the comments,
please see the plunker :-
plnkr.co/edit/3NMBNTrLT29EIFNo9lbA?p=preview
I am posting a solution here but I am not sure if it solves anything for you.
JS code
var myApp = angular.module('myApp', []);
myApp.controller('notesController', function notesController($scope, $http) {
$scope.myData = [{ name: "Moroni", age: 50, result: "Sodium" },
{ name: "Naomi", age: 50, result: "Sodium" },
{ name: "Rambo", age: 50, result: "Sodium" }
];
$scope.changename = function (h) {
h.name = "Simon";
h.age = "34";
h.result = "This is a test";
};
})
.directive("kid", function () {
return {
restrict: "E",
scope:{
myData:"=simon",
changed:"&change"
},
template: '<input type="text" ng-model="myData.name"/><span>'
+'{{myData.name}}</span><label '
+'ng-click="changed({\'data\':myData});">'
+'Change Name</label><br/>'
};
});
HTML
<div ng-app="myApp" >
<div ng-controller="notesController" >
<kid ng-repeat="data in myData" simon="data" change="changename(data)">
</kid>
</div>
</div>

Categories