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>
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.
I am trying to build a custom directive (based on an example I saw) to ensure that the confirm password is correct.
I am running into some trouble with the $setValidity tag, although all my console.log() execute and print out, it does not seem that the validity is being updated. Pass stays true even when I read in my console "set to false".
I made sure the name and the ng-model are the same and I also tried using scope.$apply, but that would comeout with an error saying i was trying to apply when something else is being applied.
Here is my code:
HTML
<form ng-submit="signup()" name="profileForm">
<div class="form-group">
<label class="form-label" for="input-example-2">Password</label>
<input class="form-input" ng-model="pnew" type="password" name="pnew" placeholder="Password" required>
</div>
<div class="form-group">
<label class="form-label" for="input-example-2">Confirm Password</label>
<input class="form-input" name="confirm" ng-model="confirm" type="password" placeholder="Password" required pwcheck>
</div>
{{profileForm.confrim.$error.pass}}
<hr>
{{profileForm.confirm.$error}}
<hr>
{{profileForm.confirm.$valid}}
<span ng-show="profileForm.confirm.$error.pwCheck"> the passwords dont match</span>
<div class="form-group">
<button class="btn btn-primary">Sign up</button>
</div>
</form>
JS code for the pwcheck directive
.directive('pwcheck', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
function valid(value){
scope.$watch("confirm", function(newval, oldval){
console.log(value);
console.log(scope.pnew);
if(value==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return value;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
});
}
ctrl.$parsers.push(valid);
} } });
No need to use $watch on confirm if you're using your directive on same element & having ng-model in require. So your code can be
ctrl.$parsers.unshift(valid);
ctrl.$formatters.unshift(valid);
function valid(viewValue){
console.log(viewValue);
console.log(scope.pnew);
if(viewValue==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return viewValue;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
}
Most trivial example plunk
https://plnkr.co/edit/vPbICfSCDnwHKh07DAXJ?p=preview
One of the features of my web application is the possibility to add new users (username + password) through a form. Thereby, I have one JSON object (l_usernames) defined in a controller (UsersController) with all the usernames already chosen by users to avoid the repetition of usernames (it's a unique key).
Sample of my data (fetched-data.json) - format of object "usernames" (l_usernames):
[{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}]
There is a sample of the form to add new users (add-user.html):
<div class="row" ng-controller="UsersController">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Add New User</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputUserUsername" class="col-sm-2 control-label"><i class="icon fa fa-user"></i> USERNAME</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUserUsername" placeholder="Username" ng-model="user_username">
</div>
</div>
<div class="form-group">
<label for="inputUserPassword" class="col-sm-2 control-label"><i class="icon fa fa-key"></i> PASSWORD</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputUserPassword" placeholder="Password" ng-model="user_password">
</div>
</div>
</form>
</div>
</div>
<form class="form-inline">
<div class="span7 text-center">
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="(!user_username || !user_password)">Save</button>
</div>
</form>
</div>
</div>
Sample of my controller (userscontroller.js):
var app = angular.module('myApp');
app.controller('UsersController', ['$scope', 'services', function($scope, services) {
services.getData().then(function(data){
$scope.l_usernames = data.data;
});
}])
.factory('services', ['$http', function($http){
var serviceBase = 'services/'
var object = {};
object.getData = function(){
return $http.get('fetched-data.json');
};
return object;
}]);
I would like to know how it is possible to not allow the insert of new users if the username is already chosen - searching through the JSON object l_usernames - with ng-disabled (by disabling the "Save" button). I also want to print a simple message - "Username already chosen" - if such situation occurs. Thank you.
Add a watch on the user_username scope variable. Whenever it changes search through the JSON object, you can use lodash or underscorejs to search through l_usernames to see if the username already exists. If it exists then set a variable in the scope to false. Bind the ng-disabled of the save button to this variable. Use debounce on the user_username for better performance.
Take a look at this fiddle here
Controller
function UsersController($scope) {
$scope.name = 'Superhero';
$scope.l_username = [{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}];
$scope.allowSave = true;
$scope.$watch('user_username', function(value) {
if (_.findWhere($scope.l_username, {"USERNAME": value}) !== undefined)
$scope.allowSave = false;
else
$scope.allowSave = true;
})
}
HTML
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="!allowSave">Save</button>
Whenever the entered username is found in the array, the allowSave variable is changed which disables the 'save' button.
Note: I have used underscore.js to search through the list. You can use you custom method as well.
I have added the warning message and debounced the model for better performance.
I would make a validation directive.
HTML:
<input username-exists type="text" ng-model="userName" ng-model-options="{updateOn: 'default blur', debounce: { 'default': 700, 'blur': 0 }}" />
<div ng-if="myFormName.$error.usernameExists">Username exists!</div>
<button type="button" ng-disabled="myFormName.$invalid">
The ng-model-options is so that your model doesn't go crazy and update always (it delays the validation).
Javascript:
app.directive('usernameExists', function() {
return {
restrict: 'A', //match attributes only
require: 'ngModel',
scope: {
l_usernames: '='
},
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
//first, assume the model is valid until proven otherwise
ctrl.$setValidity('usernameExists', true);
if(viewValue.length > 0 && !ctrl.$error.usernameExists) {
for(var i = 0; i < scope.l_usernames.length; ++i) {
if(scope.l_usernames[i].USERNAME === viewValue) {
//username exists, so set valididty to false
//the form is not valid
ctrl.$setValidity('usernameExists', false);
break; //found match
}
}
}
return viewValue;
});
}
};
})
I have a custom directive which works when I once change input value but when I submit form without changing input field value directive does not display error message.
html code:
<form name="myform" role="form" class="form-horizontal" method="post" novalidate>
<div class="form-group">
<label class="control-label col-sm-3" for="firstName">First name :</label>
<div class="col-sm-9">
<input name='firstName' class="form-control" type='text' required ng-model='name' string>
</div>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-success" />
</div>
</form>
directive :
validationModule.directive('string', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function validationError(value) {
if (value.length < 1) {
ctrl.$setValidity('required', false);
//showPopOver(element, "Required field");
errorValidation(element, "Required field");
// $(element).popover('show');
}
if (/[a-zA-Z]/.test(value)) {
ctrl.$setValidity('invalid', true);
// $(element).popover('destroy');
successValidation(element);
}
if (/[0-9]/.test(value)) {
ctrl.$setValidity('invalid', false)
errorValidation(element, "number not allowed");
// showPopOver(element, "number not allowed");
// $(element).popover('show');
}
return value;
}
ctrl.$parsers.push(validationError);
}
};
});
How can I call directive to perform validation on submit event ?
I don't know which version of angular you are using, but since 1.3 validators are not hacked into parsers. See $validators in https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Also using such generic names for directives is discouraged due to possible name collisions. Use some prefix eg myString
I'm having a problem with AngularJs. I have created a directive that $watch the model and it takes some action based on the model's current status. However, although during debugging I can see that the $watch is set, it is only being triggered after the model get valid at least once and I don't know why that is happening. Debugging it doesn't even gets into the $watch function when something is typed.
The code is below:
Directive:
(function() {
'use strict';
var app = angular.module('app');
app.directive('tooltipValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var tooltip = $(element).qtip({
content: {
text: element.next('div')
},
show: false,
hide: true
}).qtip('api');
scope.$watch(attrs.ngModel, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
}
};
});
})()
HTML:
<div class="form-address clearfix" ng-show="isNewShippingAddress" ng-form="newShippingAddressForm">
<h3>Include new shipping address:</h3>
<div class="novo-endereco clearfix" id="newAddress">
<div class="required address apelido">
<label for="newShippingAddressAlias">Alias</label>
<input id="newShippingAddressAlias" name="newShippingAddressAlias" type="text" tooltip-validation ng-model="newShippingAddress.Alias" required ng-maxlength="32" />
<div data-ng-show="newShippingAddressForm.newShippingAddressAlias.$dirty && newShippingAddressForm.newShippingAddressAlias.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.maxlength">max 32 char</p>
</div>
</div>
<div class="required endereco">
<label for="newShippingAddressStreet">Street</label>
<input id="newShippingAddressStreet" name="newShippingAddressStreet" type="text" tooltip-validation ng-model="newShippingAddress.Street" required ng-maxlength="256" />
<div data-ng-show="newShippingAddressForm.newShippingAddressStreet.$dirty && newShippingAddressForm.newShippingAddressStreet.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.maxlength">max 256 char</p>
</div>
</div>
<div class="required cep">
<label for="newShippingAddressZipCode">ZipCode</label>
<input id="newShippingAddressZipCode" name="newShippingAddressZipCode" type="text" tooltip-validation ng-model="newShippingAddress.ZipCode" required ng-pattern="/^[0-9]{8}$/" />
<div data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$dirty && newShippingAddressForm.newShippingAddressZipCode.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.pattern">8 digits</p>
</div>
</div>
<input type="submit" class="button grey" value="Save new address" data-ng-click="saveShippingAddress()" ng-disabled="newShippingAddressForm.$invalid" />
</div>
</div>
Regards,
dimello
Try:
scope.$watch(function(){
return ngModel.$viewValue; //Watch for view value (the value in your input)
}, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
DEMO
Explanation:
When you type an invalid value into the input with ng-model, the underlying model is not updated, causing your scope.$watch(attrs.ngModel not being fired because you're watching for changes in the model. If you need to fire the function every time the input changes no matter it's valid or not, try the above solution.