Bootstrap modal form elements unfocussable (on mobile/mobile emulation) - angularjs

From the repo: https://github.com/jmsherry/kitchenapp
Live at: https://kitchenapp2.herokuapp.com
If you click the 'contact us' link in the bottom right corner of the screen a modal appears. The modal is coded like:
<div class="modal-container">
<button type="button" class="close" aria-label="Close" ng-click="$close(null)">
<span aria-hidden="true">×</span>
</button>
<div class="content">
<h1>Contact Us</h1>
<div class="contact-form">
<form ng-submit="vm.sendMessage(this)" novalidate name="contactForm">
<div class="form-group" ng-if="vm.user && vm.user.email">
<p class="faux-label">From: <span class="faux-value">{{vm.user.email}}</span></p>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.from.$invalid && !contactForm.from.$pristine }" ng-if="!vm.user || !vm.user.email">
<label for="subject">From</label>
<input type="email" name="from" id="from" class="form-control" placeholder="From" ng-model="vm.user.email" ng-maxlength="30" ng-required="true" autofocus>
<ul ng-if="contactForm.from.$invalid && !contactForm.from.$pristine" ng-messages="contactForm.from.$error" class="help-block list-unstyled">
<li ng-message="maxlength">The subject must be less than 30 characters.</li>
<li ng-message="email">Not a valid email. Please check carefully and try again...</li>
</ul>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.subject.$invalid && !contactForm.subject.$pristine }">
<label for="subject">Subject</label>
<input type="text" name="subject" id="subject" class="form-control" placeholder="Subject" ng-model="vm.email.subject" ng-maxlength="30">
<ul ng-if="contactForm.subject.$invalid && !contactForm.subject.$pristine" ng-messages="contactForm.subject.$error" class="help-block list-unstyled">
<li ng-message="maxlength">The subject must be less than 30 characters.</li>
</ul>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.message.$invalid && !contactForm.message.$pristine }">
<label for="message">Message</label>
<textarea name="message" id="message" class="form-control" placeholder="Message..." ng-model="vm.email.body" ng-required="true" ng-minlength="10" ng-maxlength="500"></textarea>
<ul ng-if="contactForm.message.$invalid && !contactForm.message.$pristine" ng-messages="contactForm.message.$error" class="help-block list-unstyled">
<li ng-message="required">A description is required.</li>
<li ng-message="minlength">The name must be longer than 10 characters.</li>
<li ng-message="maxlength">The name must be less than 500 characters.</li>
</ul>
</div>
<!-- <div class="form-group"> -->
<div class="checkbox">
<label for="cc_self">
<input type="checkbox" id="cc_self" name="cc_self" ng-model="vm.email.cc_self">Send a copy to my email.
</label>
</div>
<!-- </div> -->
<div class="form-group">
<div class="pull-right">
<button type="button" class="btn btn-default" ng-click="$close(null)">Cancel</button>
<button type="submit" class="btn btn-primary" ng-disabled="contactForm.$invalid">Send</button>
</div>
</div>
</form>
</div>
</div>
</div>
Controller looks like:
angular.module('kitchenapp.directives')
.controller('KaFooterCtrl', ['$scope', '$q', '$log', '$modal', 'Email', 'Auth', 'toastr', function ($scope, $q, $log, $modal, Email, Auth, toastr) {
var vm = this,
$user;
function contact(day) {
$log.log('contact fn', arguments);
$modal.open({
templateUrl: '/views/modals/contact-modal.html',
controller: function modalController($modalInstance, $scope) {
$scope.vm = this;
$scope.vm.email = vm.email;
$scope.vm.user = vm.user;
$scope.vm.sendMessage = function () {
$modalInstance.close();
$q.when(vm.user, function (user) {
var $emailSent = Email.sendEmail(vm.email, vm.user);
$q.when($emailSent, function (res) {
toastr.success('Email sent! Thank you for your interest...');
}, function () {
toastr.error('We\'re sorry your email hasn\'t been sent, please try again later...');
});
});
event.preventDefault();
event.stopPropagation();
};
},
contollerAs: 'vm'
});
}
vm.year = new Date().getFullYear();
vm.email = {};
$user = Auth.getUser();
$q.when($user, function (user) {
vm.user = user;
});
vm.contact = contact;
vm.name = 'KaFooterCtrl';
}])
.directive('kaFooter', function () {
return {
restrict: 'E',
replace: true,
templateUrl: 'directives/footer/footer.html',
scope: {
companyName: '#',
companyEmail: '#'
},
controller: 'KaFooterCtrl as vm',
bindToController: true
};
});
}());
For some reason the first element focusses once, but any further interaction and the elements are not focussable by touch or mouse. Any clues why?
Thanks

So, it transpires that this is a problem when ng-touch is used. ng-touch auto-blurs the fields (https://github.com/angular/angular.js/pull/11212). A fix is on the way, but for now the user #jdhiro suggests using a directive to stop propagation of the touchend event outside of your modal. See https://github.com/angular-ui/bootstrap/issues/2017#issuecomment-39515923 for more

Related

Angular object not binding

I have the following issue. I have 2 controllers defined for 2 separate functions.
The 1st controller handle my login and the second 1 handles forgot password.
Login controller binds well but forgot password controller does bind but not the object.
Controllers
var singlePageApp = angular.module('SinglePageApp',[]);
singlePageApp.controller('LoginController', function ($scope, $location, $window) {
$scope.isFormValid = false;
$scope.message = null;
$scope.login = null;
// Login object
$scope.Login = {
Email: '',
Password: '',
};
//check form validation
$scope.$watch('LoginForm.$valid', function (newValue) {
$scope.isFormValid = newValue;
});
$scope.submitForm = function () {
console.log('Form is submitted with:', $scope.login);
...
};
});
singlePageApp.controller('ForgotPasswordController', function ($scope, $location, $window) {
$scope.message = null;
$scope.password = null;
// Password object
$scope.Password = {
Email: '',
};
$scope.$watchGroup(['password', 'Password'], function (newValues, oldValues, scope) {
console.log(newValues);
});
$scope.forgotPassword = function () {
};
});
Login HTML
<div ng-controller="LoginController">
<form class="form-signin mt10" name="LoginForm">
<h2 class="form-signin-heading pwc">Please login</h2>
<div class="login-wrap">
<input ng-model="login.Email" id="tbEmail" name="Email" type="email" class="form-control" placeholder="Email" autofocus required>
<span class="error" ng-show="LoginForm.Email.$touched && LoginForm.Email.$error.required">Email is required</span>
<input ng-model="login.Password" id="tbPassword" name="Password" type="password" class="form-control" placeholder="Password" required>
<span class="error" ng-show="LoginForm.Password.$touched && LoginForm.Password.$error.required">Password is required</span>
<label class="checkbox">
<span class="pull-right">
#* Forgot Password?*#
Forgot Password?
</span>
</label>
<div class="clearfix"></div>
<button id="btnloginAdmin" type="button" ng-click="submitForm()" ng-disabled="LoginForm.$invalid && !!LoginForm.$error.email" class="btn btn-success btn-block">Login</button>
<div class="text-center">
<label style="color: red;" id="lblError" class="hasError-label clearfix">{{message}}</label>
<div class="error error-red"></div>
</div>
</div>
</form>
</div>
HTML Modal
<div ng-controller="ForgotPasswordController">
<div aria-hidden="false" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="modalForgotPassword" class="modal fade">
<div class="modal-dialog">
<div class="modal-content" ng-form="fgForm">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Forgot Password ?</h4>
</div>
<div class="modal-body">
<p>Enter your e-mail address below to reset your password.</p>
<input type="email" name="Email" ng-model="password.Email"
placeholder="Email" autocomplete="off"
class="form-control placeholder-no-fix" required>
<span class="error" ng-show="fgForm.Email.$touched && fgForm.Email.$error.required">Email is required</span>
<div class="text-center">
<label style="color: red;" id="fError" class="hasError-label clearfix">{{message}}</label>
<div class="error error-red"></div>
</div>
</div>
<div class="modal-footer">
<a href="javascript:;" class="btn btn-success" ng-disabled="fgForm.$invalid && !fgForm.$error.email" ng-click="forgotPassword();">
<i class="fa fa-check"></i> | Submit
</a>
<a href="javascript:;" class="btn btn-danger" data-dismiss="modal">
<i class="fa fa-times"></i> | Cancel
</a>
</div>
</div>
</div>
</div>
I have tried changing to bind to a single object 'password' but it states its undefined. Then I changed it to 'Password.Email' but then it stays empty.
The 2 controllers are bind on the same page so not sure if that is the issue.
Thank you in advance.

external template (templateURL) in Angular directive is not working

I'm trying to create an accordion on button click using AngularJS directive. I have external template for creating accordion. But I couldn't get it working. Here is the code:
Directive:
(function() {
'use strict';
angular.module('accountApp')
.directive('addSubsite', addSubsite);
function addSubsite ($compile, $templateRequest){
var ddo = {
restrict: 'A',
link: function(scope, element) {
element.bind("click", function(e){
$templateRequest("addSubsiteTemplate.html").then(function(html){
var template = angular.element(html);
$element.append(template);
$compile(template)(scope);
$element.find(".add-subsites").append(template);
});
});
}
};
return ddo;
}
})();
index.html
<button add-subsite type="button" class="btn btn-primary pull-right margin10-b"id="aid-addSubSitebtn">Add Sub-Site</button>
<accordion>
<accordion-group>
<div class="accordion-heading header">
<a class="accordion-toggle" id="primary__site__address" data-toggle="collapse" href="#primary__site">
<h4><span class="pull-left"><i class="icon-minus"></i></span>Business Name (Primary Site)</h4></a>
</div>
<div class="form-group">
<label for="exampleInputName2">Name</label>
<input type="text" class="form-control" id="exampleInputName2" placeholder="Jane Doe">
</div>
<div class="form-group">
<label for="exampleInputEmail2">Email</label>
<input type="email" class="form-control" id="exampleInputEmail2" placeholder="jane.doe#example.com">
</div>
<button type="submit" class="btn btn-default">Send invitation</button>
</accordion-group>
</accordion>
Template.html
<accordion>
<accordion-group>
<div class="accordion-heading header">
<a class="accordion-toggle"data-toggle="collapse" href="#subsite__site">
<h4><span class="pull-left"><i class="icon-minus"></i></span>Business Name (SubSite)</h4></a>
</div>
<div class="form-group">
<label for="exampleInputName2">Name</label>
<input type="text" class="form-control" id="exampleInputName2" placeholder="Jane Doe">
</div>
<div class="form-group">
<label for="exampleInputEmail2">Email</label>
<input type="email" class="form-control" id="exampleInputEmail2" placeholder="jane.doe#example.com">
</div>
<button type="submit" class="btn btn-default">Send invitation</button>
</accordion-group>
</accordion>
I changed my approach Here is the sample https://jsfiddle.net/2u7aLg5c/
angular.module('testApp',[])
.controller('TestController', function(){
const vm = this;
vm.inputs=[{}];
vm.myInput = [];
vm.add = function(){
vm.inputs.push({})
}
})

angularjs scope within scope dynamic elements saving

I am having quite the trouble with scopes created by directives, and saving these dynamic elements' scope back to the parent.
Here is my directive:
app.directive('action', function() {
return {
restrict: "E",
scope: {},
templateUrl:'views/pages/projects/triggers/newaction.html',
controller: function($rootScope, $scope, $element) {
$scope.groups = $scope.$parent.groups;
$scope.scenes = $scope.$parent.scenes;
$scope.actions = $scope.$parent.actions;
$scope.Delete = function(e) {
//remove element and also destoy the scope that element
$element.remove();
$scope.$destroy();
};
}
};
});
here is my controller:
.controller('NewTriggerCtrl', ['Auth', '$scope', 'toastr', '$state', '$stateParams', 'FBURL', '$filter', '$compile',
function(Auth, $scope, toastr, $state, $stateParams, FBURL, $filter, $compile) {
var authData = Auth.$getAuth();
var ref = new Firebase(FBURL + '/projects/' + authData.uid + '/' + $stateParams.projectid);
// Submit operation
var retriveActions = function() {
// http://stackoverflow.com/questions/12649080/get-to-get-all-child-scopes-in-angularjs-given-the-parent-scope
var theseactions = [];
var ChildHeads = [$scope.$$childHead];
var currentScope;
while (ChildHeads.length) {
currentScope = ChildHeads.shift();
while (currentScope) {
/* theseactions.push({
type: currentScope.type,
data: currentScope.data,
data2: currentScope.data2
}); */
console.log("currentscope.type = " + currentScope.type);
};
currentScope = currentScope.$$nextSibling;
}
//return theseactions;
};
var retrieveactions2 = function() {
var theseactions = [];
var newevent = null;
var newdata = null;
var newdata2 = null;
console.log("in retrieve actions");
angular.forEach(angular.element(document.getElementsByClassName("newaction")), function(element){
console.log("iterating");
newevent = $(this).find('.newactionevent').value;
newdata = $(this).find('.newactiondata').value;
newdata2 = $(this).find('.newactiondata2').value;
theseactions.push({
event: newevent,
data: newdata,
data2: newdata2
});
});
return theseactions;
}
$scope.ok = function(actions) {
//console.log(retriveActions(actions));
//retriveActions(actions);
//console.log("$scope.trigger.actions = " + $scope.trigger.actions);
//console.log("actions = " + actions);
console.log(retrieveactions2());
$scope.triggers.$add($scope.trigger).then(function (triggerRef) {
ref.child('triggers').child(triggerRef.key())
.update({created_at: Firebase.ServerValue.TIMESTAMP});
toastr.success('Trigger Added!', 'Trigger has been created');
$state.go('app.projects.edit', {projectid : $stateParams.projectid}, {reload: true});
});
};
$scope.newaction = function() {
var divElement = angular.element(document.querySelector('#actions'));
var appendHtml = $compile('<action></action>')($scope);
divElement.append(appendHtml);
};
$scope.cancel = function() {
$state.go('app.projects.edit', {projectid : $stateParams.projectid}, {reload: true});
};
/////////////////////// *Submit operation
}])
here is my new trigger html:
<div class="page page-newtrigger" ng-controller="NewTriggerCtrl">
<!-- row -->
<div class="row">
<div class="col-md-12">
<!-- tile -->
<section class="tile tile-simple">
<!-- tile body -->
<div class="tile-body">
<form name="form" class="form-horizontal form-validation" role="form" novalidate>
<div class="form-group mt-12" style="margin-top: 15px;">
<label for="name" class="col-sm-1 control-label">Name <span class="text-danger" style="font-size: 15px;">*</span></label>
<div class="col-sm-1">
<input type="text" name="name" class="form-control" id="name" placeholder="Trigger name..." ng-model="trigger.name" required>
</div>
<div class="btn-group col-sm-2">
<label class="btn btn-green" ng-model="trigger.type" uib-btn-radio="'astro'">Astro</label>
<label class="btn btn-green" ng-model="trigger.type" uib-btn-radio="'time'">Real-Time</label>
<label class="btn btn-green" ng-model="trigger.type" uib-btn-radio="'input'">Input</label>
</div>
<div class="animate-switch-container" ng-switch on="trigger.type">
<div class="animate-switch" ng-switch-when="astro">
<div class="btn-group col-sm-2">
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'sunrise'">Sunrise</label>
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'sunset'">Sunset</label>
</div>
<div class="col-sm-1">
<input type="text" name="offset" class="form-control" id="offset" placeholder="Offset (+/- minutes)" ng-model="trigger.option">
</div>
</div>
<div class="animate-switch" ng-switch-when="time">
<div class="btn-group col-sm-2">
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'repeat'">Repeat</label>
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'once'">Once</label>
</div>
<div class="animate-switch-container" ng-switch on="trigger.event">
<div class="animate-switch" ng-switch-when="repeat">
<div class="col-sm-3 btn-group">
<label class="btn btn-cyan" ng-model="trigger.option.mon" uib-btn-checkbox>Mon</label>
<label class="btn btn-cyan" ng-model="trigger.option.tue" uib-btn-checkbox>Tue</label>
<label class="btn btn-cyan" ng-model="trigger.option.wed" uib-btn-checkbox>Wed</label>
<label class="btn btn-cyan" ng-model="trigger.option.thu" uib-btn-checkbox>Thur</label>
<label class="btn btn-cyan" ng-model="trigger.option.fri" uib-btn-checkbox>Fri</label>
<label class="btn btn-cyan" ng-model="trigger.option.sat" uib-btn-checkbox>Sat</label>
<label class="btn btn-cyan" ng-model="trigger.option.sun" uib-btn-checkbox>Sun</label>
</div>
<div class="col-sm-1">
<input type="text" name="time" class="form-control" id="time" placeholder="Time (HH:mm:ss)" ng-model="trigger.data">
</div>
</div>
<div class="animate-switch" ng-switch-when="once">
<div class="col-sm-2" >
<p class="input-group" ng-controller="DatepickerDemoCtrl">
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="trigger.option" is-open="opened" min-date="minDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)"><i class="fa fa-calendar"></i></button>
</span>
</p>
</div>
<div class="col-sm-1">
<input type="text" name="time" class="form-control" id="time" placeholder="Time (HH:mm:ss)" ng-model="trigger.data">
</div>
</div>
</div>
</div>
<div class="animate-switch" ng-switch-when="input">
<div class="col-sm-1">
<select ng-init="1" ng-model="trigger.option" class="form-control mb-10">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
</select>
</div>
<div class="col-sm-2">
<div class="btn-group">
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'high'">Goes high</label>
<label class="btn btn-green" ng-model="trigger.event" uib-btn-radio="'low'">Goes low</label>
</div>
</div>
</div>
</div>
<button class="btn btn-success b-0 pull-right" style="margin-right: 30px;" ng-click="newaction()"><i class="fa fa-plus mr-5"></i>New Action</button>
</div>
<div id="actions">
<action ng-repeat="action in trigger.actions"></action>
</div>
<div class="form-footer">
<button class="btn btn-success b-0 pull-right" ng-click="ok(trigger)" ng-disabled="form.$invalid">Submit</button>
<button class="btn btn-lightred btn-ef btn-ef-4 btn-ef-4c" ng-click="cancel()"><i class="fa fa-arrow-left"></i> Cancel</button>
</div>
</form>
</div>
<!-- /tile body -->
</section>
<!-- /tile -->
</div>
</div>
<!-- /row -->
</div>
here is my newaction html:
<!-- row -->
<div class="newaction row">
<div class="col-md-10 col-sm-offset-2">
<form name="form" class="form-horizontal form-validation" role="form" novalidate>
<div class="form-group mt-12" style="margin-top: 15px;">
<div class="col-sm-1"><button ng-click="Delete($event)" class="btn btn-danger"><i class="glyphicon glyphicon-trash"></i></button></div>
<div class="btn-group col-sm-2">
<select ng-init="1" ng-model="action.type" class="newactiontype form-control mb-10">
<option value="transition-scene">Transition Scene</option>
<option value="set-group-intensity">Set Group Intensity</option>
<option value="inject-trigger">Inject Trigger</option>
<option value="color-change">Color Change</option>
</select>
</div>
<div ng-if="action.type=='transition-scene'">
<div class="col-sm-3">
<select chosen="" class="newactionevent form-control mb-10" ng-options="scene.name for scene in scenes track by scene.name" ng-model="action.scene"></select>
</div>
<div class="col-sm-1">
<input type="text" name="fadetime" class="newactiondata form-control" id="fadetime" placeholder="Fade Time (seconds)" ng-model="action.fadetime">
</div>
</div>
<div ng-if="action.type=='set-group-intensity'">
<div class="col-sm-3">
<select multiple chosen="" class="newactionevent form-control mb-10" ng-options="group.name for group in groups track by group.name | filter: {type : 'white'}" ng-model="$parent.action.group"></select>
</div>
<div class="col-sm-1">
<input type="text" name="intensity" class="newactiondata form-control" id="intensity" placeholder="Intensity(%)" ng-model="action.intensity">
</div>
<div class="col-sm-1">
<input type="text" name="fadetime" class="newactiondata2 form-control" id="fadetime" placeholder="Fade Time (sec)" ng-model="action.fadetime">
</div>
</div>
<div ng-if="action.type=='inject-trigger'">
<div class="col-sm-2">
<input type="text" name="triggernumber" class="newactiondata form-control" id="triggernumber" placeholder="Trigger number..." ng-model="action.data">
</div>
</div>
<div ng-if="action.type=='color-change'">
<div class="col-sm-3">
<select multiple chosen="" class="newactionevent form-control mb-10" ng-options="group.name for group in groups track by group.name| filter:group.type!='white'" ng-model="action.group"></select>
</div>
<div class="col-sm-1">
<input colorpicker="rgb" ng-model="action.data" type="text" class="newactiondata form-control w-md mb-10">
</div>
<div class="col-sm-1">
<input type="text" name="fadetime" class="newactiondata2 form-control" id="fadetime" placeholder="Fade Time (sec)" ng-model="action.fadetime">
</div>
</div>
</div>
</form>
</div>
</div>
<!-- /row -->
I have multiple attempts at this, as you can see with the two different functions in NewTriggerCtrl. The first was to get all the child scopes and iterate through, however when I call this it locks the browser up with over 250,000 logs. So maybe I am passing the wrong scope?
I am relatively new to Angular, and have some experience in JQuery, and I attempted to name the inputs with classes and finding them with document calls, but that is not working either. I have an app running and I can create, detele, etc. groups, triggers (not the trigger actions), and scenes, So I understand the basics of controllers and scopes. But saving these child scopes to the main trigger (what I assume would be trigger.actions) has stumped me. Maybe there is a better way to this? I know my code may not be efficient, I am attempting to get a base then clean up later.
Thank you.
UPDATE:
Ok so new directive:
app.directive('action', function() {
return {
restrict: "E",
scope: true,
templateUrl:'views/pages/projects/triggers/newaction.html',
controller: function($rootScope, $scope, $element) {
$scope.Delete = function(e) {
console.log("$scope.action = " + $scope.action);
//remove element and also destoy the scope that element
$element.remove();
$scope.$destroy();
};
}
};
});
and entire trigger controller:
'use strict';
app
.controller('TriggersCtrl', ['Auth', '$scope', '$state', '$stateParams', '$firebaseArray', '$firebaseObject', 'FBURL',
function(Auth, $scope, $state, $stateParams, $firebaseArray, $firebaseObject, FBURL) {
// General database variable
var authData = Auth.$getAuth();
var ref = new Firebase(FBURL + '/projects/' + authData.uid + '/' + $stateParams.projectid);
$scope.triggers = $firebaseArray(ref.child('triggers'));
$scope.groups = $firebaseArray(ref.child('groups'));
$scope.scenes = $firebaseArray(ref.child('scenes'));
$scope.triggersObject = $firebaseObject(ref.child('triggers'));
//////////////////////////// *General database variable
// get the model
if($stateParams.triggerid) {
var id = $stateParams.triggerid;
$scope.trigger = $firebaseObject(ref.child('triggers').child(id));
$scope.actionsObject = $firebaseObject(ref.child('triggers').child(id).child('actions'));
} else {
$scope.trigger = {};
$scope.actionsObject = {};
}
}])
.controller('NewTriggerCtrl', ['Auth', '$scope', 'toastr', '$state', '$stateParams', 'FBURL', '$filter', '$compile', '$firebaseArray',
function(Auth, $scope, toastr, $state, $stateParams, FBURL, $filter, $compile, $firebaseArray) {
var authData = Auth.$getAuth();
var ref = new Firebase(FBURL + '/projects/' + authData.uid + '/' + $stateParams.projectid);
// Submit operation
$scope.ok = function() {
console.log("$scope.actions = " + $scope.actions);
console.log("$scope.trigger.actions = " + $scope.trigger.actions);
$scope.triggers.$add($scope.trigger).then(function (triggerRef) {
ref.child('triggers').child(triggerRef.key())
.update({created_at: Firebase.ServerValue.TIMESTAMP});
toastr.success('Trigger Added!', 'Trigger has been created');
$state.go('app.projects.edit', {projectid : $stateParams.projectid}, {reload: true});
});
};
$scope.newaction = function() {
var divElement = angular.element(document.querySelector('#actions'));
var appendHtml = $compile('<action></action>')($scope);
divElement.append(appendHtml);
};
$scope.cancel = function() {
$state.go('app.projects.edit', {projectid : $stateParams.projectid}, {reload: true});
};
/////////////////////// *Submit operation
}]);
The scopes for the actions are created, and when i delete they are logged as an object. But the actions are not saved into trigger. I tried creating an actionsObject and then ng-repeat="action in actionsObject" but that didnt work. I try $scope.trigger.actions = $scope.actionsObject (to no avail) [my thought is how I created the $scope.trigger and the $scope.actionsObject is that they should behave the same if I call "action in actionsObject" vs "action in trigger.actions"..?]. My assumption is that I append the newaction template, which it's scope is action, the ng-repeat="action in trigger.actions" part, does this create the bind for when I add the new element to #actions that it saved to the trigger.actions scope? Having "scope: true" in the directive gives me the scenes and groups perfectly (understand the inheritance a little better). I should note that I create a trigger (a single one) and add multiple actions (which groups and scenes are part of the ng-options which is why i needed those models). Does the multiple appends affect anything? This is my last major feature to work out. I appreciate the help!
Your action directive use an isolate scope, and that is for internal use by the directive only.
The data you want to save in your Actions-array (to be submitted with the form) should go directly into the action-object and not on the scope.
You can access the action-object in two ways:
Use a child scope (by specifying scope: true instead of scope: {})
This allows you to access the parent's scope variables by inheritance:
$scope.action refers to the current action object (from ng-repeat)
$scope.groups refers to the groups array from the parent scope
This means you can assign data to $scope.action.something and it will affect the action object immediately. This is the beauty of angular: Manipulate the data directly, and skip the tedious "loop through everything to get the values".
You can also use an isolate scope (using scope: {} syntax). When using an isolate scope you need to specify explicitly the variables you want to use.
{
...
scope: {
theActionObject: '=',
groupList: '=',
sceneList: '='
}
...
}
This can then be referenced in you directive's link or controller function as $scope.theActionObject.
$scope.theActionObject.something = 'test';
// Access the groups array
alert($scope.groupList.length);
And to tie it together you need to specify the references in the HTML:
<div id="actions">
<action ng-repeat="action in trigger.actions"
the-action-object="action"
group-list="groups"
scene-list="scenes"></action>
</div>
Hope this will help you!
where are your groups, scenes and actions objects defined on the $parent scope? You would need to define them in your NewTriggerCtrl
$scope.groups = [];
$scope.scenes = [];
$scope.actions = [];
Since you are isolating scope, you would normally bind them through attributes like:
scope: {groups: '=', scenes: '=', actions: '=' }
and
<action ng-repeat="action in trigger.actions" groups="groups" scenes="scenes" actions="actions"></action>
An alternative to isolate scope would be to use a prototypical inherited scope with scope: true
Either way, binding $scope.foo = $scope.$parent.foo; seems unnecessary.
Those two methods are for if you actually need new scopes for each action directive. If you don't, then you can just leave the scope property out of your action directive definition and the action directives will use whatever scope they are rendered in.

Angular validation directive not working properly

I am using custom angular directive for showing validation. Directive code is as below
angularFormsApp.directive('showErrors',['$timeout', function ($timeout) {
return {
restrict: 'A',
require: '^form',
link: function (scope, el, attrs, formCtrl) {
// find the text box element, which has the 'name' attribute
var inputEl = el[0].querySelector("[name]");
// convert the native text box element to an angular element
var inputNgEl = angular.element(inputEl);
// get the name on the text box so we know the property to check
// on the form controller
var inputName = inputNgEl.attr('name');
var helpText = angular.element(el[0].querySelector(".help-block"));
// only apply the has-error class after the user leaves the text box
inputNgEl.bind('blur', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
helpText.toggleClass('hide', formCtrl[inputName].$valid);
});
scope.$on('show-errors-event', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
});
scope.$on('hide-errors-event', function () {
$timeout(function () {
el.removeClass('has-error');
}, 0, false);
});
}
}
}]);
and Html is as below
<div class="container" id="login-form">
<img src="assets/img/logo-big.png">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h2>Login Form</h2>
</div>
<div class="panel-body">
<form name="loginForm" class="form-horizontal" novalidate>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12">
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-user"></i>
</span>
<input type="text" class="form-control" placeholder="Username" autocomplete="Off" ng-required="true" name="username" autofocus ng-model="loginUser.username">
</div>
<span class="help-block" ng-if="loginForm.username.$error.required">Username is required</span>
</div>
</div>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12" >
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-key"></i>
</span>
<input type="password" class="form-control" placeholder="Password"
name="password"
ng-model="loginUser.password" autocomplete="Off"
ng-required="true">
</div>
<span class="help-block" ng-if="loginForm.password.$error.required">Password is required</span>
</div>
</div>
<div class="form-group mb-n">
<div class="col-xs-12">
Forgot password?
<div class="checkbox-inline icheck pull-right p-n">
<label for="">
<input type="checkbox"></input>
Remember me
</label>
</div>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<div class="clearfix">
Register
Login
</div>
</div>
</div>
</div>
</div>
</div>
Controller code :
var loginController = function ($scope, $window, $routeParams, $uibModal, $location, $filter, $rootScope, DataService, SharedProperties) {
$rootScope.bodylayout = 'focused-form animated-content';
$scope.loginUser = {
username: "",
password:""
}
$scope.load = function () {
//getAppointmentInfo();
};
$scope.SubmitLoginForm = function () {
$scope.$broadcast('show-errors-event');
if ($scope.loginForm.$invalid)
return;
}
}
angularFormsApp.controller("loginController", ["$scope", "$window", "$routeParams", "$uibModal", "$location", "$filter", "$rootScope", "DataService", "SharedProperties", loginController]);
Now When I open form below input control validation span is displaying by default . When I click on Login button then its showing in red and working fine.
problem is it shouldn't show by default when Page is opend.. Please see image below
Instead of this
loginForm.password.$error.required
try this
(loginForm.$submitted || loginForm.username.$dirty) && loginForm.password.$error.required
Take a look at the ng-messages directive. Its fairly elegant. Example:
<form name="myForm">
<input type="text" ng-model="field" name="myField" required minlength="5" />
<div ng-messages="myForm.myField.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message="minlength">The value entered is too short</div>
</div>
</form>
You can then combine it with any form validation. Just place the error messages from the validators onto the elements $error object and they are automatically rendered in your UI.
I ended up here as part of my search for an issue. In my case I was using a directive with a changing minimum value like this:
ngOnChanges(changes: SimpleChanges) {
if (changes.minDate && this.control) {
this.control.updateValueAndValidity({ onlySelf: true });
}
}
This means that the form will not be updated, I removed onlySelf and it worked correctly.
this.control.updateValueAndValidity();
Just leaving this as a breadcrumb in case someone else does something similar.

One model not accessible, one is

I have two tiny projects, both use essentially the same setup, one returns a model value one returns the model value as undefined. At this point I am lost as to why these two examples are behaving differently. I checked the failing example, and the model correctly populates
Successful Behavior
Partial
<div class="row">
<div class="col-md-4 .col-md-offset-4 content-pane no-margin">
<h4>Input Your Inquiry</h4>
<form ng-submit="submit(invoice)">
<div class="control-group">
<div class="controls">
<div class="input-prepend">
<span class="add-on">Invoice Number</span>
<input id="who" type="text" class="input-xlarge" ng-model="invoice.number">
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-prepend">
<span class="add-on">PO Number</span>
<input id="who" type="text" class="input-xlarge" ng-model="invoice.po">
</div>
</div>
</div>
<input type="submit">
</form>
</div>
<div ng-if="status" class="col-md-4 .col-md-offset-4 content-pane no-margin">
<h4>Invoice Status</h4>
<div>{{status}}</div>
</div>
</div>
App.js
app.config(function($routeProvider){
$routeProvider
.when('/',
{
templateUrl: 'app/partials/inquiry-input-form.html',
controller: 'MainCtrl'
})
.otherwise(
{
templateUrl: 'app/partials/404.html'
})
});
app.controller('MainCtrl', function($scope,formsResults) {
$scope.submit = function() {
var invoiceNum = $scope.invoice.number;
var po = $scope.invoice.po;
formsResults.getInvoiceStatus(po,invoiceNum).success(function(data) {
var status = data.records;
if (angular.isUndefined(status)) {
$scope.status = 'Pending Reciept or Entry';
} else {
$scope.status = status[0].field_124208[0];
}
});
};
});
app.factory('formsResults', function($http) {............
Returning Model as undefined in controller
Partial
<tabset>
<tab active=workspace.active>
<tab-heading>
Search All Fields
</tab-heading>
<div>
<br /><br />
<form class="form-horizontal" ng-submit="submitAll(searches)">
<div class="form-group">
<div class="col-md-4">
<input id="textinput" name="textinput" type="text" class="form-control input-md" ng-model="searches.term">
<span class="help-block">Enter the term you wish to search on.</span>
{{searches.term}}
</div>
</div>
<!-- Button -->
<div class="form-group">
<div class="col-md-4">
<button id="singlebutton" name="singlebutton" class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
</div>
</tab>
<tab>
<tab-heading>
Search By Field
</tab-heading>
<div>
<br /><br />
<form class="form-horizontal" ng-submit="submitSpecific(search)">
<div class="form-group">
<div class="col-md-4">
<select id="selectbasic" name="selectbasic" class="form-control">
<option value="4">Country</option>
</select>
<span class="help-block">Enter the field you wish to search on.</span>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<div class="col-md-4">
<input id="textinput" name="textinput" type="text" class="form-control input-md">
<span class="help-block">Enter the term you wish to search on.</span>
</div>
</div>
<div class="form-group">
<div class="col-md-4">
<button id="singlebutton" name="singlebutton" class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
</div>
</tab>
</tabset>
App.js
var app = angular.module ('app',['ngRoute','ngSanitize','ui.bootstrap','dialogs.controllers','dialogs']);
app.config(function($routeProvider){
//http://docs.angularjs.org/tutorial/step_07
$routeProvider
.when('/',
{
templateUrl: 'app/partials/home_tpl.html',
controller: 'HomeCtrl'
})
.when('/search',
{
templateUrl: 'app/partials/search-form.html',
controller: 'SearchCtrl'
})
.when('/add-sop',
{
templateUrl: 'app/partials/support-process.html',
controller: 'EditSOPCtrl'
})
.otherwise(
{
redirectTo: '/'
})
});
app.controller('HomeCtrl', function($scope) {
});
app.controller('SearchCtrl', function($scope,formsSopSearchCriteria) {
$scope.submitAll = function() {
var searchTerm = $scope.searches.term;
console.log(searchTerm);
var searchPackage={
"offset":0,
"limit":0,
"fields":["record_id","dt_created","created_by","dt_updated","updated_by",149654,149655,149692],
"filters":{"and":[{"field_id":"149655","operator":"contains","value":searchTerm}]},
"sort":{}
};
searchPackage=JSON.stringify(searchPackage);
$scope.searchPackage = searchPackage;
formsSopSearchCriteria.getMatches(searchPackage).success(function(data) {
$scope.sopRecords = data.records;
});
};
$scope.submitSpecific = function() {
var searchPackage = ""
searchPackage=JSON.stringify(searchPackage);
$scope.searchPackage = searchPackage;
formsSopSearchCriteria.getMatches(searchPackage).success(function(data) {
$scope.sopRecords = data.records;
});
};
});
app.factory('formsSopSearchCriteria',function($http) {........
Any ideas? My understanding is that it as an issue of scope. But in the failing example, the partial I provide is mapped to the SearchCtrl in the routeProvider. As a result the model should be available in the controller, just like the example that is working. I suspect I must be missing something, but I am not clear what I am missing.
You need to create $scope.searches as an empty object on initialization:
app.controller('SearchCtrl', function($scope,formsSopSearchCriteria) {
$scope.searches = {};
Or for clarity:
$scope.searches =
{
term: ''
};
If you don't, it will not be created until the first input is entered (I believe it is the $parse service that the ng-model directive uses that creates the searches object in this case).
The problem is that the object might not be created in the scope you want it to.
The reason it works in the first case is because there are no child scopes, so the invoice object is created in the controller scope.
In the second case there is one or many child scopes (probably from the tabs, I haven't dug any deeper into it), so the searches object is created in one of those instead.
Due to how prototypical inheritance works in Javascript, if you predefine the objects in the controllers' scopes, the properties will be created correctly.

Resources