I'm building a list in my app, which users can add to. Once they add their item and hit the save button, I would like the item they added to appear instantly on the list, and for the input box to clear. Here is my save function in my controller. It works fine when I reload the page but I think there is a way to bind by updating the model through the database. Thank you!
export class QuestionDetailsController {
public question;
public answer;
public id;
public save() {
this.answerService.save(this.answer, this.question.id).then(() => { this.$location.path("$location.path") });//how to update models?
}
constructor(
private answerService: MyApp.Services.AnswerService, private $location: angular.ILocationService, private questionService: MyApp.Services.QuestionService, $routeParams: ng.route.IRouteParamsService)
{
this.question = this.questionService.get($routeParams["id"])
}
And now HTML page
<!--<legend id="qdLegend">{{controller.question.title}}</legend>-->
<h2 id="qdLegend">{{controller.question.title}}</h2>
<hr id="qdhr" />
<div id="questionDiv">
<div class="col-md-7" id="questionBody">
<p>{{controller.question.userQuestion}}</p><br /><br />
</div>
</div>
<div class="col-md-7">
<div id="answerDiv" ng-repeat="a in controller.question.answers">
{{a.userAnswer}} --answered by {{a.userName}}
</div>
</div>
<form ng-submit="controller.save()">
<div>
<div class="form-group col-md-7" id="answerBox">
<label class="control-label" for="textarea">Your Answer</label>
<textarea ng-model="controller.answer.userAnswer" class="form-control" id="textarea1" name="textarea"></textarea>
</div>
<div class="form-group">
<label class="col-md-4 control-label" for="button1id"></label>
<div class="col-md-8" id="detailsButtons">
<button type="submit" id="button1id" name="Save" class="btn btn-success">Save</button>
<button id="button2id" name="button2id" class="btn btn-danger">Cancel</button>
</div>
</div>
</div>
</form>
Okay, I don't have a running typescript compiler at the moment, so I'll give you some hints and some untested code here.
public save() {
this.answerService.save(this.answer, this.question.id)
.then(() => {
this.question.answers.push(angular.copy(this.answer));
this.answer = {};
});
}
When the service returns succes you add the answer the the questions list of answers. I use angular.copy() to break the references so you can add multiple answers with no references issues.
After this I just overwrite the object with an empty one.
This is fairly simple. AngularJS docs have an example in the same style.
Here is a simplified plunker showing the action with a javascript controller. This should be no problem for you to get a hold of how it should be done.
As a note to the question, please supply minified examples to make it faster to understand your question.
Related
I have the following code where I have added 5 input texts via ng-repeat and now am trying to fetch each of the input element's values. I am hitting some roadblock, so please help me out on how to fetch individual values.
HTML:
<form class="quick-add-form" method="post">
<label class="quick-add-label">Enter the catalog number and quantity</label>
<div class="quick-add">
<div class="catalog-list">
<label class="catalog-number-label" for="catalog-number-input">Catalog Number:</label>
<div ng-repeat="values in catalogNumber(number) track by $index">
<input id="catalog-number-input-{{$index}}" class="catalog-number-input"
type="text" ng-model="catalog.description" validate-input>
</div>
</div>
<div class="qnty-list">
<label class="pdt-qnty-label" for="pdt-qnty-input">Quantity:</label>
<div ng-repeat="values in catalogNumber(number) track by $index">
<input id="pdt-qnty-input-{{$index}}" class="pdt-qnty-input" type="number" pattern="[0-9]*"
inputmode="numeric" min="1" max="999" ng-model ="catalog.quantity" name="qty"
maxlength="3">
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-danger cta-button quick-add-btn"
ng-click ="quickAdd(catalog)">Add to List</button>
</div>
</form>
and controller:
$scope.catalog = {};
$scope.catalogNumber = catalogNumber;
$scope.initializeModal = initializeModal;
$scope.quickAdd = quickAdd;
function catalogNumber(num) {
return new Array(num);
}
function quickAdd(val) {
console.log(val);
}
function init() {
$scope.number = 5;
$scope.catalog.quantity = 1;
}
I am pretty sure its because of indexing and which is why by adding value in one input, its adding same value in all at once. But I am not sure how/where to use the $index properly to achieve the result.
Thanks.
Perhaps a better way would be to use the model? For example:
<div ng-repeat="values in catalogNumber(number) track by $index">
<input
id="catalog-number-input-{{$index}}"
class="catalog-number-input"
type="text"
ng-model="catalog.description[$index]"
validate-input>
</div>
Now in your controller:
catalog.description = [];
Then you have an array of objects, which you can perform your math on:
var total = _.sum(Object.values(catalog.description));
Edit: I had forgot I was using Lodash/Underscore there, but if you wanted to use standard JS you could use a standalone method, or reduce etc.,
Hopefully a simple Angular binding question.
I have a TextBox on my Angular 4 [x.component.html] that has an (input) event tied to it, so each time the user types in a character into the textbox it reaches out to a method in the [x.component.ts] typescript file - which then retrieves data from a service to populate a local array of data in the typescript file - which is bound to a dropdownlist on the [x.component.html]. This process functionally works fine.
However the problem I am having is that when it updates the data in the local array, it is not clearing out the previous data in the dropdown, so the dropdown list ends up getting populated with lots of duplicate data. Has anyone experienced this same problem before? The local array of items seems to have the correct number of items in it, it just seems to keep populating the dropdownlist with the items without ever clearing the previous.
Thanks so much for your help.
Please note I have cut the code down to only the relevant parts for brevity.
[x.component.ts]
export class CreateComponent {
testService: TestService;
valueList: testValue[];
constructor(testService: TestService) {
this.testService = TestService;
}
ngOnInit() {
}
FieldChanged(event: any, controlNameToPopulate: string){
if (event.target.value.length >= 9) {
this.testService.getRecords(event.target.value).subscribe((data: any[]) => {
if (data.length != 0){
document.getElementById(controlNameToPopulate).hidden = false;
//this.valueList.slice();
this.valueList = data;
}
});
}
}
}
[x.component.html]
<div class="row">
<div class="col-md-6">
<nb-card>
<nb-card-header>Testing with Updating dropdownlist</nb-card-header>
<nb-card-body>
<form [formGroup]="formGrp">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Quick search</label>
<div class="col-sm-9 input-group">
<input type="text" class="form-control input-sm" style="width: 50%" id="txtbx_ValTest" formControlName="testVal" (change)="FieldChanged($event, 'ddValuesToUpdate')">
<select id="ddValuesToUpdate" class="btn btn-secondary dropdown-toggle" style="width: 50%" hidden>
<option *ngFor="let val of valueList" [value]="val.id">{{val.id}}</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button type="submit" class="btn btn-success pull-right">Add</button>
</div>
</div>
</form>
</nb-card-body>
</nb-card>
</div>
</div>
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.
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>
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;
}
});
});
});
}