AngularJS - bind ng-model with button click - html

I've got an irritating problem with data binding using ng-model and button.
The principle of operation of my site:
My HTML site displays a list of projects (loaded from external .json file).
Each row has a button named Edit which displays a modal containing some <input type="text" filled with relevant data about project (like project.name, project.date etc.)
Initial value of input is equal to object data (text-input called Name will contain project.name etc.)
Object is modified only if you click Save button and confirm the operation (confirm(sometext) is okay).
Closing the modal, not clicking the button or pressing cancel on confirmation box should prevent data from being updated.
Editing input (let's say that project.name is "Project2" and I modify it by adding 3 numbers resulting in "Project2137"), closing modal and opening it again should result in "Project2" text inside input (because object wasn't modified, only input)
So far I understand that single text input should look like this
<input type="text" id="editName" class="form-control" ng-model = "project.name">
Using ng-model means that they are binded. That's what I know. However editing input means that object is updated as soon as I enter some data.
I tried to fiddle with ng-model-options but I didn't find any possible solutions.
I tried to do it programmatically as well using
<input type="text" id="editName" class="form-control" value = {{project.name}}>
....
<button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>
And function:
$rootScope.edit = function(project)
{
if(confirm("Are you sure to save changes?"))
{
project.name = angular.element(document.getElementById('editName')).val();
// ...and so on with other properties
This solution is kinda close to what I wanted to achieve (object is updated only on confirm), but I faced another problem: input loads data from object only once at the beginning instead of each time the modal is opened which is against rule #5
Is there any way to fix this using either ng-model bind or custom function? Or maybe there is some other, easier way?
--EDIT--
Here I don't have any problem with saving the data using a button, everything works well and clicking Save is reflected in a projects list. (well until I hit a F5 key).
The problem is that input text is not properly binded to project and that's what I want to fix.
Sample data (pseudocode)
project1.name = "Proj1"
project2.name = "Proj2"
I click an Edit button on row #1
Text input displays "Proj1". Everything is fine.
I change input by adding some random characters like "Proj1pezxde1"
Text input is now "Proj1pezxde1"
I do not click Save button.
I close the modal.
Project summary still displays "Proj1". Okay.
I click an edit button on first row
10. Text input is "Proj1pezxde1" even though I didn't modify an object.
Text input should read data from object again (each time I open this modal) and thus display "Proj1"
That's the problem I want to fix. Sorry for being a little bit inaccurate.

You can create a copy of the project object in modal controller and use this object to bind with the input element of the modal
$scope.copyProj = angular.copy($scope.project);
Assign the copy object properties to project only when save is clicked.

As per my understanding after reading the provided descriptions, you have a list of projects, which is being used as in an repeater and you want to bind each projects data to a Text box and a Button.
Have you tried initializing your Projects object following way?
$scope.projects = [
{ 'name': 'proj1', 'id': '1' },
{ 'name': 'proj2', 'id': '2' }
];
Then you can do something like below to show your data
<div ng-repeat="project in projects">
<div>
<input type="text" class="form-control" ng-model = "project.name">
<button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>
</div>
</div>

The simplest way to do this in my opinion is using a second object that is a copy of the project, and after confirmation applying the changes to the original project object.
For example, a simple "pseudo code" of a controller:
function MyCtrl($scope) {
$scope.projects = [...];
$scope.currentProject = null;
$scope.edit = function(project) {
$scope.currentProject = angular.copy(project); // This will create a copy so the changes in $scope.currentProject will not reflect.
// Open dialog with input bound to $scope.currentProject
if (confirm) {
// Assign all properties from currentProject to project
angular.extend(project, $scope.currentProject);
}
}
}

So , as I understand from your question , you need to update the project data only if it is saved. To do that you can maintain a copy of the actual object which get updated only it is saved like below :
Here we are using angular.copy(), which does a deep copy of the source object.
$scope.original = {name : "xyz"};
$scope.project = angular.copy(original);
//Call this when the user confirms to save , here we are replacing the
//original copy with the latest object that needs to be saved.
$scope.save = function () {
$scope.original = angular.copy($scope.project);
}
//Call this when closing the modal or clicking cancel or when losing
//focus, this will reset the changes to the original copy.
$scope.reset = function () {
$scope.project = angular.copy(original);
}

Related

Angular. Getting wrong data from ngb-panel

I use ngb-accordion in my app. I am trying to get data from every panel but when the first panel is opened click from the second panel returns me wrong data.
Result
I think the problem is the event which raises when input file changes.
Stackblitz Link
I will be glad if someone give me a hint for solving this problem.
There are few things to note in your code.
Your *ngFor is at ngb-accordion which is creating a new accordion for every loop, instead of creating multiple panel within one accordion.
Fix: <ngb-panel *ngFor="let data of datalist; let i = index">
You are using the same label for all three panels, because of which your first panel is opening every time, regardless of which panel you are clicking.
Fix: <label [for]="'image-input-' + i"> and <input ... [id]="'image-input-' + i"
The modal that opens after image selection has no knowledge of which panel it's getting triggered from. So, you have to use your (change)="onFileChange($event, data)" event/function to keep track of selected panel/corresponding data.
Then you can pass that selection from your modal to your processFile(...)
Fix:
export class AppComponent {
...
selectedData: Data;
...
...
onFileChange(event: any, data): void {
...
this.selectedData = data;
}
}
html:
...
<input ... (change)="onFileChange($event, data)>
...
...
<button
...
(click)="processFile(imageInput, selectedData)"
> Done
</button>
Stackblitz Demo

How to dynamically add options to a select element in an Angular component?

I currently have an Angular component that contains a solutions array that I want users to be able to manually alter. I already have a button that allows users to dynamically add to this array, but I'm trying to implement deletion. I want a select box to be displayed that contains all of the solutions, then when the user clicks one of the options and hits "delete solution", it will remove that element from the array.
Currently the html of my component looks as follows:
<div *ngIf="logged" class="solutionsInput">
<div>
New Solution:
<div>
<textarea id="Solution" [(ngModel)]="newSolution" placeholder="None"></textarea>
</div>
</div>
<button class="add-solutions" (click)="addSolutions(defect)">
Add Solution
</button>
<!-- BELOW IS THE PART THAT NEEDS TO BE FIXED -->
<select id = "solutions"></select>
<button class="delete-solutions" (click)="deleteSolutions(defect)">
Delete Solution
</button>
</div>
The typescript of my component looks as follows:
defect.solutions = [] //THIS IS WHAT I WANT TO ALTER
newSolution = "";
addSolutions(defect: Defect): void {
if(this.newSolution !== "") {
this.defectService.getSolutionsHelper(defect).subscribe((currSolutions) => {
//not necessary to see all of this
})
});
}
}
deleteSolutions(defect: Defect): void {
//THIS NEEDS TO BE IMLPEMENTED
}
Are there any ideas for what I should do? Thank you so much in advance for your help!
When I run into these situations, I use a multiselect drop down list. My team uses the Kendo UI for Angular pack, but there are other free choices, like this one:
https://www.npmjs.com/package/ng-multiselect-dropdown
With this approach, you can simply bind your results from the call to this.defectService.getSolutionsHelper to the control (defect.solutions), and then the user can delete individual members from easily selectable items. Since the control is bound to defect.solutions, the control will natively trim the array.
This may work for you. Good luck!

multiple form generated jquery doesn't submit

I'm building a website (e-commerce like) with Django.
At some point I display a list of items and for each item there is a form with submit button Order and Quantity picker.
I've implemented filter function that delete the html code of my items list and rebuild it with the matching items with jquery.
The new forms generated by this function do nothing when the submit button is clicked
Here is a part of the code I use in my function (I use an ajax call to generate a json of matching items and then I display them in the table list) :
$.each(code_json, function(index, value){
var string = "<tr id="+ value.material +"><td>"+ value.manufNo +"</td><form method='POST' action='/add_to_cart/"+value.material+"/"+ value.node+"/{{language}}'><td><input type='submit' class='btn' value='Commander' style='color:blue;'></td><td><input id='qty' type='number' name='qty' class='form-control' value='1'></td></form></tr>";
$("#header").after(string);
});
I know that usually with Django you have to add {% csrf_token %} for each form. However it throw an error page usually when missing and here it doesn't show anything
Edit : I tried to bind an onclick event on the submit button dynamically created. In this I did a $.post in jquery to simulate the submit of the form but nothing happend
$(document).on('click', '.btnStandard', function(event) {
console.log($(this).attr('id'));
$.post('/add_to_cart/'+$(this).attr('id'),
{
qty: $("#qty"+$(this).attr('id')).val()
},function(data,status,xhr){
alert("Data : "+data+", status: "+status+", xhr: "+xhr);
});
It print in console $(this).attr('id') but it doesn't do anything else
Thank you for your help
It doesn't explain why I have this problem but I found a workaround to solve my problem.
Instead of dynamically generate forms, I generate them with template and then I hide them all. Later, I make those I need visible when I need thanks to css.

Angular 2 html form validation

I've created a form using html validations with Angular 2.
I want to to check the sate of the inputs (no empty, correct format, etc) when the user click to a certain button. At the moment I'm doing it as following:
<form id="memberForm" #memberForm="ngForm" >
<input
type="text"
id="MemberName"
required
name="MemberName"
[(ngModel)]="newMember.name">
</form>
<div
[ngClass]="{'button_disabledButton' : !memberForm?.valid}"
(click)="onSubmit(memberForm?.valid, memberForm);">
<span>Next</span>
</div>
With this, I'm only evaluating the input once clicked and focus out. How can I make it hapens when the user click in the "Next" element?
You should make getter/setter solution for your ngModel input.
In the .ts file in the appropriate class put this:
savedVar:string = '';
get variable(): string {
return this.savedVar;
}
set variable(str: string) {
this.savedVar = str;
// do your validation
}
In template use ngModel=variable like this:
<input [(ngModel)]="variable">

How to make grey text on a textbox that disapears in MVC

I am searching for the same answer that was given here:
HTML/CSS Making a textbox with text that is grayed out, and disappears when I click to enter info, how?
But I want to do this in MVC4.
I got the following view:
#using (Html.BeginForm("Kompetens", "KumaAdmin"))
{
<div class="three columns" style="margin-right: 627px;">
<h6>Kompetens</h6>
<div style="width:456px;"> #Html.ListBox("kompetensId", (SelectList)ViewBag.KomId)</div><br/>
<h6>Lägg till kompetens</h6>
<div class="focus">
#Html.EditorFor(mm => mm.KompetensTest)
</div>
<input type="submit" style="margin-right: 205px;" value="Skapa"/><br/><br/>
</div>
}
Since this is my textbox:
#Html.EditorFor(mm => mm.KompetensTest)
I don't know how to apply the "onfocus" & onblur attributes on it like in the link above.
You need to create an Editor Template. Because the Html.EditorFor does not have the "object htmlattributes" parameter to do "new { onfocus = "js here" }".
Over the Views>Shared,
Create a folder called EditorTemplates
Then, you create a view using #model string/whathever this object is. Name the file as you want.
When you put the #model on a view you are specifying that it only accepts this type mas a model.
Inside this view, you create a Html.TextBox (not TextBoxFor) and voila.
On the Html.EditorFor method there is also a way to set which editor template you want to use. Choose the one you created by typing its name like this:
#Html.EditorFor(mm => mm.KompetensTest, "GreyedTemplate")
Code for the View I named as: GreyedTemplate.cshtml
#model string
#Html.TextBox("", Model, new { onfocus = "", onclick="" })
Note that the first parameter is empty. This was done on purpose, because when you use EditorFor(mm => mm.KompetensTest,"GreyedTemplate") it uses KompetensTest as the name of the field automatically.
You want to use the placeholder html attribute (http://www.w3schools.com/tags/att_input_placeholder.asp)
Something like #Html.EditorFor(mm => mm.KompetensTest, new { placeholder = "Text" })
#Gmoliv It worked finaly! I googeld arround and found that the "Editfor" does not have access to html attributes. Although I found "TextBoxFor" which has access to them, so the soloution is:
#Html.TextBoxFor(mm => mm.Profile, new { placeholder = "Ange Profil" })
#Pedro I really tried hard to make it work but the problem was that i could not get the value to be set so it was alwasy empty, i treid setting it in the view and in the templateView and it simply did not take. If you could i would appreciate a full code sample
Thanks alot!