I have a question about the custom directives in AngularJS.
I have a list in my scope, a list of objects that I use to create tr lines in a table with ng-repeat. The tr line is a custom directive.
I have something like :
<tr custom-tr ng-repeat='element in list'></tr>
As the list is populated via an HTTP call, the list is populated little by little as I have a foreach for each result in my JSON response :
promise.then(function(json) {
json.data.foreach(function(response) {
other_promise.then(funtion(element) {
$scope.list.push(element);
})
})
})
So as the $scope.list gets new elements little by little, my table is supposed to display a new line each time, but actually it waits the end of the foreach before rendering the TR.
When I look at the network tab of the inspector I notice that the HTML template of the tr line is loaded AFTER all the promises.
Is there a way to pre-load the template so I can see the lines in my table before all the promises are done ? Or should I do that differently?
Thanking you in advance,
About the first solution (ng-if), I thought about it some time ago but even if the list is not empty (at least one element), it doesn't mean the html template is loaded as it is loaded at the very end. You can have 35 element, if all the promises are not done it won't work.
I tried the second solution with a similar NPM package and it seems to work now.
Thank you!
Related
I've the following code that displays table of data using ng-repeat, initially the model (array) will have no data, but upon request to an api it receives some data that i'm trying to update my model with, but unfortunately my view is not getting refreshed with model updates though I see that my model contains the new data that was received from api call.
Component code:
app.component('fooList', {
bindings: {
foo: '<'
},
templateUrl: '/app/foo.html',
controllerAs: 'list',
controller: function ($http,$scope) {
let list=this;
list.search=()=>{
$http.get('/ui/foo/'+ list.searchCriteria)
.success(response=>{
list.foo.searchResults=response.searchResults;
})
}
}
});
HTML View:
<tr ng-repeat="foo in ::list.foo.searchResults">
<td>{{::foo.name}}</td>
<td>{{::foo.address}}</td>
</tr>
I've tried the following so far which didn't help
Setting the list.foo.searchResults to undefined initially, which is working fine for the first time, but if I send a different search criteria then again it is not refreshing.
$scope.$apply(), which I hate to do, but tried which is throwing digest in progress error.
Inside the success of $http i've tried the below code
list.foo.searchResults=0;
list.foo.searchResults.push.apply(list.foo.searchResults,response.searchResults);
Created one more child component and replaced my html with the child template and moved the code to populate the array in to that component.
Any help is really appreciated.
You have to remove one time data binding.
<tr ng-repeat="foo in list.foo.searchResults">
<td>{{foo.name}}</td>
<td>{{foo.address}}</td>
</tr>
Try removing :: or can you give a jsfiddle to work on
:: is used to provide one way databinding , please remove it and then try
::if you are using this, even if your model is getting updated it will not allow to update view.
You may try to update in the view section while you are fetching data from service, use $scope.$apply() in your controller section, where your are taking value from service(means storing in array).
or remove :: from your html part because its give to one way binding in angularjs.
When I update my $scope like so in a controller
$scope.item = "Hello";
Then the whole DOM for item seems to be removed and then added again. This seems fine, but if I have a list of items and do
$scope.items = Resource.query();
To update all the items then all of the DOM for items is removed and then re-added, this looks a broken and clumsy - is there anyway around this removing and then adding of the DOM elements when $scope is updated?
This issue is further exasperated if $scope.items and its children are used inside several ng-repeat statements as all of those ng-repeat sections are removed and then re-added.
EDIT
I have read this and feel that this is the issue https://www.binpress.com/tutorial/speeding-up-angular-js-with-simple-optimizations/135
That I have so much "stuff" going on the $digest is just slow. I am working on an example but in the mean time imagine this try of data
{
{
id: 1,
name: "name1",
something: {
id: 10,
name: "something10"
else: {
id: 15,
name: "else15"
}
}
}
}
But there are 20 such objects all with nested objects - this appears to be the issue. That there are so many objects being parsed and bound to the DOM that the $watchers are just taking a long time to go over everything.
EDIT 2
I made this demo, perhaps I am using resource wrong? http://plnkr.co/edit/QOickL0Dyi8jmuvG9mzN
But the items are replaced every 5 seconds, on replace they all disappear and then reappear. This is the issue I am having.
This is a common problem when using ng-repeat. In order to track the objects the directive itself by default adds additional property to every object in the array ($$hashkey). And whenever new object is added or removed from the array the same is happening for it's associating DOM element. When the array is replaced with new array (ex. returned from the server) as you mentioned all the DOM elements representing objects from the previous array are removed and replaced with new DOM elements for the new objects. This is simply because the new objects in the array does not have the $$hashkey property even though they may be semantically the same.
A solution for this common problem came in Angular 1.2 version with the track by clause. You can find more detailed explanation about it in this post.
In your case the objects in the array have an 'id' property which it's a good candidate for tracking them if you can guarantee it's uniqueness. So using ng-repeat like this will improve it's performance.
<div ng-repeat="item in items track by item.id">
<!-- tpl -->
</div>
If you'll take a look at $resource documentation, you'll see that the correct way of manipulation with data - to use callback function
Instead of
$scope.items = Resource.query();
Try
Resource.query(function(data) {
$scope.items = data;
});
Did you try ngCloak directive? https://docs.angularjs.org/api/ng/directive/ngCloak
I'm trying have an ng-repeat which contains divs that include a CKEDITOR instance. The list is sortable by a few different properties. However, when the list is resorted, the CKEDITORs break because they don't support being moved around in the DOM. The only solution I can think of is to destroy the CKEDITOR instance before sort, and recreate them after. Is there any events on ng-repeat that can accomplish this?
plunkr here: http://plnkr.co/edit/tGnTdzvRl7xhEX7zYq4c?p=preview
Was able to get this fixed eventually. Pretty easy now that I think about, but took me 3 days to realize it. Instead of trying to listen for events on ng-repeat, catch the event that is causing the data to be modified. For me, it was jquery UI sortable (angular directive.) In the controller add $scope.$broadcast('unbind-ckeditor') before the change, and then $scope.$broadcast('rebind-ckeditor') after it. In your angularjs directive for ckeditor, call scope.$on('unbind-ckeditor', function() {instance.destroy /* instance is your ckeditor instance*/}); and then reload it in the rebind. Hope this helps someone.
Edit:
Make sure the $on('unbind-ckeditor'... is only added to the scope once, or multiple sorts will throw exceptions.
The solution for me:
use divArea and not iframe, this will work with ng-repeat when the list changed.
using other textarea (hidden) and monitoring it as the ng-sortable+ckeditor broke the order of CKEditors.
in ng-sortable (who make the change in the list) I was added this code:
$rootScope.$broadcast('rebind-ckeditor'); console.log("rebind")
and in CKEDITOR directive:
scope.$on('rebind-ckeditor', function () {
if (jQuery(element).data("loaded")) {
console.log("rebind");
CKEDITOR.instances[element[0].id].destroy();
jQuery(element).data("loaded", null);
element[0].value = $("textarea", element.parent())[1].value; // update manualy from the hidden textarea
onLoad(); //recreate CKE after textarea changed.
}
})
I really dislike how angular-ui is documented. Sometimes they really don't explain a lot. This is the documentation to sortable-ui: https://github.com/angular-ui/ui-sortable
First, I cannot pass in options.
$scope.sortableOptions = {
cursor:"move"
};
I also changed "move" to "pointer" or "crosshair". Nothing happens.
Second, I need to update the backend by the new order of which the user has sorted. I am not a great javascripter at all (more of a back end developer). The only order-related js function I can find is indexof(). Then it gets very complicated because I need to iterate through all elements and find the new order since the user has rearranged all the elements.
Is there an easier way to get the current order of the list whenever the sortable directive is updated?
I created a demo on plunker (since it allows me to add extra libraries)
http://plnkr.co/edit/uNErHgKL3ohNyFhgFpag?p=preview
Again the cursor part is not working, and I have no idea how to get the order of these items.
I see there are methods on the Sortable UI page...I'm new to angularJS. I just couldn't figure out how to call these methods within AngularJS code.
Seralize method/toArray might not be a good idea..The actual data I'm dealing with does not look like ["one", "two", "three"]. It's more like:
[{"id":"5","article_name":"New Article
Title","article_order":"1","article_author":"Author","article_body":"Start typing your
article here!","is_visible":"1","created_date":"2013-10-27
05:37:38","edit_date":null,"magazineID":"7"},
{"id":"13","article_name":"New Article
Title","article_order":"2","article_author":"Author","article_body":"Start typing your
article here!","is_visible":"1","created_date":"2013-10-27
05:45:10","edit_date":null,"magazineID":"7"}]
If you guys look into this data stream..there is one attribute called article_order. This is the attribute (database column) I am trying to modify...
Read the jQuery UI Sortable docs. There are lots of events you can bind to and methods for serializing the sorted elements. Within the event callbacks you want to use you can make ajax calls to server with updated data
This angular module is simply a wrapper for jQuery UI Sortable.
Create a demo in jsfiddle or plunker that shows the problems you are having
If you use my new sortable Angular.js directive, you would do it like this:
$scope.items = [{"id":"5","article_name":"New Article
Title","article_order":"1","article_author":"Author","article_body":"Start typing
your article here!","is_visible":"1","created_date":"2013-10-27
05:37:38","edit_date":null,"magazineID":"7"},
{"id":"13","article_name":"New Article
Title","article_order":"2","article_author":"Author","article_body":"Start typing
your article here!","is_visible":"1","created_date":"2013-10-27
05:45:10","edit_date":null,"magazineID":"7"}];
$scope.onChange(fromIdx, toIdx) {
$scope.items[fromIdx].article_order = toIdx;
$scope.items[toIdx].article_order = fromIdx;
// OR
// var temp = $scope.items[fromIdx].article_order;
// $scope.items[fromIdx].article_order = $scope.items[toIdx].article_order;
// $scope.items[toIdx].article_order = temp;
}
HTML:
<ul ng-sortable="items"
ng-sortable-on-change="onChange">
<li ng-repeat="item in items" class="sortable-element" ng-style="{backgroundColor: item.color}">
{{item.name}}, {{item.profession}}
</li>
</ul>
See demo + documentation here:
https://github.com/schartier/angular-sortable
https://github.com/schartier/angular-sortable-demo
I guess I got into an issue similar to you. If we subscribe the update callback we don't get the latest order of items. But using the stop event handler helped me. While using angular ui sortable, we get the model updated with the latest order in the stop event handler. You can post this data to backend or wherever you want to store..Hope this helps...:)
You can refer the jquery ui sortable documentation for stop here
http://api.jqueryui.com/sortable/#event-stop
I need to perform loop within loop in Angularjs to a directive.
The first looping is getting links from json file and the second loop is looping the data from
previous looped links.
But in the second loop, it returns the same result.
I created a testing plunker. You can see what's wrong here.
Thanks
When you iterate over jsonLinks you are overwriting $scope.feeds in each iteration. In order to make it work I suggest to replace your loop with: Plunker
angular.forEach($scope.jsonLinks, function(jsonLink) {
$scope.jsonLink = jsonLink.link;
dataService.jsonp($scope.jsonLink).then(function(response){
jsonLink.feeds = response.data.feed;
});
});
I had to clear my old answer because it didn't solve the problem. The major issue here is that you are making async calls inside a loop, with no reference to the original jsonLink object where you can store the feeds. You will need a reference to the corresponding parent object for the feed. probably locate it via id and then add the feed to it.