Submitting user feedback data to Elasticsearch - angularjs

I'm trying to use this angular bootstrap feedback form to collect user feedback data and index the information to Elasticsearch (to make it searchable).
So in my searchService for Elasticsearch, I simply did:
this.userSearchFeedback = function (userFeedbackForm) {
esClient.index({
index: 'searchfeedback-index1',
type: 'general',
opType: 'index',
refresh: true,
body: {
fields: ['comments', 'email']
}
}).then(function(es_return) {
console.log('success');
}, function(error) {
console.log('error');
});
};
userFeedbackForm is the name of the form, which looks like this:
<div class="row">
<div class="col-xs-12">
<div class="alert alert-info" role="alert">
We would love to hear your thoughts and suggestions on what we get right and wrong - it helps us improve our services to you. Your feedback is appreciated and helps us get better!
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<!-- DESCRIPTION -->
<div class="form-group">
<pre>Model: {{comments | json}}</pre>
<label for="description_field" class="control-label">Comment:</label>
<textarea class="form-control" placeholder="Please describe your issue or share your feedback!" name="comments" ng-model="comments" style="min-height: 165px;"></textarea>
</div>
<!-- /DESCRIPTION -->
<!-- EMAIL -->
<div class="form-group">
<pre>Model: {{email | json}}</pre>
<label for="email_field" class="control-label">Email:</label>
<input type="text" class="form-control" placeholder="Email" name="email" ng-model="email" />
</div>
<!-- /EMAIL -->
</div>
I've added ng-models for both inputs and added those in my controller
//initialize feedback form
$scope.feedback = {
comments: '',
email: ''
};
The module already comes with a function that is called from ng-click on the form template...
function submitButtonPressed(form) {
console.log("Submit button pressed");
console.log(form);
console.log($scope.feedback.comments);//added
console.log($scope.feedback.email);//added
and that is where I'm stuck.
What else do I need to do with the function in order for it to call the userSearchFeedback() in my searchService so that the form data can be indexed to ES?

After just trial and error with the above posted. I decided to look to see if there was something else available. There is and its amazingly simple to implement. Just add the url to submit to and your done! Had it up and running in just a few minutes (once I realized I had to supply a url).
HTH anyone.

Related

Basic Save form data the Angular way

I am trying to figure out the "correct" way to get and set form data using Angular. (By "correct" I mean one that supports scalability to larger apps, where controllers aren't overly simple, with shortcut references, as they are in most demos.) When I look though the examples, the seem so basic (such as no controllerAs specifier) that I can't figure out what's pointing at what.
Anyway, I'm expecting that there is an object somewhere in my viewmodel, accessible by the controller, that contains the data in its named fields, suitable for PUTing to the api - and doesn't contain all the cruft that is associated with the actual form and field objects:
To-wit: this is what settingsVm.settingsForm contains:
$dirty:true
$error:{}
$invalid:false
$name:"settingsVm.settingsForm"
$pristine:false
$submitted:true
$valid:true
Email:"asdsa#asdsad.com"
Id:{$validators: {}, $asyncValidators: {}, $parsers: [], $formatters: [], $viewChangeListeners: [],…}
$asyncValidators:{}
$dirty:false
$error:{}
$formatters:[]
$invalid:false
$name:"Id"
$options:null
$parsers:[]
$pristine:true
$touched:false
$untouched:true
$valid:true
$validators:{}
$viewChangeListeners:[]
StreetDirection:""
StreetName:"asdasdsadsada"
StreetNumber:"34"
Suite:""
Here is my form, with just a couple of fields:
<input type="hidden" name="Id" ng-model="settingsVm.settings.Id" />
<div class="form-group row">
<label for="Email" class="col-sm-1 col-form-label text-right"> Email:</label>
<div class="col-sm-5">
<input type="text" class="form-control form-group-med" maxlength="50" name="Email" ng-model="settingsVm.settingsForm.Email" required />
<div class="error-message" ng-show="settingsVm.settingsForm.Email.$invalid && settingsVm.settingsForm.Email.$touched || settingsVm.settingsForm.submitted">
<span ng-show="settingsVm.settingsForm.Email.$error.required"> Email is required.</span >
</div>
</div>
</div>
<div class="form-group row">
<label for="StreetName" class="col-sm-1 col-form-label text-right"> Name:</label>
<div class="col-sm-5">
<input type="text" class="form-control form-group-med" maxlength="50" name="Email" ng-model="settingsVm.settingsForm.StreetName" required />
<div class="error-message" ng-show="settingsVm.settingsForm.StreetName.$invalid && settingsVm.settingsForm.StreetName.$touched || listingVm.submitted">
<span ng-show="settingsVm.settingsForm.StreetName.$error.required">Street Name is required.</span >
</div>
</div>
</div>
</form>
Am I using the ng-model correctly? And am I using the $error correctly?
Should my model be a distinct object from my form? Maybe my model should point at settingsVm.settingsData instead of settingsVm.settingsForm.
Here is my submit:
vm.submit = function (isValid) {
vm.submitted = true;
if (isValid) {
settingsService.putSettings(vm.settingsForm);
}
};
Not sure if this will help you after 3 months, but I figured it out, please correct me if I am wrong somewhere.
Yes your statement is right, you actually need to separate form validation and input fields.
I assume when you are using field validation angular actually uses input field names not the ng-Model values. So this works for me
var myapp = angular.module("taskApp", [])
.controller("taskCtrl", function($scope, $http) {
$scope.form = {};
$scope.makeTask = function(data) {
console.dir(data);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-App="taskApp" ng-controller="taskCtrl">
<form name="TaskForm" novalidate ">
<div class="form-group ">
<label>Name</label>
<input name="Name " ng-required="true " class="form-control " ng-model="form.Name " />
<span ng-show="TaskForm.Name.$invalid && TaskForm.Name.$dirty ">The name is required</span>
</div>
<button ng-Click="makeTask(form) " type="submit ">Add</button>
</form>
<div id="output ">OUTPUT</div>

Checkbox not sending false value

I have a rails application which use AngularJS and I have a problem with a form, the problem is that I want to use a checkbox to send values true or false, but it only send true if it's checked and false if it's checked and unchecked after that, but if the user doesn't touch the checkbox, then it's not even sent as parameter.
<div class="checkbox">
<label>
<input type="checkbox" ng-model="car"> Do you have a car?
</label>
</div>
What can I do to make it send false if it the user doesn't ever check it?
Edit: The entire form is this, BTW, the form it's about creating a Poll, the car thing was just an example...
<h1>Create Poll</h1>
<form ng-submit="addPoll()" style="margin-top:30px;">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" ng-model="title"></input>
</div>
<div class="form-group">
<label>Description</label>
<textarea type="text" class="form-control" ng-model="description"></textarea>
</div>
<br>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="allow_anonymous_answer" ng-false-value="false"> Allow anonymous answers
</label>
</div>
<br>
<div class="form-group">
<label>Welcome message</label>
<textarea type="text" class="form-control" ng-model="initial_message"></textarea>
</div>
<div class="form-group">
<label>Outgoing Message</label>
<textarea type="text" class="form-control" ng-model="final_message"></textarea>
</div>
<button type="submit" class="btn btn-primary" style="float: right;">Continue</button>
</form>
When you hit Continue I make HTTP POST request with Restangular to create a Poll, but the problem is that when I don't touch the checkbox this is what I see in the log of Rails...
Started POST "/polls.json" for 127.0.0.1 at 2016-01-26 14:05:57 -0300
Processing by PollsController#create as JSON
Parameters: {"title"=>"asddddddddddddddda", "description"=>"aaaaaaaaaaaaaaaaaaaaa", "initial_message"=>"asdasdddddddddd", "final_message"=>"aaaaaaaaaaaaaaaaaaad", "poll"=>{"title"=>"asddddddddddddddda", "description"=>"aaaaaaaaaaaaaaaaaaaaa", "initial_message"=>"asdasdddddddddd", "final_message"=>"aaaaaaaaaaaaaaaaaaad"}}
Note that the parameter allow_anonymous_answer doesn't even appear, if I check the checkbox then I can see that the parameter is set as true, if I check it and then uncheck it, then it's set as false, but the problem is when the user doesn't even touch this, when this happens then the parameter is not even shown...
Just in case you wanna see, this is the controller of AngularJS...
angular.module('myapp').controller('CreatePollCtrl', ['$scope', 'Restangular',
function($scope, Restangular) {
Restangular.setFullResponse(true);
$scope.addPoll = function() {
var poll = {title: $scope.title, description: $scope.description, allow_anonymous_answer: $scope.allow_anonymous_answer, initial_message: $scope.initial_message, final_message: $scope.final_message};
Restangular.all('polls').post(poll).then(function(response) {
});
};
}]);
I think you should put a variable in your controller to achieve the binding between your HTML component and your JS code.
I am currently developing an Angular app, and what i do is to initialize all the ng-model variables in the first lines of my controller, so why dont you give a try to this:
In your first controllers lines:
$scope.allow_anonymous_answer = false;
Did you take a look at angular docs: https://docs.angularjs.org/api/ng/input/input[checkbox]
You can explicitly state what value the checkbox should send when it is not selected using ng-false-value
Add an ng-click to that checkbox and update the model there. Works fine.
<div class="checkbox">
<label>
<input type="checkbox" ng-model="car" ng-click="updateCar(this)">Do you have a car?</input>
</label>
</div>
In your controller:
var updateCar = function(checkbox) {
if (checkbox.checked) {
car = false;
}
else {
car = true;
}
}
I solved it...
In the controller
if ($scope.allow_anonymous_answer == null)
$scope.allow_anonymous_answer = false

Ng-model does not update view

I have searched thoroughly everywhere and can't seem to find the solution to my problem.
I am trying to make a form to fill in to create new task object onto projects on the website like a scrum backlog or something in Angular and angular-ui.
I use Angular-UI for typeahead functionality for when adding team members to a task, so available members on a project pop-up. I have seen guys with similar problems, but nothing seems to solve it for me. Below is the HTML and the controller. Sorry for the long markup, but I suspect it has to do something with nesting the controllers and mixing the scopes, so I'm including everything relevant.
<div ng-controller="SubmitCreateTaskController">
<div class="modal fade" id="taskModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title" id="myModalLabel">New Task</h4>
</div>
<div class="modal-body">
<form role="form" method="post" name="create-task">
<div class="form-group">
<label for="taskname">Task Name</label>
<input type="text" class="form-control" id="taskname" ng-model="taskForm.taskName" placeholder="Task Name">
</div>
<div class="form-group">
<label for="taskdesc">Description</label>
<textarea class="form-control" id="taskdesc" size="3" ng-model="taskForm.taskDescription" placeholder="Enter a short description here..." rows="2"></textarea>
</div>
<div class="form-group">
<label for="assigneddate">Assigned Date</label>
<input type="text" class="form-control" id="assigneddate" ng-model="taskForm.assignedDate">
</div>
<div ng-controller="TypeAheadController">
<div class="form-group">
<label for="contributors">Add Contributors</label>
<input id="contributors" type="text" class="form-control" ng-model="contrib.selected" typeahead="member for member in contrib.stream_members | filter:$viewValue"
typeahead-editable="false" typeahead-on-select="contrib.onSelect($item)">
</div>
<div class="form-group">
<label for="users">Contributors:</label>
<textarea class="form-control" disabled="disabled" id="users" ng-model="contrib.entered" rows="1"></textarea>
</div>
</div>
<div class="form-group">
<label for="taskcomments">Comments</label>
<textarea class="form-control" id="taskcomments" ng-model="taskForm.comment" placeholder="Comments" rows="2"></textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" ng-click="taskForm.submit()">Add Task</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>
</div>
Controllers:
.controller('SubmitCreateTaskController', ['$scope', '$http', function($scope, $http) {
$scope.taskForm = {};
$scope.taskForm.taskName = '';
$scope.taskForm.taskDescription = '';
$scope.taskForm.assignedDate = new Date();
$scope.taskForm.contributors = [];
$scope.taskForm.comment = '';
$scope.taskForm.submit = function(item, event) {
var formData = {name: $scope.taskForm.taskName,
description: $scope.taskForm.taskDescription,
status: 'open',
assigned: $scope.taskForm.assignedDate,
completed: null,
contributors: $scope.taskForm.contributors,
comments: [{body: $scope.taskForm.comment,
user: 'RACHE User',
date: new Date()
}]
};
var postURL = '../create_task/' + $scope.stream_name;
$http.post(postURL, formData)
.success(function(){
taskForm = {}; // empty the form of previous input values
alert('New Task Created for ' + $scope.stream_name);
})
.error(function(res) {
alert(res.data);
});
};
}])
.controller('TypeAheadController', ['$scope', '$http', function($scope, $http) {
$scope.contrib = {};
$scope.contrib.selected = '';
$scope.contrib.stream_members = undefined;
$scope.contrib.entered = [];
$http.get('/stream_members/' + $scope.stream_name)
.then(function(res) {
$scope.contrib.stream_members = res.data.stream_members;
});
$scope.contrib.onSelect = function($item) {
$scope.contrib.selected = '';
$scope.contrib.entered.push($item);
console.log($scope.contrib.entered);
};
This last bit is the important part in 'TypeAheadController'. I am trying to add the poped-up team members to a textarea just below which would be sent to the DB later. That is why I have bound the actual typeahead input box to 'contrib.select' and the "display/post" box to 'contrib.entered', so the search can be continued after adding one member, the search box is cleared, selected member is added to box below, new search can begin. In the mark up I call the last (onSelect) function in the typeahead-on-select callback. This so that I can clear the input box and another member can be easily added without deleting manually. IN this function the entered member should be appended to the array that is bound to below "display/post" box. This happens, since the console log shows good value, the search/input field gets cleared, so the function gets called as well.
The view does not get updated with the updated array bound to display box however. I have tried EVERYTHING. I have googled around and found it here that dot notation needs to be used as Angular can't update with primitives, but this hasn't helped either.
Everything works fine, the logic is good, values are good when I print them out, pop-up works and clears up as expected, but The damn textarea under it does not get updated.
Any suggestions? I have been stuck on this for almost a day now and I am really frustrated by this. I would be ever so greatfull for any help!
Thanks guys in advance!
Textarea do use the value attribute, your code isn't working because contrib. entered isn't between the tags, but because you can not bind the textarea to an array (it needs to bind to a string).
look at this plunker, if you do this in your html:
<textarea class="form-control" disabled="disabled" id="users" rows="1" ng-model="contrib.enteredString"></textarea>
and add this line at the end of your typeahead controller:
$scope.contrib.enteredString = $scope.contrib.entered.toString();
then the text area will update
Textareas don't use the ng-value as their value is contained within the tag. Remove the ng-model and put the value between the tags.
<textarea class="form-control" disabled="disabled" id="users" rows="1">{{contrib.entered}}</textarea>

Binding repeatable form subset to model

On a registration form I allow a user to enter zero or more phone numbers. Each phone number consists of a prefix and a number, these are two fields per phone number. The user may decide how many numbers he wants to provide, the add more numbers link would clone this part of the form.
Prefix: [_______]
Number: [_______]
+ add more numbers
The model I bind to is fixed and should be this format:
$scope.model = {
"...main inputs": "username, etc...",
phoneNumbers: [
// for each phone number I expect this object
{ "prefix": "+1", "number": "123123123" }
]
};
I am not sure how I should set up my ng-model for these text inputs to have these objects generated within the array.
Also, I am a great fan of referential binding and limiting things like scope watch and event-based changes to the scope, as often these changes go unnoticed for other directives that may be using this value (unless being watched). Basically that means it is my intention to have dynamic generation of objects within the array as the form is filled in with numbers, or dynamic removal of objects within the array as numbers are removed or both inputs left empty.
The array should only contain valid and filled objects, partially filled objects or empty objects should not be added to the model or array (as usually is done with properties with invalid values -- those properties get removed from the model object). Basically a push to the array with every validated object, and removal (slice) of objects for every invalid object. But then rather automatically, instead of writing a push/slice function.
Hi try out this code:
Html:
<form name="form">
<div data-ng-repeat="phone in phoneNumberArray">
<div class="form-group">
<label for="inputFirstName">Prefix</label>
<input id="prefix" class="form-control" type="text" ng-model="phone.prefix">
</div>
<div class="form-group">
<label for="inputLastName">Phone number</label>
<input id="phoneNumber" class="form-control" type="text" ng-model="phone.phoneNumber">
</div>
</div>
</form>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Add more number" data-ng-click="addmore()"/>
</div>
Controller:
$scope.phoneNumberArray = [
{
prefix: "",
phoneNumber:"",
}];
$scope.addmore = function () {
$scope.phoneNumberArray.push({
prefix: "",
phoneNumber: "",
});
}
Hi check this demo in Fiddle
Use the following code.....
<!DOCTYPE html>
<html>
<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body ng-app ng-controller="todoCtrl">
<form name="form" ng-submit="addmore()">
<div class="form-group">
<label for="inputFirstName">Prefix</label>
<input class="form-control" type="text" ng-model="phone_prefix"/>
</div>
<div class="form-group">
<label for="inputLastName">Phone number</label>
<input class="form-control" type="text" ng-model="phone_Number"/>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Add more number"/>
</div>
</form>
<div ng-repeat="phone in phoneNumberArray">
{{ phone.prefix + " "+ phone.phoneNumber}}
</div>
<script>
function todoCtrl($scope) {
debugger;
$scope.phoneNumberArray = [
{prefix: '14', phoneNumber:'123'}
];
$scope.addmore = function () {
$scope.phoneNumberArray.push(
{prefix: $scope.phone_prefix, phoneNumber: $scope.phone_Number}
);
}
}
</script>
</body>
</html>

Angular Breeze one-many-one example with checkbox

I have implemented domain models using Breeze's recommended one-many-one approach, and would like to assign these relationship using a checkbox approach. I'm using Angular for my data-binding, so this would be via the ngChecked directive.
I was wondering if anyone has attempted something similar and is able to post code snippets for getting, creating and deleting the one-many-one relationship, both at a controller and data service level.
I'm about to start on the requirement, and will gladly post my example as response to my question for those interested.
I've scanned through the Breeze samples, but couldn't find one fulfilling this requirement.
Many thanks!
Adding on some of my markup and scripts for this question, as I just cannot make sense of how I'm meant to approach this. Just to explain my use-case a bit. I provide users the capability at the top ofa page to create a list of "channels". Users can then also create a number of "business units". For each business unit, all channels get displayed and users can select which channels apply for each business unit.
Markup...
...
<div class="form-group"> /* portion of markup for adding channel */
<label class="control-label">New Interaction Channel</label>
<input class="form-control" placeholder="Channel Name..." data-ng-model="vm.newChannel.name" />
</div>
<div class="form-group">
<button class="btn btn-primary" data-ng-disabled="!vm.newChannel.name"
data-ng-click="vm.addChannel(vm.newChannel, $parent.vm.dboardConfig)">
Add Channel</button>
....
<accordion>
<accordion-group data-ng-repeat="bu in vm.busUnits"> /* business units listed in accordion groups */
<accordion-heading>
{{bu.name}}
...
</accordion-heading>
<h5>Channels</h5>
<div class="well well-sm panel-body">
/* this is where I start getting stuck. */
/* just not sure how to allocate the item viewmodel from */
/* Ward's example in my scenario */
<div data-ng-repeat="buc in bu.buChannels">
<div class="col-md-6">
<input type="checkbox" data-ng-checked="?????"
data-ng-model="buc.isSelected"/>
{{buc.name}}
</div>
...
and in my controller...
function getBusUnits() {
...
.then(function(data){
vm.busUnits = data;
vm.busUnits.forEach(function(bu){
getBusUnitChannels(bu);
});
});
}
function getBusUnitChannels(busUnit) {
datacontextSvc.dboardConfigs.getBusUnitChannelsById(busUnit.id)
.then(function (data) {
busUnit.busUnitChannelList = data;
});
busUnit.buChannels = [];
vm.channels.forEach(function (channel) {
busUnit.buChannels.push(channel);
// how do I assign the isSelected for each buChannel?
// how do I associate each buChannel with the BusUnitChannel obtained via breeze?
});
Am I going even vaguely in the right direction? I haven't event dealt with saving back to the server yet, I'd just like to be able to populate my lists first :-)
I've written a plunker to demonstrate the many-to-many checkbox technique I described previously. I'm hopeful it provides the essential insights for your case.
Ok, this is the solution I've come up with. I'd really appreciate some thoughts on my approach and whether or not its the most efficient (or even correct) approach..
Markup...
...
<div class="form-group"> /* portion of markup for adding channel */
<label class="control-label">New Interaction Channel</label>
<input class="form-control" placeholder="Channel Name..." data-ng-model="vm.newChannel.name" />
</div>
<div class="form-group">
<button class="btn btn-primary" data-ng-disabled="!vm.newChannel.name"
data-ng-click="vm.addChannel(vm.newChannel, $parent.vm.dboardConfig)">
Add Channel</button>
....
<accordion>
<accordion-group data-ng-repeat="bu in vm.busUnits"> /* business units listed in accordion groups */
<accordion-heading>
{{bu.name}}
...
</accordion-heading>
<h5>Channels</h5>
<div class="well well-sm panel-body">
<div data-ng-repeat="buc in bu.buChannels">
<div class="col-md-6">
<input type="checkbox" data-ng-model="buc.isSelected"/>
{{buc.name}}
</div>
...
and in my controller...
function getBusUnits() {
...
.then(function(data){
vm.busUnits = data;
vm.busUnits.forEach(function(bu){
getBusUnitChannels(bu);
});
});
}
function getBusUnitChannels(busUnit) {
datacontextSvc.dboardConfigs.getBusUnitChannelsById(busUnit.id)
.then(function (data) {
busUnit.busUnitChannelsList = data;
busUnit.buChannels = [];
vm.channels.forEach(function (channel) {
busUnit.buChannels.push(channel);
});
busUnit.busUnitChannelsList.forEach(function (buc) {
busUnit.buChannels.forEach(function (buCh) {
if (buc.channelId === buCh.id) {
buCh.buChannel = buc;
buCh.isSelected = true;
} else {
buCh.isSelected = false;
}
});
});
});
}

Resources