Angular transcluded directive - can't access a model thats inside of it - angularjs

This is my directive code:
'use strict';
demo.directive('myModal', function($parse) {
return {
restrict: 'A',
transclude: true,
scope: '#',
template: '<div ng-transclude><h4>Please enter value</h4></div>'
}
});
Usage is as follows:
<!-- myModal directive -->
<div my-modal>
<input type="text" ng-model="myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>
And my main controller, which wraps the whole application, includes this:
demo.controller('MainCtrl', function($scope) {
$scope.getMyTest = function(){
alert($scope.myTest);
}
});
Any ideas why can't I access myTest?
JsFiddle: http://jsfiddle.net/sZZEt/679/

You should use the dot-notation:
demo.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.getMyTest = function(){
alert($scope.data.myTest);
}
});
and
<div my-modal>
<input type="text" ng-model="data.myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>
JSFIDDLE
Transclusion creates a child scope, that's why you should use the dot-notation for ng-model.

try this. add directive to model element.
<div>
<input my-modal type="text" ng-model="myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>

Related

Plug a user directive to form.$valid

I have a simple directive:
myDirective.html:
<div class="my-directive" ng-class="{'on':status,'off':!status}" ng-click="onClick()">
click me
</div>
myDirective.js:
app.directive('myDirective', function () {
return {
restrict: 'E',
templateUrl: 'myDirective.html',
controller: ['$scope', function ($scope) {
$scope.onClick = function () {
$scope.status = !$scope.status;
}
}]
}
});
I have a ng-form and I'm using this directive as a field of the form:
<ng-form name="frm">
<div class="form-field">
<input type="text" required ng-model="v1" />
</div>
<div class="form-field">
<my-directive status="v2" />
</div>
<input type="button" ng-disabled="!frm.$valid" value="save" />
</ng-form>
Live example in: JsFiddle.
When I set some text in the input frm.$valid turn to true automatically.
I want to apply to my-directive the same effect. Lets say...
Consider my-directive as valid only when status is true.
And if my-directive is invalid the form should be invalid too.
Can I set this effect in my-directive? And how can I do it?

changes in directive scope variables don't update the parent scope variables. Angular Js 1.5

This is my controller
function commentController($stateParams, commentFactory, $rootScope){
const ctrl = this;
ctrl.comment = {};
ctrl.decision = {};
ctrl.$onInit = function(){
ctrl.decision.replyVisible = false;
}
export {commentController}
This is my component
import {commentController} from './comment.controller'
export const commentComponent = 'commentComponent'
export const commentComponentOptions = {
bindings: {
post: '<'
},
templateUrl: 'blog/src/app/components/post/comment/comment.html',
controller: ['$stateParams', 'commentFactory', '$rootScope', commentController],
controllerAs: 'cm'
}
This is my directive
function showReply(){
return {
restrict: 'A',
scope: {
collapsed: '<'
},
link: function(scope, element, attrs){
element.on('click', ()=>{
console.log(`Trying to hide ${scope.collapsed}`);
scope.collapsed = true
console.log(`Trying to hide ${scope.collapsed}`);
})
}
}
}
function hideReply(){
return {
restrict: 'A',
scope: {
collapsed: '<'
},
link: function(scope, element, attrs){
element.on('click', ()=>{
scope.collapsed = false;
console.log(`Trying to hide scope.replyVisible is ${scope.collapsed}`);
})
}
}
}
export {showReply, hideReply}
This is my html
<div class="comment_wrapper">
<div class="comment" ng-repeat="comment in cm.post.comments">
<h3>{{comment.author.name}}</h3>
<p ng-bind-html="comment.comment"></p>
<button show-reply collapsed="cm.decision.replyVisible">Reply</button>
<div class="reply_wrapper" ng-show="cm.decision.replyVisible">
<label for="name">Name</label>
<input type="text" id="name" ng-model="cm.comment.author.name">
<label for="email">Email</label>
<input type="text" id="email" ng-model="cm.comment.author.email">
<label for="comment">Comment</label>
<textarea name="" id="comment" cols="10" rows="5" ng-model="cm.comment.comment"></textarea>
<button ng-click="cm.reply(comment._id)">Submit</button> <button hide-reply collapsed="cm.decision.replyVisible">Cancel</button>
</div>
</div>
</div>
And this is my module
import angular from 'angular'
import {showReply, hideReply} from './comment.directive'
import {commentFactory, commentFactoryFunc} from './comment.factory'
import {commentComponent, commentComponentOptions} from './comment.component'
export const comment = angular.module('comment', [uiRouter])
.factory(commentFactory, ['$http', commentFactoryFunc])
.component(commentComponent, commentComponentOptions)
.directive('showReply', showReply)
.directive('hideReply', hideReply)
.name
Here's what I am trying to do, in my html, I want to show the reply form when someone clicks on the reply button. And since there are multiple comments for with each having a reply form, I can't use the regular ng-click.
The changes made to the scope.collapsed don't get reflected on cm.decision.replyVisible
How do I make this work? I have always been confused about accessing parent controller variables from a directive.
You should use comment.decision.replyVisible instead of cm.decision.replyVisible
HTML
<div class="comment_wrapper">
<div class="comment" ng-repeat="comment in cm.post.comments">
<h3>{{comment.author.name}}</h3>
<p ng-bind-html="comment.comment"></p>
<button show-reply
collapsed="comment.decision.replyVisible">Reply</button>
<div class="reply_wrapper" ng-show="comment.decision.replyVisible">
<label for="name">Name</label>
<input type="text" id="name" ng-model="cm.comment.author.name">
<label for="email">Email</label>
<input type="text" id="email" ng-model="cm.comment.author.email">
<label for="comment">Comment</label>
<textarea name="" id="comment" cols="10" rows="5" ng-model="cm.comment.comment"></textarea>
<button ng-click="cm.reply(comment._id)">Submit</button> <button hide-reply collapsed="cm.decision.replyVisible">Cancel</button>
</div>
</div>
</div>

ng-Template CSS not working

Using AngularJS ngDialog.
Expecting that the CSS in the document is applied to the ngDialog, but it hwas not applied.
Code:
<script type="text/ng-template" id="attach_account_template">
<div id="attach_account_div" ng-controller="Opportunity_Controller" style="width:250px">
<input type="text" class="form-control" id="account_name" style="width:200px;height:35px" />
<input id="btn_account" type="button" style="margin-left:10px;height:35px;width:50px" value="GO" />
</div>
</script>
$scope.attach_account = function () {
//attach_account_template
ngDialog.open({ template: 'attach_account_template', scope: $scope });
};

ng-class not working inside directive for form validation

I have a form and inside it a directive like this:
addEditUser.html:
<form role="form" class="form-horizontal" novalidate name="addEditUserForm" >
<div class="form-group labelInputContainer">
<label for="emailAddr" class="col-xs-1 col-sm-1 col-md-1 control-label">E-mail</label>
<div class="col-xs-8 col-sm-8 col-md-8">
<input type="text" ng-class="{'invalid': addEditUserForm.emailAddr.$error.required}" maxlength="100" class="form-control" id="emailAddr" name="emailAddr" placeholder="E-mail" ng-model="user.emailAddr" required />
</div>
</div>
<address default-addr="defaultAddr" />
<button type="submit" class="btn greenButton" style="width: 100%;" ng-click="saveUser();" data-ng-disabled="addEditUserForm.$invalid">SAVE</button>
</form>
addEditUserController.js
app.controller('addEditUserController', function ($scope, $routeParams, $location, $filter, reposSvc) {
$scope.defaultAddr = {};
var id = $routeParams.id;
if (id) {
$scope.user = reposSvc.user.get({ id: id },
function (data) {
$scope.defaultAddr = data.defaultAddr;
});
});
the directive HTML (address.html):
<div>
<div class="form-group labelInputContainer">
<label for="streetAddress" class="col-xs-2 col-sm-2 col-md-2 control-label">Street</label>
<div class="col-xs-3 col-sm-3 col-md-3">
<input type="text" ng-class="{'invalid': addEditUserForm.streetAddress.$error.required}" maxlength="100" class="form-control noLimitsTextBox" id="streetAddress" name="streetAddress" ng-model="defaultAddr.streetAddress" placeholder="Street" required />
</div>
<label for="no" class="col-xs-2 col-sm-2 col-md-2 control-label">No</label>
<div class="col-xs-3 col-sm-3 col-md-3">
<input type="text" maxlength="6" class="form-control" id="no" name="no" placeholder="No" />
</div>
</div>
</div>
address.js:
(function () {
'use strict';
app.directive('address', function () {
return {
restrict: 'E',
replace: true,
templateUrl: 'app/directives/address.html',
scope: {
defaultAddr: '='
},
controller: function ($scope, reposSvc, toastr) {
}
};
});
})();
The button is disabled until I fill in all the required fields (in this short example the email and the street). But only the email gets the class "invalid" applied (a background color). So mainly the validation works for both controls, but the ng-class only gets applied on the email field that is in the main html.
What am I doing wrong? how can I get the "invalid" class from ng-class applied on the directive controls too?
The reason is because your directive is isolated scoped (it does not automatically inherit from the parent), so it does not get the special form object added as property on the scope with the name of the property. So you would need to pass that as 2-way binding, or use $parent.addEditUserForm to access it which seems bad though (impact on reusability), or just use scope:true but it may not serve the purpose of your intention of using isolated scope. Validation works because your ng-model is on 2-way bound property ng-model="defaultAddr.streetAddress" which is still attached to the formcontroller instance.
So you could do:
return {
restrict: 'E',
replace: true,
templateUrl: 'app/directives/address.html',
scope: {
defaultAddr: '=',
form:'=' //<-- Add a binding
},
controller: function ($scope, reposSvc, toastr) {
}
};
and
<input type="text" ng-class="{'invalid': form.streetAddress.$error.required}" maxlength="100" class="form-control noLimitsTextBox" id="streetAddress" name="streetAddress" ng-model="defaultAddr.streetAddress" placeholder="Street" required />
and pass it in as:
<address default-addr="defaultAddr" form="addEditUserForm" />
Note: i have used the name as form in the 2-way binding so that it is not tightly coupled with a specific form name and is more reusable.
Or as other options instead of setting class using ng-class just use the ng-invalid, ng-invalid-required etc.. classes added on the element when it is invalid.

How to dynamically add ng-repeat to an element inside an angular directive?

I want to add 'ng-repeat="n in counter"' to the 'form' tag inside my directive. How do I do this?
I tried accessing the element via compile but tElement.find('form') does not work.
See : http://jsfiddle.net/fea40v2c/1/
I tried all these variations:
console.log(tElement.find('form')); // fails
console.log(tElement[0].querySelector('form')); // null
console.log(document.querySelector('form')); // fails
Do you really need the add button to be defined by the directive's user? Because if you don't you can do this.
<script id="repeatableForm.html" type="text/ng-template">
<input type="button" value="add" ng-click="add()">
<div ng-repeat="c in counter">
<div ng-transclude></div>
</div>
</script>
Update
After a little work I got something that allows the user to provide their own markup for the add button. It a bit more complicated and involves a nested directive. A few points that are good to know:
The repeatableForm directive has no isolated scope. It modifies the host scope by adding/overwriting the repeatableForm property. This means multiple such directives cannot execute in the same host scope.
The repeatableForm publishes its controller in its host scope as the repeatableForm property. This is better than publishing the controller's methods directly in the scope because it namespaces those methods and leaves the host scope cleaner.
The view
<repeatable-form>
<input type="button" value="add" ng-click="repeatableForm.add()"/>
<form action="">
First Name: <input name="fname" type="text" />
Last Name: <input name="lname" type="text" />
<input type="checkbox" name="food" value="Steak"/> Steak
<input type="checkbox" name="food" value="Egg"/> Egg
<input type="button" value="remove" ng-click="repeatableForm.remove($index)" />
</form>
</repeatable-form>
The directives
app.directive('repeatableForm', function () {
return {
templateUrl:'repeatableForm.html',
restrict: 'E',
transclude: true,
controller: function () {
var repeatableForm = this
repeatableForm.add = function () {
repeatableForm.forms.push(repeatableForm.forms.length + 1);
};
repeatableForm.remove = function (index) {
repeatableForm.forms.splice(index, 1);
};
repeatableForm.forms = [1, 2, 3];
},
controllerAs: 'repeatableForm',
};
});
app.directive('form', function () {
return {
templateUrl: 'repeatedForm.html',
restrict: 'E',
transclude: true,
};
})
The templates
<script id="repeatableForm.html" type="text/ng-template">
<div ng-transclude></div>
</script>
<script id="repeatedForm.html" type="text/ng-template">
<div ng-repeat="form in repeatableForm.forms"><div ng-transclude></div></div>
</script>
Check this demo.

Resources