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>
Related
everyone.
I have a trouble with angularjs. I created custom directive for input[type="text"] and passed into variable as model. But ng-change event called function with previous value of variable.
Example:
State: 0, Type 1, In function - 0.
State: 1, Type 548, In function - 1.
State:548, Type 3, In function 548.
My html:
<div ng-controller="simpleCTRL">
<mr-textfield is-required="true" value="val" title="Minutes" type="text" change="ChangeVal()"></mr-textfield>
<input type="text" ng-model="val" ng-change="ChangeVal()"/>
</div>
</div>
And js:
<!-- language: lang-js -->
angular.module('SimpleInterestApp', [])
.controller('simpleCTRL', function($scope) {
$scope.val = 0;
$scope.ChangeVal = function() {
console.log($scope.val);
};
})
.directive('mrTextfield', function () {
return {
restrict: 'E',
template: "<div class='textfield'><input type='{{::type}}' ng-required='isRequired' ng-model='value' ng-change='change()'><span class='bar'></span><label>{{::title}}</label></div>",
replace: true,
transclude: true,
scope: {
value:"=",
title:"#",
type:"#",
isRequired:"=",
change:"&"
}
};
});
Wrap console.log inside a $timeout.
$scope.ChangeVal = function() {
$timeout(function() {
console.log($scope.val);
})
};
See working plunker.
Brief intro to my problem
I have a directive that dynamically shows a list of checkboxes. It has a parameter called options that should be an array like the following, in order to show the list of checkboxes correctly. For example:
var options = [
{
id: 1,
label: 'option #1'
},
{
id: 2,
label: 'option #2'
},
{
id: 3,
label: 'option #3'
}
];
So, by passing this array to my directive, a group of three checkboxes would be shown.
Also, the directive requires ngModel that will have the result of checking/unchecking the checkboxes (this object is always passed initialized). For example:
var result = {
"1": true,
"2": true,
"3": false
};
This case means that the first and second checkboxes (options with id=1 and id=2) are checked and the third (option with id=3) is unchecked.
My directive
template.html
<div ng-repeat="option in options track by $index">
<div class="checkbox">
<label>
<input type="checkbox"
ng-model="result[option.id]">
{{ ::option.label }}
</label>
</div>
</div>
directive.js
angular
.module('myApp')
.directive('myDirective', myDirective);
function myDirective() {
var directive = {
templateUrl: 'template.html',
restrict: 'E',
require: 'ngModel',
scope: {
options: '='
},
link: linkFunc
};
return directive;
function linkFunc(scope, element, attrs, ngModel) {
scope.result;
ngModel.$render = setResult;
function setResult() {
scope.result = ngModel.$viewValue;
};
};
};
What I want to achieve
Wherever I use my directive, I want to be able to trigger a function whenever the ngModel changes. Of course, I would like to achieve this using ngChange. So far I have the following:
<my-directive
name="myName"
options="ctrlVM.options"
ng-model="ctrlVM.result"
ng-change="ctrlVM.selectionChanged()">
</my-directive>
but the .selectionChanged() function is not triggered whenever the model changes. Anyone has any idea why this is not working as I am expecting it to work?
First thing first, please try to provide jsfiddle, codepen etc code snippet link so that it will be easy for others to answer your question.
The problem in your case is that you are never updating the ctrlVM.result object as you are passing the object's reference and that reference never change even if you manually update the model by calling ngModel.$setViewValue().
To solve the problem, just update the model by manually calling ngModel.$setViewValue() and pass in the new Object so that the reference changes and that will trigger the ngChange directives logic.
I've added the logic to do that and it will successfully trigger the change. Look at the code below:
angular
.module('myApp', [])
.directive('myDirective', myDirective)
.controller('MyController', function($timeout) {
var vm = this;
vm.options = [{
id: 1,
label: 'option #1'
}, {
id: 2,
label: 'option #2'
}, {
id: 3,
label: 'option #3'
}];
vm.result = {
"1": true,
"2": true,
"3": false
};
vm.selectionChanged = function() {
vm.isChanged = true;
$timeout(function() {
vm.isChanged = false;
}, 500)
}
});
function myDirective() {
var directive = {
templateUrl: 'template.html',
restrict: 'E',
require: 'ngModel',
scope: {
options: '='
},
link: linkFunc
};
return directive;
function linkFunc(scope, element, attrs, ngModel) {
scope.result;
ngModel.$render = setResult;
function setResult() {
scope.result = ngModel.$viewValue;
};
scope.updateValue = function(val) {
ngModel.$setViewValue(Object.assign({}, val))
}
};
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="myApp">
<script type="text/ng-template" id="template.html">
<div ng-repeat="option in options track by $index">
<div class="checkbox">
<label>
<input type="checkbox"
ng-model="result[option.id]" ng-click="updateValue(result)">
{{ ::option.label }}
</label>
</div>
</div>
</script>
<div ng-controller="MyController as ctrlVM">
<my-directive name="myName" options="ctrlVM.options" ng-model="ctrlVM.result" ng-change="ctrlVM.selectionChanged()">
</my-directive>
<div> Data: {{ctrlVM.result}} </div>
<div> isChanged: {{ctrlVM.isChanged}} </div>
</div>
</div>
#Gaurav correctly identified the problem (ng-change is never called because the object reference does not change). Here is a simpler solution that doesn't require manually cloning into the controller's model:
Add a binding for the ng-change attribute:
scope: {
options: '=',
ngChange: '&' // Add this, creates binding to `ctrlVM.selectionChanged()`
}
Add an ng-change to your checkbox template:
<input type="checkbox"
ng-model="result[option.id]" ng-change="ngChange()">
Now, when any checkbox changes it will automatically call the outer ng-change function without the intermediate step of cloning into the model.
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
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: '&'
}
});
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