ng-model with isolated scope directive - angularjs

I have an isolated directive as an element
How can I bind the ngmodel of it since the return html is being overrided ?
<div ng-repeat="x in list">
<form-element ng-model="value[x.name]"></form-element>
</div>
I am having troubles adding the ngmodel to it
JS :
app.directive('formElement', function($compile) {
return {
restrict: 'E',
scope : {
type : '='
} ,
link : function(scope, element, attrs) {
scope.$watch(function () {
return scope.type ;
}, function() {
var templates = {};
templates['text'] = '<input type ="text" name="{{name}}">' ;
templates['radio'] = '<input ng-repeat="option in optionsList" name="{{name}}" type="radio">';
if (templates[inputType]) {
scope.optionsList = scope.type.data;
scope.name = scope.type.name;
element.html(templates[scope.type.inputType]);
} else {
element.html("");
}
$compile(element.contents())(scope);
}
);
}
}
});
Thanks in advance

As you want the ng-model to introduced inside the field created by the directive you could inject that ngModel inside isolated scope.
Markup
<div ng-repeat="x in list">
<form-element ng-model="x.value" type="x.inputType"></form-element>
</div>
Directive
app.directive('formElement', function($compile) {
return {
restrict: 'E',
scope: {
type: '=',
ngModel: '='
},
link: function(scope, element, attrs) {
var templates = {};
templates['text'] = '<input type ="text" name="{{name}}" ng-model="ngModel">';
templates['radio'] = '<input ng-repeat="option in optionsList" name="{{name}}" type="radio">';
if (templates[scope.type]) {
scope.optionsList = scope.type.data;
scope.name = scope.type.name;
element.append($compile(templates[scope.type])(scope));
} else {
element.html("");
}
//$compile(element.contents())(scope);
}
}
});
Demo Plunkr

What you are doing is to call a property of your isolated scope "ngModel", but you are not using the ngModelController. This doesn't mean that you are using the ngModel directive provided by Angular.
You can change the name of your ngModel property in any other name on your directive, and it should works fine.
Using ngModel it means that you add the require:'ngModel'" prop. in your directive object.
return {
restrict: 'E',
require:'ngModel',
scope: {
type: '='
}
and in the link function use the ngModelController to access to the $viewValue
function(scope, element, attrs, ngModelCtr)
{
var mymodel=ngModelCtr.$viewValue;
}
Here more info.
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

Related

AngularJS passing object into isolated scope

I am trying to create a reusable html element / angular directive that will be used inside of ng-repeat so I want to pass it the values it will display in the DOM.
Something worth noting, I don't care for the values to bind. They can be a one-time binding simply displaying their values the first time ng-repeat creates them.
For example here is my directive:
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
templateUrl: '../Pages/Views/Templates/newsListing.html',
scope: {},
link: function (scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});
My HTML template:
<div>
<span class="glyphicon glyphicon-list-alt logo-green"></span>
<label>{{DateValue}}</label>
<label>{{Category}}</label>
<label class="noBorder">{{Content}}</label>
What I want the ending product to be:
<news-Listing Date="{{someValue}}" Category="{{someValue}}" Content="{{someValue}}"></news-Listing>
I have never created a directive before and all the guides I am trying to follow don't explain the scope, and the way binding happens inside of a directive.
Try like this
var app = angular.module('anApp', []);
app.controller('aCtrl', function($scope) {
$scope.data = [{date:"2000-12-12",category:"sport",content:"this is a test"}]
});
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
template: '<div><label>{{date}}</label><p>{{category}}</p><p>{{content}}</p></div>',
scope: {
date:'#',
category:'#',
content:'#'
},
link: function (scope, elem, attrs) {
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="anApp" ng-controller="aCtrl">
<div ng-repeat="d in data">
<news-listing date="{{d.date}}" category="{{d.category}}" content="{{d.content}}" ></news-listing>
</div>
</div>
Here is a working example of what you want: https://jsfiddle.net/jonva/kuk3pbbz/
.directive('newsListing', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<div> < span class = "glyphicon glyphicon-list-alt logo-green" > < /span> < label > {{dateValue}} < /label> < label > {{category}} < /label> < label class = "noBorder" > {{content}} < /label>',
scope: {
dateValue: '#',
content: '#',
category: '#',
},
link: function(scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});

Directive that returns template needs scope variable

The template that loads content("html/script template") for each cell:
<td class="tableColumnsDocs" ng-repeat="attobj in columns track by $index" >
<div ng-init="values = dbo.get4(attobj.key); key = attobj.key; template = attobj.template || getAttributeTemplate(dbo.clazz + attobj.key);">
<div plain-template="template">
</div>
</div>
</td>
I need the directive to access the value "template" from its directive "plain-template"
Directive:
app.directive('plainTemplate', function($parse) {
return {
templateUrl: 'testTemplate',
function (scope, element, attrs) {
console.log($parse(attrs.plainTemplate));
}
};
});
You can use something like
app.directive('plainTemplate', function($parse) {
return {
templateUrl: 'testTemplate',
scope: {
plainTemplate:"="
},
link: function (scope, element, attrs) {
console.log(scope.plainTemplate));
}
};
});
and when you call it in html your variable should be like
<plain-template test-template="anything"> </plain-template>
this is isolated scope. You can see more at https://docs.angularjs.org/guide/directive

Two way data binding failing with ng-model

My two-way data binding using ng-model is not working.
AngularJS Docs
Relevant Question
Parent directive template:
<div class="form-group">
<label>Company Phone</label>
<input ng-model="formData.company_phone" type="phonenumber" class="form-control" placeholder="Company Phone">
</div>
And the child directive:
.directive('input', [function(){
return: {
restrict: 'E',
require: '?ngModel',//right now this is binding to this directive scope, not a parent one
link: function($scope, element, attr, ngModel){
if (attr.type !== 'phonenumber') {
return;
}
//some code to validate a phone number
$scope.$apply(function () {
//bind updated number, but I need this to reflect in the parent scope
$scope[attr.ngModel] = formattedNumber;
}
I used $parse to solve this issue:
.directive('input', ['$parse', function($parse){
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attr, ngModel){
var getter = $parse(attr.ngModel);
var setter = getter.assign;
if (attr.type !== 'phonenumber') {
return;
}
//code that validated a phone number
scope.$apply(function () {
setter(scope, formattedNumber);
});
}
}
}]);

AngularJS model not updating while typing

In the following AngularJS code, when you type stuff into the input field, I was expecting the div below the input to update with what is typed in, but it doesn't. Any reason why?:
html
<div ng-app="myApp">
<input type="text" ng-model="city" placeholder="Enter a city" />
<div ng-sparkline ng-model="city" ></div>
</div>
javascript
var app = angular.module('myApp', []);
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
template: '<div class="sparkline"><h4>Weather for {{ngModel}}</h4></div>'
}
});
http://jsfiddle.net/AndroidDev/vT6tQ/12/
Add ngModel to the scope as mentioned below -
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
scope: {
ngModel: '='
},
template: '<div class="sparkline"><h4>Weather for {{ngModel}}</h4></div>'
}
});
Updated Fiddle
It should be
template: '<div class="sparkline"><h4>Weather for {{city}}</h4></div>'
since you are binding the model to city
JSFiddle
The basic issue with this code is you aren't sharing "ngModel" with the directive (which creates a new scope). That said, this could be easier to read by using the attributes and link function. Making these changes I ended up with:
HTML
<div ng-sparkline="city" ></div>
Javascript
app.directive('ngSparkline', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var newElement = '<div class="sparkline"><h4>Weather for {{' + attrs.ngSparkline + '}}</h4></div>';
element.append(angular.element($compile(newElement)(scope)));
}
}
});
Using this pattern you can include any dynamic html or angular code you want in your directive and it will be compiled with the $compile service. That means you don't need to use the scope property - variables are inherited "automatically"!
Hope that helps!
See the fiddle: http://jsfiddle.net/8RVYD/1/
template: '<div class="sparkline"><h4>Weather for {{city}}</h4></div>'
the issue is that require option means that ngSparkline directive expects ngModel directive controller as its link function 4th parameter. your directive can be modified like this:
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
template: '<div class="sparkline"><h4>Weather for {{someModel}}</h4></div>',
link: function(scope, element, attrs, controller) {
controller.$render = function() {
scope.someModel = controller.$viewValue;
}
}
}
});
but this creates someModel variable in scope. that I think isn't necessary for this use case.
fiddle

AngularJS - Different template in directive

I have a form based on twitter bootstrap, each field have it's own configuration
// controller (the template shows this in ng-repeat
$scope.fields = [{name:"f1", label:"Field 1", with_button: false},
{name:"f2", label:"Field 2", with_button: true}]
I'm trying to make a "conditional directive" that customize the template according to "field.with_button"
// Without button
<div class="controls">
<input type="text" id="i_{{field.name}}">
</div>
// With button
<div class="controls">
<div class="input-append">
<input type="text" id="i_{{field.name}}">
<span class="add-on">bt</span>
</div>
</div>
I searched a lot and didn't find any solution, I tried to create only one div and put contents inside with a compiler function but it didn't parse, and if I call $apply it crashes.
How could I make this directive?
wrong My last try:
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls">{{innerContent}}</div>',
controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
$scope.$eval('$scope.innerContent = \'<input type="text" id="input_{{field.name}}" placeholder="{{field.name}}" class="input-xlarge">\'');
}]
};
});
//<ss-field field="{{field}}"></ss-field>
You can use the $http and $compile services to do what you're after.
http://plnkr.co/edit/Xt9khe?p=preview
This plnkr should demostrate what needs to be done, but basically:
Use $http to load the template depending on the condition.
Compile the loaded template against the current scope with $compile.
angular.module('mymodule',[]).directive('ssField', ['$http', '$compile', function($http, $compile) {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls"></div>',
link: function(scope, element, attrs) {
var template;
var withButtonTmpl = 'with_button.html';
var withoutButtonTmpl = 'without_button.html';
if (scope.field.with_button) {
$http.get(withButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
} else {
$http.get(withoutButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
}
}
};
}]);
You can change the directive to be more robust so the URLs aren't directly embedded in the directive for re-usability, etc., but the concept should be similar.
Just to further expand on Cuing Vo's answer here is something similar to what I use(without using external partials and additional $http calls):
http://jsfiddle.net/LvUdQ/
myApp.directive('myDirective',['$compile', function($compile) {
return {
restrict: 'E',
template: '<hr/>',
link: function (scope, element, attrs, ngModelCtrl) {
var template = {
'templ1':'<div>Template 1</div>',
'templ2':'<div>Template 2</div>',
'default':'<div>Template Default</div>'
};
var templateObj;
if(attrs.templateName){
templateObj = $compile(template[attrs.templateName])(scope);
}else{
templateObj = $compile(template['default'])(scope);
}
element.append(templateObj);
}
};
}]);
However Im not quite sure its by the bible from performance perspective.
In AngularJS, directly manipulate the DOM must only be a last resort solution. Here, you can simply use the ngSwitch directive :
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template:
'<div class="controls" data-ng-switch="field.with_button">' +
'<input type="text" id="i_{{field.name}}" data-ng-switch-when="false">' +
'<div class="input-append" data-ng-switch-default>' +
'<input type="text" id="i_{{field.name}}">' +
'<span class="add-on">bt</span>' +
'</div>' +
'</div>',
};
});

Resources