A bit confused with binding...
How to properly bind values from input fields to textarea?
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.fromjson = "{{hello}} {{world}} and have a good time";
//this template comes from json
});
And a simple body:
<body ng-controller="MainCtrl">
<input ng-model="hello">
<input ng-model="world">
<helloworld></helloworld>
</body>
A have to edit my miserable example because your
kindly answers didn't solve my problem.
I had plenty of unique texts - letter templates in which some fields should be filled by user. There are ten fields occuring conditionally depending of text selected.
text1: "Blah, blah {{field.first}}.blah {{filed.second}}"
text2: "{{field.third}} blah, blah {{field.first}}"
text3: "Blah, blah {{field.fourth}}"
and so on...
Texts are stored in database and obtained through JSON
function(textid) {
$http.get('json/json.php',
{ params: { id: textid } }).
success(function(data, status, headers, config) {
$scope.SelectedText = data;
})
};
I organized it in one form with all ten input fields, visible depending of
selected text.
Completed/filled template should be visible in textarea at the bottom of form to be copied to another place.
Should I change the way I store the templates?
or back to question is there any other way the fields could be inserted into view ?
I think what you need is $interpolate service and $scope.$watch take a look at this jsfiddle :
http://jsfiddle.net/michal_taborowski/6u45asg9/
app.controller('MainCtrl', function($scope,$interpolate) {
$scope.hello = 'Hello';
$scope.world = 'World!';
//this template comes from json
$scope.template = " {{hello}} {{world}} and have a good time";
//this template comes from json
var updateTemplate = function(oldVal,newVal,scope){
scope.fromjson = $interpolate(scope.template)(scope);
}
$scope.$watch('hello', updateTemplate );
$scope.$watch('world', updateTemplate );
});
Of course you should move $watch to link function in your directive and pass hello and world as scope variable to this directive - this is just a quick example how you can do it.
I think that what you want is this:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.fromjson = function(){
return $scope.hello + " " + $scope.world + " and have a good time";
};
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{fromjson()}}</textarea>'
};
});
Example here: http://plnkr.co/edit/8YrIjeyt9Xdj2Cf7Izr5?p=preview
The problem with your code is that when you declare $scope.fromjson = "{{hello}} {{world}} and have a good time" you are not binding anything, you are just assiging that string to the fromjson property.
EDIT:
As HeberLZ pointed out in the comment bellow, it would be much more efficient to do this instead:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{ hello + " " + world + " and have a good time"}}</textarea>'
};
});
One way would be something like this:
Controller:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
});
Directive:
app.directive('helloworld', function($http) {
return {
restrict: 'E',
scope: {
'hello': '=',
'world': '='
},
link: function(scope){
scope.jsonFromServer = '';
$http.get('someUrl').then(function(response){
scope.jsonFromServer = response.data;
});
var updateFromjson = function(){
scope.fromjson = scope.hello + ' ' + scope.world + ' ' + scope.jsonFromServer;
}
scope.$watch('hello', updateFromjson);
scope.$watch('world', updateFromjson);
}
template: '<textarea>{{fromjson}}</textarea>'
};
});
Body:
<body ng-controller="MainCtrl">
<input ng-model="hello">
<input ng-model="world">
<helloworld hello="hello" world="world"></helloworld>
</body>
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.aDiffFunc = function() {
return $scope.hello + " " + $scope.world + " and have a good time";
};
//this template comes from json
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{aDiffFunc()}}</textarea>'
};
});
this should be it??
http://plnkr.co/edit/ygA4U0v7fnuIbqAilrP7?p=preview
Related
I'm currently trying to pass a validation directive to a custom element directive. But I'm struggling to make it work since it should receive model as an input while I am using bind to controller.
I have to premise that I cannot upgrade to a more recent version of Angular, so 1.5 is the limitation, together with the fact I cannot edit validation directive.
I thought transclude would have helped but with directive attribute it looks not so promising.
What the following code should do is to validate vm.model on input element.
Here's the HTML:
<body ng-controller="MainCtrl">
<div class="myClass">
<my-custom-directive data-placeholder="No text"
data-id="myModel.id"
data-model="myModel.text"
not-editable-directive-attribute >
</my-custom-directive>
</div>
</body>
And here the app.js:
var myTemplate = '<div class="myContainer">' +
'<input class="myInput"' +
' ng-mousedown="$event.stopPropagation();"' +
' ng-show="vm.isFocused"' +
' ng-model="vm.model"' +
' ng-change="vm.onChange()"' +
' type="text">' +
'<span ng-show="!vm.isFocused">{{vm.model}}</span>' +
'<span ng-show="!vm.isFocused && !vm.model && vm.placeholder">{{vm.placeholder}}</span>' +
'</div>';
app.controller('MainCtrl', function($scope) {
$scope.myModel = {
id: 'test',
text: 'this is text'
};
});
app.directive('myCustomDirective', ['$timeout', function($timeout) {
return {
restrict: 'E',
replace: true,
template: myTemplate,
controllerAs: 'vm',
bindToController: {
id: '#',
model: '=',
onChange: '&',
placeholder: '#'
},
scope: {},
controller: angular.noop,
link: function(scope, element) {
var input = element.find('input')[0];
var spans = Array.from(element.find('span'));
var vm = scope.vm;
vm.isFocused = false;
vm.focus = function() {
vm.isFocused = true;
scope.$applyAsync(function() {
$timeout(function() {
input.focus();
input.select();
});
});
};
spans.forEach(span => span.addEventListener('click', vm.focus));
}
};
}]);
app.directive('notEditableDirectiveAttribute', [function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.myCustomDirectiveAttribute = function(modelValue, viewValue) {
if (viewValue) {
return viewValue.indexOf('e') < 0;
}
return false;
};
}
};
}]);
I've created a plunker to make it clearer:
http://plnkr.co/edit/auminr?p=preview
So clicking on span element i should be able to edit text and directive should validate it (in this specific case check if it contains letter "e").
Is it even possible or am I struggling against windmills?
One approach to adding directives to templates based on component attributes is to use the function form of the template property:
<my-custom-directive data-placeholder="No text"
model="vm.data"
custom="not-editable-directive-attribute" >
</my-custom-directive>
app.directive("myCustomDirective", function() {
return {
template: createTemplate,
scope: {},
//...
});
function createTemplate(tElem, tAttrs) {
var placeholder = tAttrs.placeholder;
var model = tAttrs.model;
var custom = tAttrs.custom;
return `
<input placeholder=${placeholder}
ng-model=${model}
${custom} />
`;
}
})
The createTemplate function copies attributes and used them in a template literal.
For more information, see
AngularJS Comprehensive Directive API - template
MDN JavaScript Reference - Template Literals
This is a simplified version of a real problem but I want to understand what is happening and how to fix it. I have a directive that should load different templates deppending on a parameter that is being bind to it.
var app = angular.module('my_app', [])
.controller('controller', Controller)
.directive('dirTwo', DirTwo);
function Controller() {
var este = this;
this.list = ["number", "text", "text"];
this.change = function() {
this.list = this.list.reverse();
}
}
function DirTwo() {
return {
restrict: 'E',
//template: ' Name: {{name}}{{type}}',
template: my_template,
scope: {
type: "="
},
link: function($scope) {
$scope.name = "Pepito";
console.log("scope type: " + $scope.type);
}
}
}
var my_template = function(elem, attrs, $scope) {
console.log("template type: " + attrs.type);
switch (attrs.type) {
case 'number':
return '{{type}}';
break;
default:
return ' ---- '
break;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div class="app" ng-app="my_app" ng-controller="controller as App">
<p>{{App.list}}
<button ng-click="App.change()">Reverse</button>
</p>
<dir-two ng-repeat="item in App.list track by $index" type="item"></dir-two>
</div>
The log in the template function prints the word item instead of number or text. How can I fix it to load the template correctly?
Unfortunately there is no way to use template like that because the template resolution happens before the compile phase where your type would assume a value on scope. To change the template of a directive based on a scope value, you must watch that value and recompile the directive element when it gets changed. It will recompile the directive entirely with a new template but with the same scope. You could do this once (i.e., without the $watch) as well.
The following snippet implements this solution.
var app = angular.module('my_app', [])
.controller('controller', Controller)
.directive('dirTwo', DirTwo);
function Controller() {
var este = this;
this.list = ["number", "text", "text"];
this.change = function() {
this.list = this.list.reverse();
}
}
function DirTwo($compile) {
return {
restrict: 'E',
template: '',
scope: {
type: "="
},
link: function(scope, element) {
var tmplScope;
scope.name = "Pepito";
scope.$watch('type', function(type) {
if (tmplScope) tmplScope.$destroy();
tmplScope = scope.$new();
element.html(getTemplate(type));
$compile(element.contents())(tmplScope);
});
}
}
}
var getTemplate = function(type) {
switch (type) {
case 'number':
return '{{type}}';
break;
default:
return ' ---- '
break;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<div class="app" ng-app="my_app" ng-controller="controller as App">
<p>{{App.list}}
<button ng-click="App.change()">Reverse</button>
</p>
<dir-two ng-repeat="item in App.list track by $index" type="item"></dir-two>
</div>
UPDATE NOTE: I've changed the actual compile system to create a new child scope every time it gets compiled and also destroy this child scope when it gets recompiled to prevent adding new watchers when recompiling it without discarding the previous ones. Thanks to #georgeawg who warnned about this issue in the comments.
i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;
i am having a problem with ng-model .When i am adding tag from suggestion list its not updating model value until i am not deleting tags and adding again.In my project its working fine but for plunker only its happening.Please check this out and help me..
thank you..
Here is my html:-
<tags-input ng-model="tags2" display-property="tagName" on-tag-added="getTags()" id="target">
<auto-complete source="loadTags($query)" min-length="2"></auto-complete>
</tags-input>
<p>{{tags2}}</p>
Here is my js:-
var app = angular.module('myApp', ['ngTagsInput', 'ui.bootstrap']);
app.controller(
'myController',
function($scope, $http) {
$scope.tagsValues =[];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
$scope.getTags = function() {
$scope.tagsValues = $scope.tags2.map(function(tag) {
return tag.tagId;
});
alert(" Tag id is :"+ $scope.tagsValues);
};
});
Here is my plunker:-
http://plnkr.co/edit/6Mr2qk2S2RvGJLevf2UI?p=preview
All you have to do to make this work, is define and initialize the variable $scope.tags2 first:
var app = angular.module('myApp', ['ngTagsInput', 'ui.bootstrap']);
app.controller(
'myController',
function($scope, $http) {
$scope.tagsValues = '';
$scope.tags2 = [];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
$scope.getTags = function() {
$scope.tagsValues = $scope.tags2.map(function(tag) {
return tag.tagId;
});
alert(" Tag id is :"+ $scope.tagsValues);
};
});
See my plunker: http://plnkr.co/edit/Y3f8kLBnexOXg5gwmldL?p=preview
I'm trying to make a custom directive to create a menu where the href value changes depending on the family.id value which I got via service.
I'm trying to use that family.id value of each element in ng-repeat to query a new value through a service and change the href depending on that value
product.html
<ul class="list" custom-href categories="getfamilies"></ul>
href.js
(function(){
var app = angular.module('hrefDirective',['productsService']);
app.directive('customHref',['productsService', function(productsService) {
return {
restrict: 'EA',
transclude: false,
scope: {
categories: '='
},
template: '<li ng-repeat="family in categories"><p><a ng-bind="family.name" ng-href="{{newHref}}"></a></p></li>',
link : function($scope,$element, $attrs){
productsService.finalFamilies(family.id).then(function(data){
if(data == 0){
$scope.newHref = '#/product/';
}else{
$scope.newHref = '#/product/{{family.id}}';
}
});
}
};
}]);
})();
products.js
(function(){
var app = angular.module('productosController',['productsService']);
app.controller('ProductsFamiliesController', function($scope, productosService){
productsService.getFamilies().then(function(data){
$scope.getfamilies = data.data;
});
});
})();
services.js
(function(){
var app = angular.module('productsService',[]);
app.factory('productsService', ['$http','$location', function($http, $location){
var webServiceUrl = 'my url'
var products = [];
products.getFamilies = function(){
return $http.get(webServiceUrl+'getFamilies');
};
products.finalFamilies = function(idFamily){
return $http.get(webServiceUrl+'getFinalFamiles?id'+ idFamily);
};
return products;
}]);
})();
Of course is not working...any clues...?
plnkr.co/edit/3N19E0KyeWW7wvsLOlkk?p=info