ng-click not updating UI automatically - javascript

I'm having trouble displaying a <td> in my ng-repeat produced table.
The last <td> in the table should be displayed based on a condition, if vm.editDeleteButtonsVisibile (Using the Controller-As syntax) is true. By default this value is false but when I change it to true, the UI doesn't reflect the change.
This is how my <tbody> looks:
<tbody ng-controller="EditProductController as vm"> <!-- This ng-controller is somewhere else -->
<tr ng-repeat="product in vm.products">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
<td width="15%" ng-if="vm.editDeleteButtonsVisible"> <!-- Error Here -->
<button type="button" class="btn btn-default" ng-click="vm.editItem(product.productCode)">Edit</button>
<button type="button" class="btn btn-danger" ng-click="vm.deleteItem(product.productCode)">Delete</button>
</td>
</tr>
</tbody>
This is the controller where I'm updating the value vm.editDeleteButtonsVisible:
var EditProductController = function() {
vm.editDeleteButtonsVisible = false; // Default value
vm.showEditDeleteButtons = function() {
vm.editDeleteButtonsVisible = true;
console.log(vm.editDeleteButtonsVisible); // Prints true, so value was updated
}
}
This is how vm.showEditDeleteButtons() is being called, from another view:
<div id="editProductBtn" ng-controller="EditProductController as pvm">
<button type="button" class="btn btn-default" ng-click="pvm.showEditDeleteButtons()">Edit Products</button>
</div>
Could the problem be that I'm already declaring ng-controller="EditProductController"? I declare it once in it's main view and another time in a separate view.

Your problem is that you are defining the controller twice. Angular controllers are not singletons. So you are creating two instances of it. You are updating a variable in one of them and looking for it in the other. This won't work.
<div id="editProductBtn" ng-controller="EditProductController as pvm">
and
<tbody ng-controller="EditProductController as vm">
Creates two instances.
What you need to do is either use one controller or create a service (which IS a singleton) and hold the variable in the service.

Related

Get values of dynamically named inputs in Angular

In my fees-list.component.html template, I have something like the following:
<div class="container">
<div class="row" *ngFor="let meta of fees">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Amount owing (AUD)</th>
<th>Fee type</th>
<th>Title</th>
<th>Amount to pay (AUD)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let fee of meta.fee">
<td>{{ fee.balance }}</td>
<td>{{ fee.type.desc }}</td>
<td>{{ fee.title }}</td>
<td><input id="{{ fee.id }}" type="text" name="toPay" value="{{ fee.balance }}"></td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<div class="col-md-4"><strong>Total to pay: </strong><input type="text" name="total" value="{{ meta.total_sum }}"> </div>
</div>
</div>
</div>
The requirement for my interface is twofold. If users change the value of an individual fee (fee.balance), the value in the total text input field should be updated.
Secondly, if the total input field is updated, I need to update the value(s) in the individual fees accordingly (reducing those by the appropriate amount from oldest fee to newest fee).
My question is, how do I do binding for these input fields which are dynamically generated, though they do have unique ids (id="{{ fee.id }})? I cannot work out how to target an individual fee field in my typescript file.
Use two way binding with ngModel
<input [name]="fee.id" type="text" [(ngModel)]="fee.balance">
Now when the user types in the textbox it will update the value in the array directly.
Also you should not be using {{}} binding on your attributes like that, use attribute binding with the [box] syntax.
<input type="text" name="total" [value]="total_sum">
You can calculate the total in your TypeScript like
get total_sum() {
return meta.fees.reduce((total, fee) => total + fee.balance, 0);
}
One approach ( preferred) is to create the controls dynamically.
your typescript file :
constructor(){
this.formGroup = new FormGroup();
const controls = meta.fee.map((fee)=>{
this.formGroup.addControl(fee.id, new FormControl(fee.balance))
});
// now you have a formGroup with all the controls and their value are initialized, you just need to use it in your template
}
your template :
<tbody>
<tr *ngFor="let fee of meta.fee">
<td>{{ fee.balance }}</td>
<td>{{ fee.type.desc }}</td>
<td>{{ fee.title }}</td>
<td><input id="{{ fee.id }}" [formControl]="formGroup.get(fee.id)" type="text" name="toPay"></td>
</tr>
</tbody>
So obviously, after this, you have the full power to do anything, for example, if you want to update a specific one of them:
updateValue(){
this.formGroup.get('oneOfThoseFeeIds').setValue('new value')
}
NOTE :
I haven't done Angular for a while and don't remember the exact syntaxes, but I hope this gives you a path
Can't you just use a service for that? I guess this is a way to go here, since you should try keeping your components as small as possible and communicate them by services.
Please, have a look at this chapter in angular documentation: https://angular.io/tutorial/toh-pt4

Angular.js - Redirection is cleaning my $scope(?)

I have an $ngController that is being used in two views. The first view has an <table> with ng-repeat that lists all my data from the db.
I get the selected object form the table by using get($index) and set it to a $scope variable.
The problem is that when i cannot use this same $scope variable on the other view, because its value is undefined. Both views share the same ng-controller.
My Question is: is the redirection cleaning my $scope? Is there any way i can share this data between pages since my application isn't single page?
Things i tried:
1 - Share data through a factory
2 - Using $rootScope
First View - Table with ng-repeat
<table class="table table-striped">
<thead>
<tr>
<th>CNPJ</th>
<th>Razão Social</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="company in companies | filter:{companyName:searchString}">
<td> {{ company.cnpj }}</td>
<td> {{ company.companyName }}</td>
<td>
Visualizar
<button type="button" class="btn btn-warning btn-xs">Editar</button>
<button type="button" class="btn btn-danger btn-xs">Deletar</button>
</td>
</tr>
</tbody>
</table>
Controller
angular.module("distCad").controller("adminCtlr", function($scope, $http, config, $window, $cacheFactory){
$scope.searchTerm = null;
$scope.hideSearcAlert = true;
$scope.companies = [];
$scope.cache = $cacheFactory('companyCache');
$scope.expireLogin = function(){
localStorage.removeItem("type");
$window.location.href = "/";
}
$scope.getResults = function(){
$scope.searchTerm = true;
$http.get("/api/companies").then(
function successCallback(response){
$scope.companies = response.data;
},
function errorCallback(response){
console.log("aaaa" + response);
}
);
}
$scope.getCompany = function(){
return $scope.cache.get("company");
}
$scope.setCompanyFromTable = function(index){
$scope.cache.put("company", $scope.companies[index]);
}
});
Destination View - Part where i am testing
<div class="container" ng-init="getCompany()">
<div class="row">
<div class="col-md-12">
<h2>Dados da empresa {{cache.get("company").companyName}}</h2>

AngularJS - cannot reference $index inside ng-model using a directive

I am currently facing an issue whereby I would like to generate a dynamic number of input forms and also name them dynamically (as defined by a JSON) so that I can reference them separately.
For example, if my JSON object had three items in it, I would generate three input boxes with ng-model="1", ng-model="2" and ng-model="3" respectively. In real life the ID's will come from the JSON itself.
I'm currently using ng-repeat to generate the forms in a table;
<tr ng-repeat="x in names track by $index">
<td>{{ $index }}</td> <!-- this outputs fine -->
<td>{{ x.Description }}</td>
<td>{{ x.Metric }}</td>
</tr>
And using a directive & the $compile function to dynamically generate an input form with a unique ng-model name.
app.directive("outDynamic", function($compile){
return{
link: function(scope, element, attrs){
var template = "<input type='number' ng-model='" + scope.ray[attrs.element1] + "'>";
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
}
} });
However: none of the following works (when nested in the ng-repeat)
<!-- None of these work -->
<td out-dynamic element1=$index></td>
<td out-dynamic element1="$index"></td>
<td> <input type='number' ng-model="array[$index]"> </td>
<td> <input type='number' ng-model="array['$index']"> </td>
Summary of issue;
Every time I try and reference the $index tracker, I get an
undefined error in my directive code.
You should use {{}} to assign $index value for attribute. so use
element1={{$index}} instead of element1=$index
<tr ng-repeat="x in names track by $index">
<td out-dynamic element1={{$index}}></td>
<td out-dynamic element1="{{$index}}"></td>
</tr>
I guess your scope.arr is perfect.
PLUNKER DEMO

how to get the unique id of array element on click through angularjs from firebase database

HTML:
<div ng-controller="getIdController">
<div class="container">
<div class="row">
<div class="col-md-6">
<hr />
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in userData">
<td> {{ user.name }} </td>
<td> {{ user.age }} </td>
<td> {{ user.gender }} </td>
<td><button class="btn btn-warning" ng-click="getObjectId()">Get Id</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
App Js:
var crudApp = angular.module('crudApp', ['firebase']);
crudApp.controller('getIdController', ['$scope', '$firebaseArray', function($scope, $firebaseArray){
var ref = new Firebase('https://blistering-fire-4719.firebaseio.com/users');
$scope.userData = $firebaseArray(ref);
$scope.getObjectId = function(){
$scope.userData.$loaded()
.then(function(id){
var obj = id;
console.log(obj);
});
}
}]);
Explanation:
Ok here In firebase i have User table and it has 3 objects each with unique id. Currently I am trying to extract the unique id for object on click of Get Id button in html. But the problem is if I do console.log(id) the function getObjectId() gives me //Array [ Object, Object, Object ].. clicking on object takes me to its data and it has $id as -K9jyc0K9i28h53d23Uw..
Is there any way to get the unique id for a particular element on clicking the button
I want this unique id to be shown for element on clicking a particular element/object in html.
Please help.
If your user object has the id property on it, you can just refer directly to that. You can pass it as a parameter from your ng-click directive:
<button ng-click="selectUser(user)">
and in your controller:
$scope.selectUser = function(user) {
console.log(user); // should emit the user object. is id a property?
}
If all of your objects use the same property, you can have a single function:
$scope.showObjectId = function(object) {
alert(object.id);
}
You can show the id in the html like this (within the ng-repeat):
<td> {{ user.$id }} </td>
Or send it to the ng-click function (within the ng-repeat):
<button class="btn btn-warning" ng-click="getObjectId(user.$id)">Get Id</button> //Just send the id to the function
<button class="btn btn-warning" ng-click="getObjectId(user)">Get Id</button> //Send the etire user object to the function
And in you javascript you can use it like this:
$scope.getObjectId = function(user){
//If you only send the id to this function you can get the entire user object like this (it gives a $firebaseObject i think):
var user = $scope.userData.$getRecord(user);
//Here you can do anything with the user
//Update user data
$scope.UserData.$save(user);
//Remove user
$scope.UserData.$remove(user);
}

AngularJS on ng-dblclick change input[text]

I have a table that is populated with ngRepeat and I have a input[text] where you can filter the table.
This works fine but now I came up with the idea to have the possibility to double-click on an element in the table and add the text to the search input[text] so the filter is applied straight when you double-click on the text.
Unfortunately it does not work as expected.
I have done this:
<input type="text" placeholder="Search..." data-ng-model="userinput" />
<p data-ng-dblclick="userinput='query'">Double click to use query to search</p>
And in the ngRepeat I use the ng-model "userinput" to filter but the value of the text input is not changing.
I also tried to specify the model "userinput" as variable in the controller and then change it per function but it is not working.
Is there something I'm missing?
Normally I would change the variable in the controller and it should automatically change the text input since it uses this variable as model. Then with this it should change the filter too but nothing happens.
WORKING
Code ngRepeat
<tr data-ng-repeat="dat in data | filter: userInput | filter: tsSelect | filter: advSelect | filter: checkedFilter | orderBy: ['client', 'ssrstatus'] | limitTo: totalDisplay" id="{{ dat.bannerid }}"> <!-- | unique: 'bannerid' | filter: errorSelect| -->
<td>
<input type="checkbox" id="checked" data-ng-model="dat.checked" data-ng-change="updateCheckedStatus(dat._id['$id'], dat.checked)">
<label for="checked">Checked</label>
</td>
<td data-ng-dblclick="search(dat.clientid)">{{ dat.clientid }}</td>
<td data-ng-dblclick="search(dat.client)" class="txtleft">{{ dat.client }}</td>
<td data-ng-dblclick="search(dat.tsengineer)">{{ dat.tsengineer }}</td>
<td data-ng-dblclick="search(dat.bannerid)">{{ dat.bannerid }}</td>
<td data-ng-dblclick="search(dat.bannertype)" class="txtleft">{{ dat.bannertype }}</td>
<td data-ng-dblclick="search(dat.width + 'x' + dat.height)">{{ dat.width == 0 ? 0 : dat.width - 50 }}x{{ dat.height == 0 ? 0 : dat.height - 50 }}</td>
<td data-ng-dblclick="search(dat.ssrstatus)" class="txtleft">{{ dat.ssrstatus }}</td>
<td data-ng-dblclick="search(dat.datebegin)">{{ dat.datebegin }}</td>
<td data-ng-dblclick="search(dat.dateupdated)">{{ dat.dateupdated }}</td>
<td>
<button class="preview {{ dat.bannerid }}" data-ng-click="showPreview(dat.bannerid, dat.clicktotestbanner, dat.width, dat.height)"></button>
</td>
<!-- <td id="{{ dat.bannerid }}" class="banner-preview"></td> -->
Controller
$scope.userInput = "";
$scope.search = function(query){
$scope.userInput = query;
}
I think it's because of your userinput='query' evaluated inside ng-repeat.
Let's name your outer scope "scopeA". The ng-model="userinput" of the search input would be referencing scopeA.userinput.
As we know, a new scope is created for every ng-repeat items. If you run userinput='query' in one of these scopes (name it scopeB), you would be assigning 'query' to scopeB.userinput instead of scopeA.userinput.
In this situation, scopeB is likely to be a child of scopeA. If you use angular-batarang Chrome extension to have a look at the scope tree, you would find both scopes to have userinput field.
One solution would be to use a function to assigning value to userinput instead of ng-dblclick expression. Like:
<p data-ng-dblclick="setUserinput('query')">Double click to use query to search</p>
And add a function setUserinput to your scope:
$scope.setUserinput = function(newValue) {
$scope.userinput = newValue;
}

Categories

Resources