AngularJs Component doesn't update view after img loading - angularjs

i'm using angularJs 1.7 components.
This is my component who uploads a picture then converts it to base 64, then it is supposed to display it, but the displaying doesnt work .
myApp.component('club', {
templateUrl: 'vues/club.html',
controller: function($log,$scope) {
// HTML form data, 2 way binding ..
this.club = {};
// Bse 64 encoder
encodeImageFileAsURL = function() {
var filesSelected = document.getElementById("inputFileToLoad").files;
if (filesSelected.length > 0) {
var fileToLoad = filesSelected[0];
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
var srcData = fileLoadedEvent.target.result; // <--- data: convert base64 : OK
this.club.img = srcData ; // Displaying in view doesnt work
}
fileReader.readAsDataURL(fileToLoad);
}
}
// Jquery watcher when we upload a picture
$(document).on('change', 'input[type="file"]' , function(){
encodeImageFileAsURL();
});
This is the html button inside the template :
<div id="upload_button">
<label>
<input name="inputFileToLoad" id="inputFileToLoad" ng-model="logo" type="file" onchange="" /> </input>
<span class="btn btn-primary">Upload picture</span>
</label>
</div>
This is the error :
TypeError: this.club is undefined
srcData is ok, and holds a base 64 image, the function works well.
I've tried the solution provided (.bind(this)) there with no luck , i dont know where to place it:
How to access the correct `this` inside a callback?
When using the $scope syntax, it is working, adding $scope.$apply(), but now i'm using components based dev, and the .this syntax, it doesnt work any more .
EDIT 1 :
Ok, i've initialized club with
$scope.club = {} ;
then inside the function, i'm writing
$scope.club.img = srcData ;
Then, it is working ok. I dont understand why .this is not the same than $scope !

See following example for where this object has reference to
a={
firstname:"something",
lastname:"something2",
fullname:function(){
console.log(this.firstname+' '+this.lastname);
}
}
a.fullname();
In above example Object a is created and inside fullname() function this object pointing to 'a' Object.
So that in your case
templateUrl is only variable on this Object if you do not want to use $scope. You can declare it by using var club = {}

Related

angular file upload in ng-repeat with preview input bind

I'm working on an angular 1.6 based image upload with ng-repeat, note the input is not multi, but there are multiple ng-repeated inputs, I have the image preview working as well as adding lines / removing lines, the only thing that seems to not be working is if I remove an item the file inputs do not update (I have code that does properly update the previews). Here is what I am working with:
<div ng-repeat="item in data.items track by $index">
<input ng-model="item.fileinput" type="file" name="image_{{$index}}" id="image_{{$index}}" onchange="angular.element(this).scope().imageChoose(this)"/><i ng-click="removeEvent($index)" class="fa fa-trash fa-lg"></i>
<img ng-if="!item.thumb" class="preview-image-small" ng-src="/images/general/placeholder.jpg"</img>
<img ng-if="item.thumb" class="preview-image-small" ng-src="{{item.thumb}}"</img>
</div>
Then in my controller I handle the imageChoose as follows:
$scope.imageChoose = function (data) {
var id = data.id.split("_");
id = id[id.length-1];
var elem = document.getElementById(data.id);
if (typeof (FileReader) != "undefined") {
var reader = new FileReader();
reader.onload = function (e) {
$scope.$apply(function() {
$scope.data.data.items[id].thumb = e.target.result;
});
};
reader.readAsDataURL(elem.files[0]);
} else {
alert("This browser does not support FileReader.");
}
};
This properly sets the image previews and when I run a remove on a line they re-order correctly due to the ng-src of event.thumb. The problem is the actual file input does not bind or update, here is the code for removing a line:
$scope.removeEvent = function (index) {
$scope.data.items.splice(index, 1);
};
I'm hoping there is a relatively simple way to bind the input or handle the remove so that the inputs stay correct. Thanks in advance for any ideas.
Your removeEvent method is not working because of using track by $index together with ng-repeat. This is a known side effect. Try removing it/using different track by expressions.

cannot manipulate string with angular filter

I am an angular newbie and I study the book "Angular JS by example" and I try to create my own filter. (pp. 93-94).
In my controller this is the string I want to manipulate
procedure: "Assume a position, with feet together .\Slightly bend your knees, .\While in air, bring your legs out .\ As you are moving your legs outward"
and then I sanitise it
$scope.trustedHtml = $sce.trustAsHtml($scope.currentExercise.details.procedure);
Since this is an SPA , the filter is in the description-panel.ejs file, that is inside workout.ejs, that is inside index.ejs
description-panel.ejs has
<div class="panel-body" ng-bind-html ="trustedHtml | myLineBreakFilter"> </div>
workout.ejs has
<div id="video-panel" class="col-sm-3" ng-include="'description-panel.ejs'">
and index.ejs has
<script src="/javascripts/7MinWorkout/filters.js"></script>
filter.js has the filter
angular.module('7minWorkout').filter('myLineBreakFilter', function () {
return function (input) {
var str = input;
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return output ;
}
});
The filter should replace all the . with </br></br>.
This does not work and I get no text at all in my front-end. I get this error in the console
TypeError: str.slice is not a function at filters.js:22
Shouldn't basic js stuff like str.slice be supported out of the box? What am I missing?
Thanks
$sce.trustAsHtml() return you an object so slice will not work on it.You can pass that object to $sce.getTrustedHtml(object) to obtain the original value and then can apply slice on it.
angular.module('7minWorkout').filter('myLineBreakFilter', function ($sce) {
return function (input) {
var str = $sce.getTrustedHtml(input);
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return $sce.trustAsHtml(output) ;
}
});
Try this add this before the splice
str.toString();
str.splice(//pass parameters);

How can I get ng-click to function with ng-repeat and ng-bind-html?

I am trying to get a ng-click directive to function within an ng-repeat and ng-bind-html. The ng-click code is added to a string of html from data pulled from the server (hence the ng-bind-html). The setup has a controller, a base template that is put onto the page with Drupal, and a partial that is loaded via the template from Drupal.
The controller looks like this at the moment:
var guideListController = angular.module('app')
.controller('guideListController', [
'$scope',
'$sce',
'$compile',
'ViewService',
'ToolKit',
function($scope, $sce, $compile, ViewService, ToolKit) {
// Storage for which rows need more/less links
this.rowIndex = [];
this.showFull = false;
this.showFullClick = function() {
alert('Showfull');
};
this.trustIntro = function(code) {
return $sce.trustAsHtml(code);
};
// Fetch the guide list view from services
var data = ViewService.get({view_endpoint:'guide-service', view_path: 'guide-list'}, function(data) {
//console.log(data);
// Update/process results
for (var row in data.results) {
// Create short intro w/ truncate.js
data.results[row].Intro_short = $sce.trustAsHtml($scope.guideList.getShortIntro(data.results[row], row));
//data.results[row].Intro_short = $scope.guideList.getShortIntro(data.results[row], row);
// Update intro
data.results[row].Introduction = $sce.trustAsHtml($scope.guideList.updateIntro(data.results[row], row));
//data.results[row].Introduction = $scope.guideList.updateIntro(data.results[row], row);
}
$scope.guideList.guides = data.results;
});
// Add a read less anchor tag at the end of the main intro
this.updateIntro = function(row, row_index) {
var intro = row['Introduction'].trim();
if ($scope.guideList.rowIndex[row_index]) { // only apply Less link if needed
var index = intro.length - 1;
var tag = [];
if (intro.charAt(index) === '>') { // we have a tag at the end
index--;
do {
tag.push(intro.charAt(index));
index--;
} while (intro.charAt(index) != '/'); // the closing tag
index--; // we move the index one more for the "<"
tag.reverse(); // Reverse
tag = tag.join('');
}
var inserts = ['div', 'p']; // we insert the Less link here.
if (jQuery.inArray(tag, inserts) >= 0) { // insert into the tag
intro = intro.substr(0, index) + ' <a class="less" ng-click="$parent.guideList.showFull = false">Less</a>' + intro.substr(index);
}
else { // insert at the end of the html
intro = intro + '<a class="less" ng-click="this.showFull = false">Less</a>';
}
}
return intro;
};
// Truncate the long intro into a shorter length blurb
this.getShortIntro = function(row, row_index) {
// Truncate if necc.
var short_intro = jQuery.truncate(row['Introduction'], {
length: 250,
words: true,
ellipsis: '\u2026 <a class="more moreish" attr-ng-click="guideList.showFullClick()">Read on</a>'
});
var more = jQuery('.more', short_intro); // select more link
if (more.length) { // do we have a more link
$scope.guideList.rowIndex[row_index] = true;
}
else { // no more link
$scope.guideList.rowIndex[row_index] = false;
}
$compile(short_intro)($scope);
return short_intro;
};
}]);
As you can see in the ViewService.get() call, data is fetched and then processed. The processing simply involves putting a link at the end of the "Intro" field that is intended to be clickable.
For a while I was having a tough time to even get the ng-click directive to even show (it was being filtered out w/out $sce.trustAsHtml). Now it is there but clicking it has no effect.
The main template (from Drupal) currently looks like:
<div class="guide-listing" ng-controller="guideListController as guideList">
<a ng-click="guideList.showFullClick()">Click me</a>
<div class="guide-teaser"
ng-repeat="guide in guideList.guides"
ng-include src="'/sites/all/themes/ngTheme/ngApp/partials/guide_teaser.html'">
<!-- See guide_teaser.html partial for guide teasers -->
</div>
</div>
The ng-click as placed in the Drupal template above works as expected.
And for the partial that is used in the ng-repeat, it looks like so:
<div ng-controller="guideListController as guideList">
<h2 class="guide-teaser-title">{{guide.node_title}}</h2>
<div class="guide-teaser-intro" ng-bind-html="guide.Introduction" ng-show="guide.showFull">
{{guide.Introduction}}
</div>
<div class="guide-teaser-intro-short" ng-bind-html="guide.Intro_short" ng-show="!guide.showFull">
{{guide.Intro_short}}
</div>
</div>
So far I have only been working on getting the ng-click to work on the short_intro and have had no success so far. Any ideas as to what I am doing wrong would be greatly appreciated!
Ok, So I did get some traction! I used the ngHtmlCompile (http://ngmodules.org/modules/ng-html-compile) directive that was created by https://github.com/francisbouvier (thanks!)
The issue was that the new (dynamic) html wasn't being compiled.
At first it didn't work. I had two issues that prevented it from firing:
A: I stopped using $sce.trustAsHtml. Using this in conjunction with the directive caused the content to disappear!
B: The other issue was one of scope. After I changed the directive such that transclude was set to false it worked just fine!

Notable to link javascript object and angularjs in controllers

I am trying to develop a mobile application in which i am getting JSON object using javascript page main.js,now I am trying to print the object using angualjs Controllers,but could not find any way.CAn anyone help me out on this?
function written in Main.js`
function getViewColumnsSuccess(result){
var httpStatusCode = result.status;
if (200 == httpStatusCode) {
var invocationResult = result.invocationResult;
var isSuccessful = invocationResult.isSuccessful;
if (true == isSuccessful) {
var result = invocationResult.text; //var FinalCol=reult;
} else {
alert("Error. httpStatusCode=" + httpStatusCode); } } }
The var result I want to get in DemoController in another page
app.controller('tableCtrlNew', function($scope,$http) { });
First controllers are not for print data, for that you should dataBinding, in views.
For Example:
In your controller you have this var;
$scope.name="John Doe";
So, then to print that in some view, you shoud print that in some view that has that scope, so to print in one view(html) that is simple like:
<span class="labelName"> {{name}}</span>
It is data-binding, with this you automatically print data from a controller into a view, but remember, it must be in the same scope to works.
Regards.
Assign Result to Window object
window.result = invocationResult.text;
In controller you can use $window
app.controller('tableCtrlNew', function($scope,$http,$window) {
console.log($window.result);
});

How do I change AngularJS ng-src when API returns null value?

In working with the API from themoviedb.com, I'm having the user type into an input field, sending the API request on every keyup. In testing this, sometimes the movie poster would be "null" instead of the intended poster_path. I prefer to default to a placeholder image to indicate that a poster was not found with the API request.
So because the entire poster_path url is not offered by the API, and since I'm using an AngularJS ng-repeat, I have to structure the image tag like so (using dummy data to save on space):
<img ng-src="{{'http://example.com/'+movie.poster_path}}" alt="">
But then the console gives me an error due to a bad request since a full image path is not returned. I tried using the OR prompt:
{{'http://example.com/'+movie.poster_path || 'http://example.com/missing.jpg'}}
But that doesn't work in this case. So now with the javascript. I can't seem to get the image source by using getElementsByTagName or getElementByClass, and using getElementById seems to only grab the first repeat and nothing else, which I figured would be the case. But even then I can't seem to replace the image source. Here is the code structure I attempted:
<input type="text" id="search">
<section ng-controller="movieSearch">
<article ng-repeat="movie in movies">
<img id="myImage" src="{{'http://example.com/'+movie.poster_path}}" alt="">
</article>
</section>
<script>
function movieSearch($scope, $http){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://example.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
imgSrc = document.getElementById('myImage').src;
ext = imgSrc.split('.').pop();
missing='http://example.com/missing.jpg';
if(ext !== 'jpg'){
imgSrc = missing;
}
});
}
</script>
Any ideas with what I'm doing wrong, or if what I'm attempting can even be done at all?
The first problem I can see is that while you are setting the movies in a async callback, you are looking for the image source synchronously here:
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
// This code will be executed before `movies` is populated
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
However, moving the code merely into the callback will not solve the issue:
// THIS WILL NOT FIX THE PROBLEM
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
// This will not solve the issue
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
// ...
});
This is because the src fields will only be populated in the next digest loop.
In your case, you should prune the results as soon as you receive them from the JSONP callback:
function movieSearch($scope, $http, $timeout){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
$scope.movies.forEach(function (movie) {
var ext = movie.poster_path && movie.poster_path.split('.').pop();
// Assuming that the extension cannot be
// anything other than a jpg
if (ext !== 'jpg') {
movie.poster_path = 'missing.jpg';
}
});
});
});
}
Here, you modify only the model behind you view and do not do any post-hoc DOM analysis to figure out failures.
Sidenote: You could have used the ternary operator to solve the problem in the view, but this is not recommended:
<!-- NOT RECOMMENDED -->
{{movie.poster_path && ('http://domain.com/'+movie.poster_path) || 'http://domain.com/missing.jpg'}}
First, I defined a filter like this:
In CoffeeScript:
app.filter 'cond', () ->
(default_value, condition, value) ->
if condition then value else default_value
Or in JavaScript:
app.filter('cond', function() {
return function(default_value, condition, value) {
if (condition) {
return value;
} else {
return default_value;
}
};
});
Then, you can use it like this:
{{'http://domain.com/missing.jpg' |cond:movie.poster_path:('http://domain.com/'+movie.poster_path)}}

Resources