How to get Angular to update the Ui from within the controller - angularjs

I have the following html.
<div ng-controller="CustCtrl">
<table class="table table-condensed">
<thead>
etc.
</thead>
<tbody>
<tr ng-repeat="customer in customers" data-cust-id="{{customer.Id}}">
<td>
<button ng-model="Id" tracking-{{customer.Tracking}} ng-click="startTrackingCustById(customer.Id)">
</button>
</td>
etc.
</tr>
</tbody>
</table>
So the button has a class that is databound to the customer.Tracking value which is either true or false. When the button is clicked the startTrackingCustById() method is successfully called and the customer object in the customers object is successfully changed like customer.Tracking = true.
But the buttons class is not updated. What am I missing?

Look at using ng-class . For a boolean value in scope you would use:
ng-class="{'classNameToAddIfTrue':customer.Tracking}"

In the CustCtrl I wrapped the call that updated the customers array like this
$scope.$apply($scope.customers[i].Tracking = true);
Based on the suggestion in an answer I will link to when I find it that basically said "If you are having trouble updating the view you most likely need to use $scope.$apply
So that get's it to work. Now I need to figure out why and how.

Related

ng-repeat over a div not working

I have used ng-repeat numerous times already in the past, but for some reason I cannot yet understand why it is not on the following situation:
I have an array of objects called registers which I am referencing on the ng-repeat but nothing happens.
I know the array is populated because I have seen it on numerous console.log and because it works if I move the ng-repeat over to the <tbody>
<div ng-repeat = "r in registers">
<!-- START HEADER -->
<tbody class="js-table-sections-header">
<tr>
<td class="text-center">
<i class="fa fa-angle-right"></i>
</td>
<td class="font-w600">Denise Watson</td>
</tr>
</tbody> <!-- END HEADER -->
<tbody>
<tr>
<td class="text-center"></td>
<td>
<!-- Summernote Container -->
<div class="js-summernote-air">
<p>End of air-mode area!</p>
</div>
</td>
</tr>
</tbody>
<!-- END TABLE -->
</div>
I was hoping someone could tell me if there is something I may be ignoring.
Thanks in advance.
I think I just ran into this same problem. It stems from <div> not being a valid elment within a <table>.
I'm guessing that since you have <tbody> there, that there is a <table> tag that was left out of your snippet. <div>s aren't allowed in a table, so the browser moves it before the table. In my situation, doing this, causes the angular scope to change so that there was nothing to iterate over. You can verify this by using the developer tools of your browser.
So, my guess is that you probably want to move the ng-repeat onto the <tbody> or <table> tag.
If you want to use ng-repeat in "div" tag means use "span"
inside div tag. instead of using "table" and its sub attributes..
else use your ng-repeat inside "table" or "thead" or "tr" also
it will iterate rows ...
than only ng-repeat will works.

Ng-click does not work outside of my tr tag AngularJS

I am working on an app and I cant seem to figure out why my ng-click only works inside of my (single) tr tag but as soon as I put it into another tr tag it stop working. Keep in mind it was working before I used the ng-repeat within the first tr tag. Here is what my code looks like, any advice would greatly help!
<table>
<tbody>
<tr>
<td ng-click="commentOpen = !commentOpen">
<div class="iconsize">Comment Closed</div>
</td>
<td ng-click="switchOpen = !switchOpen">
<div class="iconsize">Switch Closed</div>
</td>
</tr>
<tr>
<td>
<div ng-show="commentOpen == true">
<textarea>Comment Open</textarea>
</div>
<div ng-show="switchOpen == true">
<p>Switch On</p>
</div>
</td>
</tr>
</tbody>
</table>
I had the ng-repeat on the tag which was causing my ng-click to not fire. I ended up moving my ng-repeat to the tbody and the ng-click and ng-show started working again.
ngRepeat creates new scopes for its children, it just usually seems like it's accessing the same scope because this new scope inherits from its parent.
Meaning, commentOpen is actually referring to a property on the wrong scope.
Below are three potential ways for you to fix this:
1) controller as, and always refer to the controller you're after by name
2) $parent.commentOpen (Don't do this! It becomes very confusing as you nest)
3) Instead of commentOpen and switchOpen, you can use an Object (e.g. $scope.openControls = { comment: false, switch: false }, and then in the td tags you would write something like ng-click='openControls.comment = !openControls.comment'). This way it's passed inherited by reference (where as a boolean would be by value), and keeps synced.

TrNgGrid display custom column filter

I'm trying to add custom column filter (autocomplete, select ...) but can't find how. I tried to override default filter template with a tr-ng-grid-column-filter attribute on a th, but it does not works. Header is changed somehow (title is not bold anymore) and the new template is not used at all.
Is the tr-ng-grid-column-filter right way to do it at all or there is something else?
Data is sorted, paginated and filtered on the server so it does not have any relation to angular or trnggrid client side filtering & formating. So I just want to display some other input on some columns (e.g. select) instead of default input text rendered by a grid.
I'm using angular 1.2.22 with TrNgGrid 3.0.3
There are some samples floating around the net. Here's one:
http://plnkr.co/edit/I6JJQD?p=preview
<table tr-ng-grid='' items='myItems'>
<thead>
<tr>
<th field-name="name"></th>
<th field-name="computedTagsField" display-format="computedTags:gridItem">
<div>
<div class="tr-ng-title">Tags</div>
<div class="tr-ng-column-filter">
<select class="form-control input-sm" ng-options="tag for tag in [null, 'tennis', 'basketball', 'volley']" ng-model="columnOptions.filter"></select>
</div>
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td field-name="computedTagsField"></td>
</tr>
</tbody>
</table>
I created a directive to implement a custom drop down filter. It, in itself, can be reused on any project, but it will also give you a good working example of how to implement your own custom filter by simply extending TRNG grid.
Tutorial:
http://www.davidcline.info/2015/08/trnggrid-dropdown-column-filter.html
Demo:
http://embed.plnkr.co/w39Xt74pippDajyqUIOD/preview

update ng-repeat item's data inside ng-repeat at click

I’m having my first steps with angularjs framework, and I’m not understanding how I can update my second ng-repeat data inside each of the first’s item ng-repeat only when a user click on it (lazy loading).
<table class="table table-striped table-hover">
<tbody ng-repeat="customer in customers">
<tr>
<td>
<button ng-click="loadInvoices(customer); showDetails = !showDetails;" type="button" class="btn btn-default btn-xs">
</td>
<td>{{customer.ClientId}}</td>
</tr>
<tr ng-show="showDetails">
<td>
<table class="table table-striped table-hover">
<tbody ng-repeat="invoice in invoices">
<tr>
<td>{{invoice.Id}}</td>
</tr>
</tbody>
</table>
</td>
</tr><!--showDetails-->
</tbody><!--.customer-->
</table>
How can I achieve it? Thanks for your help! :)
You can do something like this:
<button ng-click="loadInvoices(customer);" type="button" class="btn btn-default btn- xs">Invoces</button>
and in controller:
$scope.loadInvoices = function(customer) {
if (this.showDetails = !this.showDetails) {
if (!this.invoices) {
// Load invoices for current customer (probably using service)
Customer.loadInvoices(customer.ClientId).then(function(invoices) {
customer.invoices = invoices;
});
}
}
}
By checking if (!this.invoices) {...} (this points to the current customer scope) you make sure that current customer doesn't have loaded invoices yet and you need to load them. On subsequent button clicks customer.invoices is going to be already available and you will not load data again.
I also added an example how using Customer service would fit into this workflow.
View demo: http://plnkr.co/edit/LerBd9ZTPLwhp8JZ6xwU?p=preview
With the ng-controller directive
Simply use the ng-controller directive to tell that a customer scope will be managed by an instance of the controller you pass in.
With a custom directive
Typical design for this: a customer directive with a customer controller which manages the scope of one customer.
The keys are:
ng-repeat creates child scopes
add a directive to attach a controller to each of those scopes
you can now manage each customer scope independently.
The controller, in both cases
This controller will hold loadInvoices which will attach the invoices array to the customer scope.
Why a custom directive ? You will also be able to have a separate template for a customer, specific pre link and post link functions, etc.
Why a customer specific controller since scope inheritance will enable the loadInvoices context to be the customer specific scope ? Extensibility, testability, decoupling.

How can I communicate the state of a data area's pristine state to my scope?

I have the following:
<div data-ng-controller="AdminGridContentController" >
<ng-include src="'/Content/app/admin/partials/grid-content-base.html'"></ng-include>
<ng-include src="'/Content/app/admin/partials/table-content.html'"></ng-include>
</div>
My table include looks like this:
<div ng-form name="page">
{{ page.$pristine }}
<table width="100%" cellspacing="0" class="form table" >
<tr>
<th>ID</th>
<th>Title</th>
</tr>
<tr data-ng-repeat="row in grid.data">
<td>{{ row.contentId }}</td>
<td><input type="text" ng-model="row.title" /></td>
</tr>
</table>
</div>
What I would like to do is to put a button on my grid-content-base that is enabled when the data on the table becomes dirty. I know I can check the state of that data with {{ page.$pristine }} but how can I communicate that back to the grid-content-base.html?
Note that I did try to put everything inside the "ng-form name=page" but there's a problem. On the grid-content-base I have an input select. As soon as I do a select then it makes the page show as dirty.
So I still just want to set something globally when page.$pristine becomes true or false.
There is always an option to do $rootScope.$broadcast and send a message from the table page. This can he handled in the grid page.
Something like this in table controller
$scope.$watch('page.$pristine',function(newValue) {
$rootScope.broadcast("formUpdated",{state:page.$pristine});
});
And something like this in grid controller
$scope.on("formUpdated",function(args) {
//args.state would have the state.
});
Note:$rootScope has to be injected into your controller like you inject $scope

Resources