I'm trying create a directive to reuse in a form. But when I submit, the input (directive) is not there.
This is my tag:
<core-input type="text" icon="person" name="name" placeholder="Name" ng-model="mymodel.name"></core-input>
This is my directive js code:
angular.module('StarterApp').directive('coreInput', function($compile) {
return {
restrict: 'AE',
require: '?ngModel',
scope: true,
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
scope.onChange = function() {
ngModel.$setViewValue(scope.value);
};
ngModel.$render = function() {
scope.opts = attrs;
scope.value = ngModel.$modelValue;
$compile(element.contents())(scope);
};
},
templateUrl: function(elem, attr) {
return './app/views/directives/form/' + attr.type + '.html';
}
};
});
And this is what I want reuse:
<md-input-container class="md-icon-float md-block" flex-gt-sm md-no-float>
<md-icon class="material-icons">
<i class="material-icons">{{ opts.icon }}</i>
</md-icon>
<input type="text" name="{{ opts.name }}" placeholder="{{ opts.placeholder }}" ng-model="$parent.ngModel">
</md-input-container>
What am I missing here?
The angular documentation states that $setViewValue don't call the $render method on ngModel. So this have to be called manually. As the html of the element has changed shiould be possible to retrieve the original template from the angular service $templateCache
var urlPrefix = './app/views/directives/form/';
var templateExtension = '.html';
angular.module('StarterApp').directive('coreInput',['$compile', '$templateCache' function($compile, $templateCache) {
return {
restrict: 'AE',
require: '?ngModel',
scope: true,
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
scope.onChange = function() {
ngModel.$setViewValue(scope.value);
ngMode.$render();
};
ngModel.$render = function() {
var template = $templateCache.get(urlPrefix + attrs.type + templateExtension);
element.html(template);
$compile(element.contents())(scope);
};
},
templateUrl: function(elem, attr) {
return urlPrefix + attr.type + templateExtension;
}
};
}]);
Related
I have two different module in which I have two directives. I need to pass data between these two directive. I use require property. but I get some error
Error: [$compile:ctreq] Controller 'yearSort', required by directive 'budgetSort', can't be found!
My first directive is
angular.module('movieApp.yearsort.directives', []).directive('yearSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'YearsortController',
templateUrl : 'app/components/yearsort/yearsort.html',
};
}]);
In the YearsortController I have the code
angular.module('movieApp.yearsort.controller', []).controller('YearsortController', ['$scope','HomeFactory','$timeout','$state',function($scope,HomeFactory,$timeout,$state) {
this.sayHello = function() {
$scope.words = "my requier";
console.log( $scope.words);
};
}]);
In my second directive I have the code
angular.module('movieApp.budgetsort.directives', []).directive('budgetSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'BudgetsortController',
templateUrl : 'app/components/budgetSort/budgetSort.html',
require : "yearSort",
link : function(scope,element, attrs, demoCtrl){
demoCtrl.sayHello();
}
};
}]);
Why don't you try using a Service/Factory? It is a good option when you need to pass data through components or directives
I've made this plunkr to explain: http://plnkr.co/edit/V7BLbOrrtNhXl1QlUKxA?p=preview
HTML:
<body ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>
Javascript:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
The Demo
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>
I have this oversimplified directive for getting a phone number, and I want the ng-model to be invalid if the phone number is less than 10 digits. I was testing out $setValidity but it's not says that is not a function.
angular.module('valkyrie').directive('phonenumber', function(){
return{
scope: {phonemodel: '=model'},
template: '<input ng-model="inputValue" type="tel" class="form-control">',
link: function(scope, element, attributes){
scope.$watch('inputValue', function(value, oldValue) {
value = String(value);
var number = value.replace(/[^0-9]+/g, '');
scope.phonemodel = number;
scope.phonemodel.$setValidity('phone', false);
console.log(scope.phonemodel);
});
},
}
});
As Aluan Haddad mentioned in the comments, you need to require the ngModel directive within your directive. That will give you access to the ngModelController, and then you can just do ngModelController.$setValidity.
See a working demo here: http://codepen.io/miparnisari/pen/LxPoVw.
angular.module('valkyrie', [])
.controller('parent', function ($scope) {
$scope.phone = '';
})
.directive('phonenumber', function() {
return {
require: 'ngModel',
template: '<input ng-model="inputValue" type="tel" class="form-control">',
link: function(scope, element, attributes, ctrl) {
scope.$watch('inputValue', function(value, oldValue) {
if (!value) return;
scope.ngModel = value.replace(/[^0-9]+/g, '');
if (scope.ngModel.length < 10)
ctrl.$setValidity('phoneLength', false);
else
ctrl.$setValidity('phoneLength', true);
});
},
};
});
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
I have a directive for an upload file that communicates with the server and returns a scope.dbInsertId. I want to watch this and then update the ng-model with this value. I am unable to get it to work using $apply. Here is my code:
scope.$watch('dbInsertId', function(newValue, oldValue) {
if (newValue)
scope.$apply(function() {
scope.ngModel = scope.dbInsertId;
});
console.log("I see a data change!");
return ngModel.$modelValue;
}, true);
Is my code wrong?
My answer for your question
<div ng-app="app">
<div ng-controller="MyController">
<form name="someForm">
<div this-directive ng-model="theModel"></div>
<div>theModel='{{ theModel }}'</div>
</form>
</div>
</div>
var app = angular.module('app', []);
app.controller('MyController', function($scope,$rootScope,$log) {
$scope.theModel = '';
$scope.$watch('theModel',function(newVal,oldVal){
$log.info('in *MyController* model value changed',newVal,oldVal);
});
});
app.directive('thisDirective', function($compile, $timeout, $log) {
return {
scope: {
ngModel: '='
},
require: 'ngModel',
template: '<input type="text" ng-model="ngModel" /><div child-directive ng-model="ngModel"></div>',
link: function(scope, element, attrs, ngModel) {
}, // end link
} // end return
});
app.directive('childDirective', function($compile, $timeout, $log) {
return {
scope: {
ngModel: '='
},
template: '<input type="text" ng-model="ngModel" />',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
},
} // end return
});
jsfiddle.net/dXL4w/3
You need scope:
scope.$watch('dbInsertId', function(newValue, oldValue, scope) { // here
if (newValue)
scope.$apply(function() {
scope.ngModel = scope.dbInsertId;
});
console.log("I see a data change!");
return ngModel.$modelValue;
}, true);
I am trying to create 3 directives:
.directive('dirOne', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
controller: function ($scope, $element, $attrs) {
this.add = function (tag) {
tag && $scope.tags.push(tag);
};
},
template: '<div><p>Bucket from directive: {{tags}}</p><div ng-transclude></div></div>'
};
})
.directive('dirTwo', function () {
return {
restrict: 'A',
replace: true,
require: '^dirOne',
link: function (scope, element, attrs, dirOne) {
scope.add = function (tag) {
dirOne.add(tag);
};
},
template: '<div>'+
' <input type="text" ng-model="query" datum="sugestions" dir-three>' +
' <button ng-click="add(query)">add</button>' +
'</div>'
};
})
.directive('dirThree', ['$compile', function ($compile) {
var itemsTemplate = '<span class="sugestions"><span ng-repeat="item in datum|filter:query">{{item.name||item}}</span></span>';
return {
restrict: 'A',
transclude: true,
replace: true,
require: 'ngModel',
scope: {
datum: '=',
query: '=ngModel'
},
link: function (scope, element, attrs) {
var parent;
element.wrap('<span>');
parent = element.parent();
parent.append(angular.element($compile(itemsTemplate)(scope)));
}
};
}])
In the dirTwo and dirThree, I have an input <input type="text" ng-model="query" datum="sugestions" dir-three> with ngModel, this input needs to access and modify the content of ngModel, so that the scope is not isolated.
http://jsfiddle.net/joaoneto/hbABU/3/
Update
I Updated version, fix some mistakes I had committed, the content that was being transcluded in dirTwo, should not have the "ADD" function, and belongs to dirTree, hope it helps someone and apologize for peopl take to update this entry... see in http://jsfiddle.net/hbABU/4/