Okay, so here's my custom directive:
angular.module('bulwarkWebControls', [])
.directive('dropdownList', [
function() {
return {
scope: {
label: '#', // optional
changeCallback: '&',
options: '=',
required: "=",
disabled: "=",
id: "=",
name: "=",
model: "=",
value: "=",
display: "="
},
restrict: 'E',
replace: true, // optional
templateUrl: 'app/templates/dropDownList.html',
link: {
pre: function (scope, element, attr) {
scope.expression = "o."+value +" as o."+display+" for o in options";
}
}
};
}
]);
Template:
<div class="row form-group form-horizontal">
<label data-for="id" class="control-label col-md-3">{{label}}</label>
<div class="col-md-9">
<select data-id="id" data-name="name" data-ng-disabled="disabled" class="form-control"
data-ng-model="model"
data-ng-options="{{expression}}" data-ng-required="required">
<option></option>
</select>
</div>
Html code that utilizes the directive:
<div>
<dropdown-list data-label="Phone(s)" data-id="phoneList" data-name="phoneList"
data-disabled="vm.workOrder.Contacted"
data-model="vm.workOrder.PhoneNumber_Id" data-value="Id" data-display="Number"
data-options="vm.workOrder.CustomerPhoneNumbers" data-required="vm.workOrder.SpokeTo_Id!=5" />
</div>
What I am trying to do is basically create the expression for the ng-options dynamically at run-time based on the "display" and "value" parameters that I am passing to the directive. You can see that in the "pre:" linking function inside the directive. Now, I know that this is not working because at the time this function is being executed I do not have the scope values. However, I cannot do this in the post: linking because that would be too late for the ng-options expression. Is there a way to achieve this functionality or am I totally barking up the wrong tree here? Thank you so much in advance.
Never mind. I used the following code in the template:
<div class="row form-group form-horizontal">
<label data-for="id" class="control-label col-md-3">{{label}}</label>
<div class="col-md-9">
<select data-id="id" data-name="name" data-ng-disabled="disabled" class="form-control"
data-ng-model="model"
data-ng-options="o.{{value}} as o.{{display}} for o in options" data-ng-required="required" compile>
<option></option>
</select>
</div>
That, and I took whatever code I had originally put in the pre: linking function inside the directive.
Related
I am using the modal pop up where my form is present and I am unable to get the form data via ng-model using Angular.js. I am providing my code below.
<modal title="Owner Information" visible="showModal">
<form class="ng-pristine ng-valid" id="frmsignup" name="frmsignup" autocomplete="off">
<div class="input-group bmargindiv1 col-lg-4 col-md-4 col-sm-4 col-xs-12 plr-15">
<span class="input-group-addon ndrftextwidth text-left">Status:</span>
<select class="form-control" name="status" id="status" ng-model="status" required="required">
<option value="">Select Status</option>
<option value="1">Active</option>
<option value="0">Inactive</option>
</select>
</div>
<div class="input-group bmargindiv1 col-lg-4 col-md-4 col-sm-4 col-xs-12 plr-15">
<span class="input-group-addon ndrftextwidth text-left">Comment:</span>
<textarea rows="5" cols="50" class="form-control" id="comment" name="comment" ng-model="comment" required="required">
</textarea>
</div>
<input type="button" class="btn btn-success" ng-click="updateOwnerData();" id="addProfileData" value="Save" />
</form>
</modal>
My modal pop up is given below.
var dept=angular.module('cab');
dept.controller('ownerviewController',function($scope,$http,$timeout,$state,Upload,$window,DataService){
$scope.updateOwnerData=function(){
console.log('data',$scope.status,$scope.comment);
}
})
dept.directive('modal', function () {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog modal-lg">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace:true,
scope:true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value){
if(value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = false;
});
});
}
};
});
The above is my script part. Here My issue is I could not get value through $scope from pop up window. I need to collect the value via Angular.js Scope.
I failed to figure out what is the problem with scopes. It seems that values are propagated inside (so, you can set the initial value for ng-model) but they do not come outside, so I've used controllerAs syntax and view model principle:
dept.controller('ownerviewController',function($scope){
var vm = this; // Creating view model
$scope.openPopUP=function(){
$scope.showModal = !$scope.showModal;
}
$scope.updateOwnerData=function(){
console.log('data', vm.status, vm.comment); //Showing data from view model
}
})
Next step, you should define view model in your template by means of controllerAs syntax and change ng-model bindings:
<body ng-controller="ownerviewController as vm">
...
<select class="form-control" name="status" id="status" ng-model="vm.status" required="required">
...
You can find the working example here
The full version with modal popup is also available
scopes
In the modal pop-up directive both transcluded: true and scope: true create their own isolated scope. The scope you want to get the updated inputs is yet another: your controller's scope.
See visual explanation in Access Parent Scope in Transcluded Directive.
communication
In order to communicate from inside the directive/transcluded-form with the parent's scope you can use the following:
add an own scope scope: { status: '=', comment: '=' } to your directive and use two-way-binding '=' to pass the parent's scope-variables like <modal title="Owner Information" visible="showModal" data-status="status", data-comment="comment">
create and inject a factory or service to handle communication (i.e. data-updates) between directive (form) and controller
See AngularJS : Directive transcluded scope lost
solution with (inherited) scope
see fiddle.
It just uses the parent scope (from controller) via prototypical inheritance.
So the model (status, comment) can be used as well as the function to update can be called within the transcluded form.
jQuery datepicker not working inside a AngularJS ng-repeat block.
Not sure if anything wrong in code generation inside ng-repeat block. The same logic works outside ng-repeat.
Working code
<div class="form-group">
<label class="control-label col-md-4">TEST DATE</label>
<div class="input-group col-md-2">
<input type="text" id="testDate" name="testDate" readonly="readonly" class="form-control">
</div>
</div>
Not working code
<div ng-repeat="reportType in reportTypes">
<div class="form-group">
<label class="control-label col-md-4">{{reportType.reportTypeLabel}}</label>
<div class="input-group col-md-2">
<input type="text" id="{{reportType.reportTypeCodeId}}Date" readonly="readonly"
class="form-control">
</div>
</div>
</div>
Javascript
// Date picker dd/mm/yyyy
$(function() {
$("input[id*='date']").datepicker({
dateFormat : "dd/mm/yy"
});
});
$(function() {
$("input[id*='Date']").datepicker({
dateFormat : "dd/mm/yy"
});
});
Use this directive to initialize the datepicker after ng-repeat ends:
angular.module('mymodule').directive('ngOnFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.broadcastEventName ? attr.broadcastEventName : 'ngRepeatFinished');
});
}
}
};
});
In your controller:
$scope.$on('ngRepeatFinished', function(){
$("input[id*='date']").datepicker({
dateFormat : "dd/mm/yy"
});
});
In your view, add the directive ng-on-finish-render to element with ng-repeat:
<div ng-repeat="reportType in reportTypes" ng-on-finish-render>
<div class="form-group">
<label class="control-label col-md-4">{{reportType.reportTypeLabel}}</label>
<div class="input-group col-md-2">
<input type="text" id="{{reportType.reportTypeCodeId}}Date" readonly="readonly"
class="form-control">
</div>
</div>
</div>
You can add broadcast-event-name="myNgRepeatFinished" parameter if you have more than one ng-repeat in your scope and they have different purposes
$scope.$on('myNgRepeatFinished', function(){
$("input[id*='date']").datepicker({
dateFormat : "dd/mm/yy"
});
});
You have to create a custom directive for the datepicker.
The View
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="report in repArray">
<div class="form-group">
<label>{{report.values.name}}</label>
<input type="text" datepicker ng-model="datevalue" />
</div>
</div>
The Directive
var myApp = angular.module('myApp', []);
myApp.controller('myController', ['$scope', function ($scope) {
$scope.report = [
{ 'name': 'rep1' },
{ 'name': 'rep2' },
{ 'name': 'rep3' }
]
$scope.repArray = Object.keys($scope.report)
.map(function (value, index) {
return { values: $scope.report[value] }
}
);
} ]);
myApp.directive("datepicker", function () {
function link(scope, element, attrs, controller) {
element.datepicker({
dateFormat: "dd/mm/yy"
});
}
return {
require: 'ngModel',
link: link
};
});
I have the following Angular directive:
app.directive('basicInput', function () {
return {
restrict: 'AE',
templateUrl: 'DirectiveTemplates/BasicInput.html',
replace: true,
scope: {
ngModel: '=',
visual: '#',
placeholder: '#',
pattern: '=',
ngChange: '='
}
};
});
With the following Template:
<ng-form name="basicInput">
<div class="form-group">
<label for="input">{{visual}}</label>
<input type="text" class="form-control" name="input" ng-model="ngModel" placeholder="{{placeholder}}" required ng-pattern="pattern">
<div ng-messages="basicInput.input.$error" class="help-block">
<span ng-message="required"> Required</span>
<span ng-message="pattern"> Invalid Characters or is too short</span>
</div>
</div>
</ng-form>
Which I call in html as (for example):
<basic-input ng-model="formData.official_name" data-visual="Official Name" data-placeholder="Name" pattern="regex.short"></basic-input>
My Question is, how can I make the 'required' on the input tag from the template optional?
Try this:
Directive:
scope: {
ngModel: '=',
visual: '#',
placeholder: '#',
pattern: '=',
ngChange: '=',
isRequired: '='
}
Template:
<input type="text" class="form-control" ng-required="isRequired" name="input" ng-model="ngModel" placeholder="{{placeholder}}" ng-pattern="pattern">
Usage:
<basic-input ng-model="formData.official_name" is-required="false" data-visual="Official Name" data-placeholder="Name" pattern="regex.short"></basic-input>
ngRequired
you can set ng-required attribute to true or false ,, according to an attribute
docs : https://docs.angularjs.org/api/ng/directive/input
I've created a directive that show a calender like this :
app.directive('noeDatepicker', ['$parse', 'datetimeService', function ($parse, datetimeService) {
return {
restrict: 'E',
templateUrl: '/app/directives/templates/datepicker.html',
scope: {
model: '=',
withisrequired: '#'
},
require: '^form',
replace: true,
link: function (scope, element, attrs, ctrl) {
scope.buttonId = 'date_icon_' + attrs.id;
scope.inputId = 'date_input_' + attrs.id;
scope.cancelId = 'date_cancel_' + attrs.id;
setTimeout(function () {
Calendar.setup({...});
}
};
}
});
and my template is this :
<div ng-model="model" class="form-inline">
<input id="{{inputId}}" class="form-control" type="text" isrequired="{{withisrequired}}" ng-model="model" placeholder="1111/01/01" />
<img id="{{cancelId}}" src="/Content/noe/assets/image/icon/Cancle.svg" width="22" height="22" />
<img id="{{buttonId}}" src="/Scripts/jalalijscalendar/cal.png" />
</div>
but my problem is that when I wanna add for example ng-readonly to this directive, it just add readonly property to div element and input element is not readonly and it accept input from user (see code bellow) :
<noe:datepicker model="viewModel.date" id="date1" name="date1" ng-readonly="true" />
I want this ng-readonly function also make all child element in div tag readonly and not just add readonly proptety to div element!
How can i Do that ?
Update noeDatepicker with
scope:{
readonly:'=ngReadonly',
...
},
template: <div> <input ng-readonly="readonly"/> </div> ...
basically, you need to add ng-readonly for children elements you want to set.
I am trying to implement Change Password feature in MVC (Rest server) application from the User Control Panel but because of some strange reason I can't scope values from form input.
My html form:
<accordion-group heading="Change password" is-open="changePasswordStatus.open" style="cursor: pointer">
<div>
<div>
<form class="form-horizontal" role="form">
<form-row model="newPassword" name="New: " size="col-sm-8"></form-row>
<form-row model="repeatedNewPassword" name="Repeat: " size="col-sm-8"></form-row>
<form-row model="currentPassword" name="Current: " size="col-sm-8"></form-row>
<br>
</form>
</div>
<div>
<button class="btn btn-default btn-sm" ng-click="changePassword()">Save</button>
<button class="btn btn-warning btn-sm" ng-click="changePasswordStatus.open = !changePasswordStatus.open">Cancel</button>
</div>
</div>
</accordion-group>
My formRow.html:
<div class="form-group">
<label for="inputText3" class="col-sm-2 control-label">{{name}}</label>
<div class="{{size}}">
<input type="{{type}}" class="form-control" data-ng-model="model">
</div>
</div>
My formRow.js:
collectionsApp.directive('formRow', function(){
return {
restrict: 'E',
replace: true,
scope: {
model: '=',
name: '#',
size: '#',
type: '#'
},
templateUrl: '/directives/formRow.html',
link: function(scope, attrs, element) {
}
}
});
My userController:
$scope.changePassword = function() {
if ($scope.newPassword === $scope.repeatedNewPassword) {
userService.changePassword($scope.newPassword, $scope.currentPassword);
} else {
$scope.alerts.push({
msg : 'Passwords do not match!'
})
}
}
And when I enter values in inputs and place breakpoints and trigger changePassword() in debug i get:
If condition has passed with value of true because they are both undefined.
I believe this may be the case of prototypical inheritance and scope, requiring an object being passed into your scoped parameters. Mind trying to change your parent scope to use an object and bind to the properties and not the primitive values:
$scope.security = {newPassword : '', currentPassword = ''};
then you would use something like this in your attributes:
model="security.newPassword"
Or better yet, not make it confusing with model:
myapp-model="security.newPassword"
or pass in the whole object
myapp-security="security"
Working in this plunker template ?
<form ...>
<form-row model="newPassword" name="New: " size="col-sm-8" required ></form-row>
<form-row model="repeatedNewPassword" name="Repeat: " size="col-sm-8" required ></form-row>
<form-row model="currentPassword" name="Current: " size="col-sm-8" required ></form-row>
</form>