Angular ng-bind-html makes template rendering very slow - angularjs

I am trying to output a list of jobs each having their description in HTML,
but adding the data-ng-bind-html="job.description" directive makes the template rendering very slow - more than 10 seconds.
The jobs are being returned from the server as JSON e.g.
foreach ($jobs as $j) {
$data_arr[] = [
'ID' => $j->ID,
'title' => $j->post_title,
'description' => $j->post_content, // contains HTML
...
]}
// ... return json data
The controller looks something like this
api.getJobs().success(function (data) {
$scope.jobs = data;
});
The template setup it is something like this
<div data-ng-repeat="job in jobs">
<div>{{job.title}}</div>
<div data-ng-bind-html="job.description"></div>
</div>
If I comment out the data-ng-bind-html the template renders fast.
I am only rendering 25 jobs and the HTML is just a few strong and li tags not sure why it is so slow.
Update,
It appears that data-ng-bind-html is a slow operation, finally what worked for me is to only render per job html after some action has been taken by the user.
In my case when the bootstrap accordion containing the job is expanded.
<div data-ng-if="status.open" data-ng-bind-html="job.description"></div>

Related

Vue.js array is mixed up after reloading data

The data in the Vue.js array with JSON objects in it is mixed up after reloading them from Spring backend.
I am trying to make a frontend with Vue.js for editing the data on the Spring backend. Vue.js makes a call to the backend with axios, it returns a list of items that is rendered on the page with a v-for. The items can be edited separately, when the 'Save' button is clicked for an item, the item is sent to the backend and the data is updated in the database. After this, when I hit reload in the browser (or close and open the same page) the data is reloaded from the server but its mixed up.
loadExercises(){
axios({url:'http://localhost:5000/exercises/all'}).then(resp => {
this.exercises = resp.data;
})
}
This is the function that loads the data.
This is the array originally : https://imgur.com/5x8TiwY
This is the array after reload, I edited the first entry: https://imgur.com/d6G4SMm
The backend returns the correct objects, the only difference is the that
the entry which was edited is the last in the array (and it was the first in the example case before the edit). The id-s do not change during saving, just the details are updated, like name or description.
This is the part of the template that generates the html, just in case it is because something in there.
<div role="tablist" id="container" v-bind:key="ex.id" v-for="ex in exercises">
<b-card no-body class="mb-1 customcard">
<b-card-header header-tag="header" class="p-1 alignleft" role="tab" v-b-toggle="`accordion${ex.id}`">
<span>{{ex.name}}</span>
</b-card-header>
<b-collapse v-bind:id="`accordion${ex.id}`" accordion="my-accordion" role="tabpanel">
<b-card-body class = "alignleft">
<p class="card-text">
Name: <b-form-input
v-bind:value="`${ex.name}`"
v-bind:id="`input${ex.id}`"
v-on:input="saveExName(ex.id)"></b-form-input>
Target: {{ex.target}} <b-form-select v-on:input="onTargetChange(ex.id)"
v-bind:id="`target${ex.id}`" >
<option v-bind:key = "target" v-for="target in targets" :value="target" >{{target}}</option>
</b-form-select>
Description: <b-form-textarea
v-bind:value="`${ex.description}`"
:rows="3"
:max-rows="6"
v-on:input="saveExDesc(ex.id)"
v-bind:id="`textarea${ex.id}`"></b-form-textarea>
</p>
<div class="alignright">
<b-button v-on:click="saveExUpdate(ex.id)">Save</b-button>
</div>
</b-card-body>
</b-collapse>
</b-card>
</div>
Any ideas why this is happening?

Filter card list with Razor View

Im trying to make a filter like in AngularJS when u use:
ng-repeat="u in users | filter:searchBar">
And your input filter looks like
<input type="text" id="searchBar" placeholder="start typing" ng-model="searchBar">
But the things its that im working on MVC with Razor View and I do not know how to approach this filter.
The list of cards is made with a foreachlike this:
#foreach{ var item in Models){
<div class="card">
<div class="card-container">
some content
</div>
</div>
}
Any ideas?
You can do the filtering with ajax. Here is a server side filtering solution.
First, you should move the code which renders the result to a partial view. Let's say you created a partial view called CustomerList.cshtml. Move the list code to that.
#model IEnumerable<Customer>
#foreach (var item in Model)
{
<div class="card">
<div class="card-container">
#item.Name
</div>
</div>
}
Now in your main view, you can call this partial view and pass the data to it. Wrap the call to the partial view in a container div. Add a input element for user to enter the search key. Assuming your main view is also strongly typed to IEnumerable<Customer>
#model IEnumerable<Customer>
<input type="text" id="search" data-url="#Url.Action("Index")" />
<div id="div-items">
#Html.Partial("CustomerList",Model)
</div>
Now we need to have some javascript code which listen to the keyup event on the search input, read the value of it and make an ajax call to the server where it uses the search key and get the filtered set of data, pass that to the same partial view and return the partial view result.
You can use jQuery $.get method
$(document).ready(function () {
$("#search").keyup(function() {
var v = $(this).val();
$.get($(this).data("url"), { searchKey: v }).done(function(res) {
$("#div-items").html(res);
});
});
});
Now make sure your server action method returns the filtered data like this
public ActionResult Index(string serchKey="")
{
var items = db.Customers.AsQueryable();
if (!String.IsNullOrEmpty(searchKey))
{
items = items.Where(a => a.Name.StartsWith(searchKey));
}
var t = items.ToList();
if (Request.IsAjaxRequest())
{
return PartialView("CustomerList",t );
}
return View(t);
}
Another option is to do client side filtering. on the items. But if i am going that direction, i would choose a client side MVC framework like angular to do that for me

WP Rest API Get Featured Image

I am building a relatively simply blog page that uses the WP Rest Api and AngularJs to show the data on the front-end.
On my home page I want to return the title, followed by the featured image, followed by the excerpt. I have pulled the title and excerpt in fine it seems that in the JSON the featured image is a media Id. What is the best way to pull this data in on the fly?
I have seen various things around the internet that use PHP functions but I think the best way to do it is within a angular controller, just looking for some advice on exactly what the controller would be
List View HTML
<ng-include src=" dir + '/form.html?v=2' "></ng-include>
<div class="row">
<div class="col-sm-8 col-lg-10 col-lg-push-1 post">
<div class="row-fluid">
<div class="col-sm-12">
<article ng-repeat="post in posts" class="projects">
<a class="title" href="#/post/{{post.slug}}"><h2>{{post.title.rendered}}</h2></a>
<p ng-bind-html="post.excerpt.rendered | to_trusted"></p>
</article>
</div>
</div>
</div>
</div>
Controller
.controller('listPage',['$scope','Posts', function($scope,Posts){
$scope.refreshPosts = function(){
Posts.query(function(res){
$scope.posts = res;
});
};
$scope.refreshPosts();
// CLEARFORMFUNCTION
$scope.clear = function(){
$scope.$root.openPost = false;
jQuery('#save').modal('hide');
};
// SAVEMODALOPEN/COSE
$scope.openSaveModal = function(){
jQuery('#save').modal('show');
}
$scope.closeSaveModal = function(){
jQuery('#save').modal('hide');
}
// DATEFUNCTION
$scope.datify = function(date){
$scope.date = newDate(date);
return $scope.date.getDate()+'/'+$scope.date.getMonth()+'/'+$scope.date.getYear();
};
}])
You could also modify the JSON response with PHP. This returns just what I need and is very fast (Using _embed is very slow in my experience)
I have the following code in a plugin (used for adding custom post types), but I imagine you could put it in your theme's function.php file.
php
add_action( 'rest_api_init', 'add_thumbnail_to_JSON' );
function add_thumbnail_to_JSON() {
//Add featured image
register_rest_field( 'post',
'featured_image_src', //NAME OF THE NEW FIELD TO BE ADDED - you can call this anything
array(
'get_callback' => 'get_image_src',
'update_callback' => null,
'schema' => null,
)
);
}
function get_image_src( $object, $field_name, $request ) {
$size = 'thumbnail'; // Change this to the size you want | 'medium' / 'large'
$feat_img_array = wp_get_attachment_image_src($object['featured_media'], $size, true);
return $feat_img_array[0];
}
Now in your JSON response you should see a new field called "featured_image_src": containing a url to the thumbnail.
Read more about modifying responses here:
http://v2.wp-api.org/extending/modifying/
And here's more information on the wp_get_attachment_image_src() function
https://developer.wordpress.org/reference/functions/wp_get_attachment_image_src/
**Note: Don't forget <?php ?> tags if this is a new php file!
Turns out, in my case, there is a new plugin available that solves this without having to make a secondary request. See my recent Q:
WP Rest API + AngularJS : How to grab Featured Image for display on page?
If you send the ?_embed param to the query, it will return more information in the response, like images, categories, and author data.
const result = await axios.get(`/wp-json/wp/v2/my-post-type?_embed`);
// Featured Image
result.data._embedded['wp:featuredmedia'][0].source_url;
// Thumbnail
result.data._embedded['wp:featuredmedia'][0]['media_details']['sizes']['medium']['source_url']

implement embedding comments like twitter in angularjs

i know in Angular world it is better to bind data than manipulate dom elements. but i can't figure out a way to implement the 'in timeline, click a tweet, load replies, click another tweet load another replies' effects.
here is some code run into my thoughts:
<div class="tweet" ng-repeat="tweet in tweets">
<div class="tweet-content">{{tweet}}</div>
<a class="button" ng-click="loadreplay()">load reply</a>
<div class="reply-container">{{reply}}</div>
</div>
if i write controller like this
app.controller('Test', function($scope){
$scope.tweets = ["foo", "bar"];
$scope.loadreplay = function(){
$scope.reply = "reply";
}
});
then all {{reply}} fields will be filled with 'reply', so in this condition, is manipulate the dom elements the only resolution? or some more "angular" way?
Use a appropriate schema for your data/model. Considering that you would store not only the text but at least something like a ID you would use an object anyway. So think about something like this:
$scope.tweets = [
{ id:1, txt: 'foo' },
{ id:2, txt: 'bar' }
]
Then you could store the individual replies in that object as well:
$scope.loadreply = function(tweet) {
tweet.reply = 'Reply';
}
Note: In this function you could then also use the ID to e.g. fetch the tweets from the server like this:
$scope.loadreply = function(tweet) {
tweet.reply = LoadReplies(tweet.id);
}
You would then use the tweet specific reply attribute for display:
<div ng:repeat="tweet in tweets">
<div>{{tweet.txt}}</div>
<a ng:click="loadreply(tweet)">load reply</a>
<div>{{tweet.reply}}</div>
</div>
See this fiddle for a working demo: http://jsfiddle.net/XnBrp/

angular directive: infinite nesting using ng-include, model update does mess up view

I just found out, that when using the infinite structure in a directive on every model update, the template will get messed up on too many ng-changes. This is caused by the ng-include (I figured out). I use somekind of this in my directive's template via templateUrl, which is written in HAML:
.btn-group{ :name => "FIXME", "ng-required" => "true" }
%a.btn.btn-default.dropdown-toggle{ "data-toggle" => "dropdown", href: "#", "ng-disabled"=>"ngDisabled" }
{{currentValue.name || dropdownPlaceholder }}
%span.caret
%ul.dropdown-menu{ "ng-show" => "!ngDisabled" }
%div{ "ng-repeat" => "model in ngModel", "ng-include" => "'node.html'" }
%script{ :type => "text/ng-template", :id => "node.html" }
%li{ "ng-click" => "selectValue(model)" }
%a
{{model.name}}
%ul{ "ng-repeat" => "model in model.children", :style => "margin-left: 10px;" }
%div{ "ng-include" => "'node.html'" }
on many clicks on the dropdown the view seems to keeps the old items and seems to push the new ones additionally, even though the debug states the model is updating correctly and clean (checked also in directives model). I get an buggy output like this:
https://dl.dropboxusercontent.com/u/21600359/Bildschirmfoto%202013-10-13%20um%2001.31.30.png
Does anybody can give me a hint on how to have ng-include refresh and rebuild the directives template tree like structure?
I really appreciate!
kind regards,
Alex

Resources