AngularJS x-editable typeahead with conditional - javascript

trying to achieve the following behaviour with angular & x-editable:
I have an Add User form with 1 required input: "Name".
When adding a User there are 2 options:
1. If the User exists in the $scope.names I use typeahead to display the option list -> OK
2. If the User is new (not in $scope.names) then I should have a second required input "Email" and I should only display it when typeahead finds no match for my "Name" Input
And this is where I'm stuck... I don't really know how to apply the following "raw" condition:
if user is not in users -> show Email Input
Any help would be much appreciated!
Here is the
JSfiddle!
<div ng-app="app">
<div ng-controller="InvestorsController">
<ul>
<li ng-repeat="investor in investors">
<span href="#" editable-text="investor.name" e-placeholder="Name" e-name="name" e-form="investorsForm" e-typeahead="name for name in names | filter:$viewValue | limitTo:8" e-required>
{{ investor.name || 'empty' }}
</span>
<form editable-form name="investorsForm" ng-show="investorsForm.$visible" class="form-buttons form-inline" shown="inserted == investor">
<button type="submit" ng-disabled="investorsForm.$waiting" class="btn btn-primary sx-btn-ok">OK</button>
<button type="button" ng-disabled="investorsForm.$waiting" ng-click="investorsForm.$cancel()" class="btn btn-default">X</button>
</form>
<div class="buttons" ng-show="!investorsForm.$visible">
<button class="btn btn-primary" ng-click="investorsForm.$show()">OK</button>
<button class="btn btn-danger" ng-click="removeInvestor($index)">X</button>
</div>
</li>
<button class="" ng-click="addInvestor()">Add Investor</button>
</ul>
</div>
</div>

I would add something along the lines of: ng-hide="shouldHide()" in the span that is the email input.
Now, the shouldHide function should always grab the value from the name input and check it against the names list and return true if found and false if not found.
Hope this helps, didn't have the time to mock it up in a fiddle sorry.
[Edit] You find the value of the name input in the $scope.investor.name as far as I can figure, I never worked with x-editable.

As said previously, you could try to get the input value and check if exists in list, if the value isn't in the list the optional input will be shown.
$scope.shouldHide = function(index) {
//get the typeahead input value:
var nameInput = document.getElementsByName('name')[0]
var nameValue = null
if (nameInput){
nameValue=document.getElementsByName('name')[0].value;
}
if (nameValue){
//check if typeahead input value is in list
$scope.names = ['User 1', 'User 2', 'User 3', 'User 4', 'User 5', 'User 6'];
return $scope.names.indexOf(nameValue) >=0
}
return true;
};
I fork your fiddle and added the explained behaviour here: http://jsfiddle.net/nachoorme/RZK9u/1/

Related

Angular 2 Form - disable submit when ngModel = empty obj

I have an Angular2 form that is created dynamically with a for loop. For this question I am concerned with the radio buttons in my form. The form is created in the HTML then from the TS I assign the ngModel of each input to an empty object. I want the submit button in my form to be disabled until a radio button is chosen:
<form (ngSubmit)="onNext(f)" #f="ngForm">
<div class="multi-choice-question-div radio-btn-div question_div"
*ngIf="question?.type === 'multi-choice' && !question?.isYesOrNo">
<div *ngFor="let answer of question?.answerDetails">
<input
type="radio"
class="display-none"
id="{{ answer?.answerId }}"
[(ngModel)]="ngModelObj['question_' + question.questionId]"
name="answerForQustion{{ question?.questionId }}"
[value]="answer"
required>
<label class="col-md-12 col-sm-12 multi-choice-label" for="{{ answer?.answerId }}">
<p class="q-text">{{ answer?.value }}</p>
</label>
</div>
</div>
<button class="btn btn-next"
type="submit"
*ngIf="currentSectionIndex < sectionsArr.length - 1"
[disabled]="!f.valid">
NEXT
</button>
</form>
Even when the client has not chosen a radio button, the form thinks that it is valid and I think this is because ngModel for the radio input is set = to {}.
How can I keep this same setup (because it is engrained deep into my component frontend and backend) but make the form invalid when the ngModel = {}
Two ways, call a function to check if the value is empty (potentially expensive, probably overcomplicated):
[disabled]="f.invalid || isEmpty(f.value)"
isEmpty(formValue) {
let x = JSON.parse(JSON.stringify(formValue));
return Object.getOwnPropertyNames(x).length === 0;
}
The stringify and parse together strip out any undefined keys (go ahead, console.log(formValue) and look at those undefined keys!)
Or you can check the form for dirty which indicates:
dirty : boolean A control is dirty if the user has changed the value
in the UI.
Note that programmatic changes to a control's value will not mark it
dirty.
[disabled]="!f.valid || !f.dirty"
https://angular.io/docs/ts/latest/api/forms/index/AbstractControl-class.html#!#dirty-anchor
Plunker demo: http://plnkr.co/edit/14yQk2QKgBFGLMBJYFgf?p=preview

Stop multiple field filling with v-model?

I'm pretty new to Vue.js and while I've figured most of the problems I'm hitting out, I can't get my head around this one.
I'm displaying a list of posts based on API output, and want to have a comments box for each post. Posting to the API is working fine and it adds it without a problem, but because I'm using the same v-model for each input in the loop any text entered is replicated throughout all other matching inputs due to the binding.
<div class="row" v-if="">
<div class="col-md-11">
<input type="text" class="form-control" value="" title=""
placeholder="Add comments here.." v-model="note.note">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-default" style="margin-left: -1.5rem" v-on:click="addNote(task.id)">Add Comment</button>
</div>
</div>
JS:
addNote: function (id) {
if (this.note.note) {
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');
this.$http.post('api/note/create/' + id, this.note).then(function (response) {
this.fetchTaskList();
});
}
}
Screenshot:
Is there a different directive I should be using? Or is there another way around this?
You can do this by using index in v-for and then binding each box to the notes comment like so:
<!-- use index, so we know where we are in the loop -->
<div v-for="(note, index) in notes">
{{ note.note }}
<!-- Bind to the comment at the given index -->
<input v-model="notes[index].comment" />
</div>
Now you just need to set that up in data:
data: {
notes: [
{note: 'note 1', comment: ''},
{note: 'note 2', comment: ''},
{note: 'note 3', comment: ''}
]
}
Here's the JSFiddle: https://jsfiddle.net/efxzmq9s/

How do submit dynamically generated form fields to processing page with angularjs?

I am working on a somewhat lengthy form that has a section where a block of form fields are dynamically added/removed. So far I have not been able to find a way to make this method work.
For example, if I add 3 subcontractors and fill in the input for each, the ngmodel holds the value as it should. However, if I remove one of the fields the ngmodel is still holding the input value. Additionally, if I remove the 2nd field in the series it doesn't actually remove that instance, it just reduces the count so instead of having field #1 and field #3 like I should, I actually have field #1 and field #2.
The end result in all of this is to submit to the process page and have a loop iterate through the total # of added fields and insert them into a db.
I'm new to angularjs so I'm not sure that the method I am attempting is even best practice as it seems to be overly complex, so I am open to suggestions on how to improve the process as well.
HTML
<div ng-repeat="subcontractor in subcontractors">
<div class="well well-sm">Subcontractor #{{subcontractor.id}}<span id="subCounter"></span>
<button type="button" ng-click="removeSub()" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="form-group">
<div class="col-md-6">
<label for="subName">Subcontractor Name</label>
<input type="text" class="form-control" id="subName" name="subName" placeholder="Subcontractor" ng-model="formData.subName['subName'+($index+1)]">
</div>
<button type="button" class="btn btn-info btn-sm pull-right" ng-show="showAddSub(subcontractor)" ng-click="addNewSub()">[+] Add New Sub</button>
</div>
ANGULARJS
$scope.addNewSub = function() {
var newItemNo = $scope.subcontractors.length+1;
$scope.subcontractors.push({'id': +newItemNo});
};
$scope.removeSub = function() {
var newItemNo = $scope.subcontractors.length-1;
$scope.subcontractors.splice($scope.subcontractors-1, 1);
};
$scope.showAddSub = function(subcontractor) {
return subcontractor.id === $scope.subcontractors[$scope.subcontractors.length-1].id;
};

How to delete a Firebase Object based on it Property:key value?

I just starting in AngularJs and Firebase. I am creating a todo list app.
In my app I have an html form like so :
<form name="frm" ng-submit="addTodo()" id="form-border">
<div class="form-inline">
<input type="text" class="form-control" name="newtodo" ng-model="newtodo" required="" id="inputform" />
<button class="btn btn-success" ng-disabled="frm.$invalid">Go</button>
<button class="btn btn-danger" ng-click="clearCompleted(todo)">Clear Completed</button>
</div> <!-- end form group -->
</form>
Below the form are the actual "todo" tasks in an html unordered list, added through with the form above.
code example:
<li ng-repeat="todo in todos" ng-mouseenter="hover = true" ng-mouseleave="hover = false" class="list-group-item" ng-class="inactive" >
<input type="checkbox" ng-model="todo.done" ng-click="clickDone(todo)"/>
<span ng-class="{'done':todo.done}" id="donetask">{{todo.title}}</span><!-- add line-through when task is done-->
<ng-include ng-class="{'hiRank-off': !hover}" src="'rankbutton.html'"></ng-include><!-- ng-include rankbutton-->
</li>
My app.js is storing this data with Firebase:
var myData = new Firebase("https://firebaseio-demo.com/ToDos");
and
$scope.todos.$add({'title':$scope.newtodo,'done':false, 'timetag': datecreated}); //push to Array
$scope.newtodo = '';
I am marking my tasks completed clickDone(todo) which grabs variable 'todo' from my ng-repeat. Like so:
$scope.clickDone = function(todo){
$scope.todos.$save(todo); //saves todo state when is checked
};
My dilemma is I am trying to delete an object with my ng-click="clearCompleted(todo)", or in other words marked as complete.
which can be found in the first code block
But I don't think clearCompleted()function is in the ng-repeat="todo in todos" scope, so I'm having some troubles deleting objects with this function.
I am trying this to delete with no success and errors
TypeError: Cannot read property 'done' of undefined
$scope.clearCompleted = function($scope.todos){
$scope.todos.$remove($scope.todo.done);
};
I just figured it out!! I had to run forEach( ) method to look in $scope.todos and check if todo.done evaluated to true.
$scope.clearCompleted = function(){ $scope.todos.forEach(function(todo){ if(todo.done){ $scope.todos.$remove(todo); } }); };

Form validation summary in AngularJS with custom message per field

I'm trying to render a validation summary on a page using AngularJS. Here's what I have so far:
<div ng-app>
<div ng-controller="ctrl">
<form name="userForm">
<fieldset>
<legend>User Info</legend>
<p><label>Name: <input type="text" required ng-maxlength="15" name="name" ng-model="name" /></label></p>
<p><label>Age: <input type="number" required name="age" ng-model="age" /></label></p>
<p><label>Favorite Color: <input type="text" required name="favColor" ng-model="favColor" /></label></p>
<p><input type="button" value="Submit" ng-click="submitForm()" /></p>
</fieldset>
</form>
<div id="validationSummary" ng-show="hasValidationErrors()">
<ul>
<li ng-repeat="error in validationErrors">{{ error }}</li>
</ul>
</div>
</div>
</div>
In my controller, I'm creating an array with all of the errors.
function ctrl($scope) {
$scope.hasValidationErrors = function () {
return $scope.validationErrors && $scope.validationErrors.length > 0;
};
$scope.submitForm = function() {
$scope.validationErrors = [];
for (var property in $scope.userForm) {
if ($scope.userForm.hasOwnProperty(property) && $scope.userForm[property].$invalid) {
$scope.validationErrors.push($scope.userForm[property].$name);
}
}
}
}
The thing I can't figure out is: how can I get more than just the name of each field that is invalid? I've noticed that there is also an $error property on each field. Outputting this instead of $name gives me the following:
{"required":true,"maxlength":false}
{"required":true,"number":false}
{"required":true}
So I can get the field name, and I can get an object that describes what is wrong with that particular field. How can I define an error message, so that if a field is required it will output "{name} is required"? It seems like this could be a data- attribute on the input element itself, although I don't know how I would access that attribute.
Of course, it's also possible that I'm making things entirely too difficult on myself. Is there a better way to approach this while staying in the "AngularJS" world?
Here's a link to the jsFiddle I've been working on.
A far easier and cleaner way is demonstrated here
Simply put (where form1 is your form name attribute):
<ul>
<li ng-repeat="(key, errors) in form1.$error track by $index"> <strong>{{ key }}</strong> errors
<ul>
<li ng-repeat="e in errors">{{ e.$name }} has an error: <strong>{{ key }}</strong>.</li>
</ul>
</li>
</ul>
A totally dynamic validation summary based on AngularJS 1.5.7 with ngMessages using field names that the user recognizes
A template with error messages:
<script type="text/ng-template" id="error-messages">
<span ng-message="required">This field is required.</span>
<span ng-message="email">Please enter a valid email.</span>
</script>
Display of the error summary (here for a form named "candidateForm"):
<div data-ng-if="candidateForm.$submitted && candidateForm.$invalid">
Please correct these fields and then try to send again:
<ul>
<li data-ng-repeat="field in candidateForm" data-ng-if="candidateForm[field.$name].$invalid">
<div>
{{ getValFieldName(field) }}
<span data-ng-messages="candidateForm[field.$name].$error" role="alert">
<span data-ng-messages-include="error-messages"></span>
</span>
</div>
</li>
</ul>
</div>
A helper function to get the name of the label associated with the input field (instead of displaying input field names or "internal ID codes" to users):
$scope.getValFieldName = function (field) {
return $("label[for=" + field.$name + "]").text(); // to get label associated with input field
// return $("#" + field.$name).attr("placeholder"); // to get placeholder of input field
};
You can reuse this set of standard error messages on multiple forms, ngMessages ensure only one error displayed per field, and looks like the fields are listed in the order they appear in the HTML.
Should probably be made into a directive instead of the jQuery-style helper function, and might even want to add a click-handler on each error message to scroll to the input field with the error. Maybe another will run with that idea?
Use below line to get values of every text box
var value = $scope.userForm[property].$name;
var vl =$('*[name="' + value + '"]').data('required-message')
$scope.validationErrors.push(vl);

Categories

Resources