When i display more than 100 products with ngfor page is very slow.
Is there any problem about performance on browser if call a method inside ngFor with one hundred items or set a lot of elements inside ngFor for one item?
here is my typescript code that i get some products from server with http post request
public products:any = [] ;
constructor(
private setRouter: SetRouterService,
private header :HeaderService,
private cdr: ChangeDetectorRef,
private router : Router,
private crypto:EncryptDecryptService ,
private dataservices: DataService ,
private Httpservice :HttpService ,
private route: ActivatedRoute
) {
this.dataservices.update_loader(true);
this.dataservices.create_object_request( 'products', { 'type': 'default', 'number_click': 1 } );
this.my_products = this.Httpservice.Http_Post( this.dataservices.object_request ) // make request ......
.subscribe( // take success
data => {
if ( data['status'] == 'product' ) {
this.products = data['data']['products'];
this.build_pages_link( data['data']['pages_details']);
this.dataservices.update_loader(false);
}
},
error => console.log( error['data'] ) // take error .....
);
}
here is my html code that i display products with ngFor
<div class="width_products products-animation " *ngFor="let product of products ; let i = index" [ngClass]="{ 'width_products_open_menu':header.status_menu }" >
<div class="each_width_product" >
<div class="title_products more_detail_product" (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })">
{{product.product_title }}
</div>
<div class="dropdown_products">
<span class="glyphicon glyphicon-chevron-down"></span>
</div>
<div class="date_products">
<span class='glyphicon glyphicon-time'></span> {{product.product_date}}
</div>
<div class="image_profile_company " (click)="set_router({ path:product['company'].company_name , data: product['company'].company_id , relative:true })">
<img class="image_prof_admin" src="../../assets/images/products_image/{{product['company'].company_image}}">
</div>
<div class="image_product_primary " (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })">
<img class="image_product img_primary_product{{product.product_id}}" src="../../assets/images/products_image/{{product.product_image}}">
</div>
<div class="wish_list hover_all_wish notCloseDropdawnFavorite notCloseDropdawnCard" (mouseleave)="mouseLeave_wish( product )" (mouseenter) ="mouseHover_wish( product ,i )" id="wish_list{{product.product_id}}" (click)="header.add_wish_list( product )" [ngClass]="{'wish_list_hover': product_properties.index_product == i }">
</div>
<div class="about_wish" >
<div class="wish_list_write">
<div class="plus_icon">{{dataservices.language.add_wishlist}}</div>
</div>
<div class="wish_list_icon">
<button mat-mini-fab class="button_heart" >
<div *ngIf="header.wish_properties.wishList.length > 0; else emtyWish " >
<div *ngIf="check_wish( product ) ; else notinwish">
<mat-icon>favorite</mat-icon>
</div>
<ng-template #notinwish >
<mat-icon class="notCloseDropdawnFavorite notCloseDropdawnCard" [ngClass]="{'hide_border_heart' : product_properties.hover_wish_list && product_properties.index_product == i } ">favorite_border</mat-icon>
<mat-icon class="hearts_div_hover notCloseDropdawnFavorite notCloseDropdawnCard" [ngClass]="{'show_full_heart' : product_properties.hover_wish_list && product_properties.index_product == i } ">favorite</mat-icon>
</ng-template>
</div>
<ng-template #emtyWish>
<mat-icon [ngClass]="{'hide_border_heart' : product_properties.hover_wish_list && product_properties.index_product == i } ">favorite_border</mat-icon>
<mat-icon class="hearts_div_hover" [ngClass]="{'show_full_heart' : product_properties.hover_wish_list && product_properties.index_product == i } ">favorite</mat-icon>
</ng-template>
</button>
</div>
</div>
<div class="footer_products">
<span matTooltip="Views!">
<div class="button_footer_products">
<span class="glyphicon glyphicon-eye-open icon_eye"></span>
<div class="both_write ">
12889
</div>
</div>
</span>
<span matTooltip="Add to your card" class="notCloseDropdawnCard notCloseDropdawnFavorite " (click)="header.add_cart_list( product )">
<div class="button_footer_products">
<span class="glyphicon glyphicon-plus icon_eye notCloseDropdawnCard notCloseDropdawnFavorite" *ngIf="!check_cart( product ) ; else incart "></span>
<ng-template #incart>
<span class="glyphicon glyphicon glyphicon-ok icon_eye notCloseDropdawnCard notCloseDropdawnFavorite"></span>
</ng-template>
<div class="both_write ">
Cart
</div>
</div>
</span>
<span matTooltip="See Details!">
<div (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })" class="button_footer_products more_detail_product" id="<?php echo $result_all_products['id'] ?>">
<span class=" glyphicon glyphicon-option-horizontal icon_eye"></span>
<div class="both_write ">
More
</div>
</div>
</span>
</div>
<div class="prise_products">
Price:
<div class="both_prise_products prise_primary">
<del>$2500</del>
</div>
<div class="both_prise_products prise_secondary">
$3500
</div>
</div>
<div class="plus_height"></div>
</div>
</div>
what should i do to solve it ? thank you.
the problem is the code of your function is execute every time that you do something in the template (mouse moved, for example). The lifecycle of the template is causing this,add to that the fact that your are creating a instance of that function for every item in your array. I suggest you try to
1- change the change detection strategy of component
Component({
...,
changeDetection: ChangeDetectionStrategy.OnPush
})
MyComponent {}
2- reduce the number of call from your template to methods in your component (try to use pipe instead if can be applied)
Related
In my code, I'm displaying a list of elements on the main page. When you click one of those elements, a side-pannel opens and you are able to see the details of that element. Whenever the page first opens I get the error below. The code doesn't crash or anything, but I don't get why the error keeps appearing. What should I do to fix it?
StickerListComponent.html:167 ;
<fuse-sidebar class="sidebar centra details-sidebar fuse-white" name="sticker-preview-sidebar" position="right">
<sticker-preview [StickerData]="previewStickerData"></sticker-preview>
</fuse-sidebar>
sticker-preview-component html ;
<div class="h-100-p" fxLayout="column" fusePerfectScrollbar>
<div class="group mt-32">
<div fxLayoutAlign="start center">
<header class="purple-fg" style="font-size:18.72px"><strong>Sticker Data:</strong></header>
</div>
<p>
<span id="sticker" contenteditable [textContent]="_stickerData?.StickerData" (input)="onStickerDataChange($event.target.innerHTML)">
{{_stickerData?.StickerData}}
</span>
</p>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<button mat-button matRipple class="purple-500 fuse-white-fg mr-12" (click)="imageSource()"> Display </button>
</div>
<div>
<img [src]="url" *ngIf="hidden" />
</div>
</div>
sticker-preview-component ts:
export class StickerPreviewComponent implements OnInit {
EditIndex: number;
public _stickerData: IStickerData = {};
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
Filter: IFilter = {};
hidden = false;
url;
#Input()
set StickerData(prm: IStickerData) {
if (this._stickerData != prm) {
this._stickerData = prm;
}
33 this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;
}
get StickerData(): IStickerData {
return this._stickerData;
}
dataSource: MatTableDataSource<IStickerData>;
ngOnInit() {
this._productionService.getStickerDataList(this.Filter)
.subscribe((response: any) => this._stickerData = response);
}
imageSource(): void{
this.hidden = !this.hidden;
}
it seems the _stickerData is undefined at the time when it assigns to the url, try to check whether the _stickerData has the proper value and then assign it.
if(this._stickerData)
this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;
my controller :
.controller('BlogController', function(blogFactory, $routeParams, $scope){
var that=this;
stat=false;
this.checkbookmark = function(bId){
console.log(bId)
blogFactory.checkBookmark(bId, function(response){
console.log(response)
if(response == "bookmarked"){
that.stat = true;
}
else{
that.stat = false;
}
})
}
my html code :
<div class="container" ng-controller="BlogController as blogCtrl">
<div class="row" ng-repeat="chunk in blogCtrl.blogs | filter: filter_name | orderBy:'-created_at' | groupBy: 3">
<div class="outerbox1 col-sm-4" ng-repeat="blog in chunk" >
<div class="innerbox3" ng-init="blogCtrl.checkbookmark(blog._id)">
<br>
<div> > READ MORE
<a ng-if="blogCtrl.stat" ng-href="#" ng-click="blogCtrl.removebookmark(blog._id)" class="glyphicon glyphicon-heart pull-right">{{blogCtrl.stat}}</a>
<a ng-if="!blogCtrl.stat" ng-href="#" ng-click="blogCtrl.addbookmark(blog._id)" class="glyphicon glyphicon-heart-empty pull-right">{{blogCtrl.stat}}</a>
</div>
</div>
</div>
</div>
</div>
service factory :
factory.checkBookmark = function(info, callback){
$http({
url:'api/check_bookmark_blog',
method:'POST',
headers:{'x-access-token': token},
params:{'user_id': userid},
data:{'blog_id':info}
}).success(function(data){
console.log(data);
callback(data)
})
}
I want to show glyphicon based on the value of stat
I have 6 blogs, first 3 are bookmarked and next 3 are not.
The problem i'm getting is that the stat value is always set according to the last bookmark and hence it is always false / true (based on the condition of last blog).
How to resolve this issue and set glyphicon respective to value of stat for each blog?
So there are multiple ways of implementing it.
You can either store the true/false values in either an array or you can run a function every time which returns true/false.
Using function which returns true/false values:
Modify your checkbookmark function to return true/false values as follows,
Controller code:
.controller('BlogController', function (blogFactory, $routeParams, $scope) {
this.checkbookmark = function (bId) {
blogFactory.checkBookmark(bId, function (response) {
if(response == "bookmarked"){
return true; // returns true if bookmarked
} else {
return false; // returns false if not bookmarked
}
});
};
});
HTML code:
<div class="container" ng-controller="BlogController as blogCtrl">
<div class="row" ng-repeat="chunk in blogCtrl.blogs | filter: filter_name | orderBy:'-created_at' | groupBy: 3 track by $index">
<div class="outerbox1 col-sm-4" ng-repeat="blog in chunk track by $index" >
<!-- removed ng-init here -->
<div class="innerbox3">
<br>
<div> > READ MORE
<a ng-if="blogCtrl.checkbookmark(blog._id)" ng-href="#" ng-click="blogCtrl.removebookmark(blog._id)" class="glyphicon glyphicon-heart pull-right">{{blogCtrl.stat}}</a>
<a ng-if="!blogCtrl.checkbookmark(blog._id)" ng-href="#" ng-click="blogCtrl.addbookmark(blog._id)" class="glyphicon glyphicon-heart-empty pull-right">{{blogCtrl.stat}}</a>
</div>
</div>
</div>
</div>
</div>
Using an array:
Store the true/false values in an array and access them in html using
track by $index in your ng-repeat clause.
Controller code:
.controller('BlogController', function (blogFactory, $routeParams, $scope) {
this.stat = []; // initializing array
this.checkbookmark = function (bId) {
blogFactory.checkBookmark(bId, function (response) {
if(response == "bookmarked"){
this.stat.push(true);
} else {
this.stat.push(false);
}
});
};
});
HTML code:
<div class="container" ng-controller="BlogController as blogCtrl">
<div class="row" ng-repeat="chunk in blogCtrl.blogs | filter: filter_name | orderBy:'-created_at' | groupBy: 3 track by $index">
<!-- note the usage of track by $index in ng-repeat -->
<div class="outerbox1 col-sm-4" ng-repeat="blog in chunk track by $index" >
<div class="innerbox3" ng-init="blogCtrl.checkbookmark(blog._id)">
<br>
<div> > READ MORE
<a ng-if="blogCtrl.stat[$index]" ng-href="#" ng-click="blogCtrl.removebookmark(blog._id)" class="glyphicon glyphicon-heart pull-right">{{blogCtrl.stat}}</a>
<a ng-if="!blogCtrl.stat[$index]" ng-href="#" ng-click="blogCtrl.addbookmark(blog._id)" class="glyphicon glyphicon-heart-empty pull-right">{{blogCtrl.stat}}</a>
</div>
</div>
</div>
</div>
</div>
Hope this solves the problem.
I exactly don't know which implementation is better or any other implementations for the same. May be other folks in stackoverflow could suggest.
controller:
.controller('BlogController', function(blogFactory, $routeParams, $scope){
var that=this;
stat=false;
this.checkbookmark = function(bId){
console.log(bId)
blogFactory.checkBookmark(bId, function(response){
console.log(response)
if(response == "bookmarked"){
that.stat = true;
}
else{
that.stat = false;
}
})
}
html code:
<div class="container" ng-controller="BlogController as blogCtrl">
<div class="row" ng-repeat="chunk in blogCtrl.blogs | filter: filter_name | orderBy:'-created_at' | groupBy: 3">
<div class="outerbox1 col-sm-4" ng-repeat="blog in chunk" >
<div class="innerbox3" ng-init="blogCtrl.checkbookmark(blog._id)">
<br>
<div> > READ MORE
<a ng-if="blogCtrl.stat" ng-href="#" ng-click="blogCtrl.removebookmark(blog._id)" class="glyphicon glyphicon-heart pull-right">{{blogCtrl.stat}}</a>
<a ng-if="!blogCtrl.stat" ng-href="#" ng-click="blogCtrl.addbookmark(blog._id)" class="glyphicon glyphicon-heart-empty pull-right">{{blogCtrl.stat}}</a>
</div>
</div>
</div>
</div>
</div>
I want to show glyphicon based on the value of stat
I have 6 blogs, first 3 are bookmarked and next 3 are not.
The problem i'm getting is that the stat value is always set according to the last bookmark and hence it is always false / true (based on the condition of last blog).
How to resolve this?
Instead of setting the stat property on the controller you should set the property on the blog object (it obviously belongs to the object)
Replace your checkbookmark function with this:
this.checkbookmark = function(blog){ //pass the entire blog, not just the id
blogFactory.checkBookmark(blog._id, function(response){
console.log(response)
if(response == "bookmarked"){
blog.stat = true; //set the property on blog instead of the controller
}
else{
blog.stat = false;
}
})
}
And then use it like this:
<div class="innerbox3" ng-init="blogCtrl.checkbookmark(blog)">
<br>
<div> > READ MORE
<a ng-if="blog.stat" ng-href="#" ng-click="blogCtrl.removebookmark(blog._id)" class="glyphicon glyphicon-heart pull-right">{{blog.stat}}</a>
<a ng-if="!blog.stat" ng-href="#" ng-click="blogCtrl.addbookmark(blog._id)" class="glyphicon glyphicon-heart-empty pull-right">{{blog.stat}}</a>
</div>
</div>
You will need to make similar changed to your add and removebookmark functions
I have set up page that shows different products. I've made a search box with a ng-model="query" and whenever I type my query the page gets automatically updated to the products that I have.
The problem is that whenever the query doesn't match something the page stays blank and this doesn't look good. Instead of a blank page I want the site to display a message saying along the lines of "No products found". How can I achieve this?
I've looked at other questions and their solutions but they didn't work.
Here is my code for the way the products are displayed. As you can see I've added a ng-show="products.length === 0 as some solutions suggested but the message never appears when I don't have a match.
<ul class="list-group">
<li ng-repeat="product in products | filter:query" class="list-group-item">
<h3><a id="productName" href="#/products/{{product.name}}">{{ product.name }}</a></h3>
<img id="mainImage" ng-src="{{ product.imgUrl }}" alt="{{ product.name}}">
<p id="description"> {{ product.description }}</p>
<p id="price"> Price: {{ product.price }} £</p>
<p ng-show="products.length === 0">Nothing found</p>
</li>
</ul>
Here is my controller code for the products page:
//Controller that fetches the available products to display them in the view
app.controller('productListController', function($scope, $http){
$http.get('products/products.json').success(function(data){
$scope.products = data.products;
});
});
Here is the code for the search box:
<div id="searchbox">
<div class="input-group">
<input type="text" class="form-control" ng-model="query" placeholder="Search for...">
<span style="width=1%;" class="input-group-btn">
<button class="btn btn-default" type="button"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div><!-- /input-group -->
</div><!-- searchbox -->
Take the <p ng-show="products.length === 0">Nothing found</p> outside the ng-repeat.
<li ng-repeat="product in filteredProducts = (products | filter:query)" class="list-group-item">
<h3><a id="productName" href="#/products/{{product.name}}">{{ product.name }}</a></h3>
<img id="mainImage" ng-src="{{ product.imgUrl }}" alt="{{ product.name}}">
<p id="description"> {{ product.description }}</p>
<p id="price"> Price: {{ product.price }} £</p>
</li>
<p ng-show="filteredProducts.length === 0">Nothing found</p>
It doesn't show because the ng-repeat is empty.
EDIT:
In order to be sure that the msg is displayed after products data is loaded:
$http.get('products/products.json').success(function(data){
$scope.products = (data.products && data.products.length > 0) ? data.products : [];
});
});
The answer #Daniel is not entirely correct.
As you ng-repeat to use a filter, this filter must also be applicable to the condition of ng-show.
Like this:
<ul>
<li ng-repeat="product in products | filter:query" class="list-group-item">
<h3><a id="productName" href="#/products/{{product.name}}">{{ product.name }}</a></h3>
<img id="mainImage" ng-src="{{ product.imgUrl }}" alt="{{ product.name}}">
<p id="description"> {{ product.description }}</p>
<p id="price"> Price: {{ product.price }} £</p>
</li>
</ul>
<p ng-show="products && (products| filter:query).length === 0">Nothing found</p>
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.search = {};
$scope.arr = [];
$scope.isLoaded = false;
$scope.load = function() {
$scope.isLoaded = true;
$scope.arr = [{
x: 1
}, {
x: 2
}, {
x: 3
}, {
x: 11
}, {
x: 12
}];
};
$scope.clear = function() {
$scope.isLoaded = false;
$scope.arr = [];
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<input ng-model="search.x">
<button ng-click="load()">
Load array
</button>
<button ng-click="clear()">
Clear array
</button>
<div ng-repeat="a in arrFiltered = (arr|filter:search)">
{{a.x}}
</div>
<div ng-show="isLoaded && arrFiltered.length==0">
not found
</div>
</div>
</div>
I would like to store image in a existing table.
How can i do that in laravel 4 .I also want to display that image in my website.How i tried it always shows Call to undefined method Symfony\Component\HttpFoundation\File\UploadedFile::save() .
//my show blade
#extends ('layouts.default')
#section('content')
#foreach($posts as $post)
<div >
<div class="media col-md-8">
<a href="#" class="pull-left paddingTop ">
{{ HTML::image($post->image($destinationPath) }}
</a>
<div>
<h3><strong>{{$post->ad_title}}</strong><strong class="pull-right">{{ $post->price}} Tk</strong></h3>
</div>
<div>
<h5>{{ $post->description}}</h5>
</div>
</div>
</div>
#endforeach
#stop
this is my table storing method
public function store()
{
$post=new Post;
$post->user_id=Auth::id();
$post->category=Input::get('category');
$post->subcategory=Input::get('subcategory');
$post->add_type=Input::get('add_type');
$post->brand=Input::get('brand');
$post->screen_type=Input::get('screen_type');
$post->condition=Input::get('condition');
$post->ad_title=Input::get('ad_title');
$post->fuel=Input::get('fuel');
$post->transmission=Input::get('transmission');
$post->registration_year=Input::get('registration_year');
$post->engine_capacity=Input::get('engine_capacity');
$post->mileage=Input::get('mileage');
$post->description=Input::get('description');
$post->price=Input::get('price');
$post->mobile=Input::get('mobile');
$post->ad_title=Input::get('ad_title');
$post->image = Input::file('image');
//$post->image=Input::get('image');
$destinationPath = public_path() . '/images/';
$post->image->save($destinationPath);
$post->area=Input::get('area');
$post->save();
return 'your ad has been published :)';
}
my image input form
<div>{{ Form::file('image', ['class' => 'form-group']) }}</div>
Edit store function like below
public function store()
{
$post=new Post;
$post->user_id=Auth::id();
$post->category=Input::get('category');
$post->subcategory=Input::get('subcategory');
$post->add_type=Input::get('add_type');
$post->brand=Input::get('brand');
$post->screen_type=Input::get('screen_type');
$post->condition=Input::get('condition');
$post->ad_title=Input::get('ad_title');
$post->fuel=Input::get('fuel');
$post->transmission=Input::get('transmission');
$post->registration_year=Input::get('registration_year');
$post->engine_capacity=Input::get('engine_capacity');
$post->mileage=Input::get('mileage');
$post->description=Input::get('description');
$post->price=Input::get('price');
$post->mobile=Input::get('mobile');
$post->ad_title=Input::get('ad_title');
$name = Input::file('image')->getClientOriginalName();
Input::file('image')->move('public/images',$name);
$destinationPath = '/images/'.$name;
$post->image=$destinationPath;
$post->area=Input::get('area');
$post->save();
return 'your ad has been published :)';
}
and edit blade template like below
#extends ('layouts.default')
#section('content')
#foreach($posts as $post)
<div clss="row">
<div class="media col-lg-8">
<a href="#" class="pull-left paddingTop ">
{{ HTML::image($post->image) }}
</a>
<div>
<h3><strong>{{$post->ad_title}}</strong<strong
class="pull-right">{{ $post->price}} Tk</strong></h3>
</div>
<div>
<h5>{{ $post->description}}</h5>
</div>
</div>
</div>
#endforeach
<div class="">{{$posts->links()}}</div>
#stop