AngularJS scope in function does not seem to be working - javascript

I have two ajax calls. The data starts from the html (input), when enter is pressed, what is in the input field is sent to the controller then to the factory which makes the first ajax call. The success is handled back in the controller, then another ajax call is requested, and the data from that request is handled back in the controller again. I have a $scope -- $scope.ytChannel = data.items; in that final success within the function that does not seem to be working. Here is my code starting with the html
HTML:
<input class="form-control channel-index" type="text" ng-model="channel" placeholder="Enter Channel" ng-keydown="$event.which === 13 && cname(channel)"/>
JS:
.factory('ytVids', function($http){
return{
getChannel: function(name){
var url = 'https://www.googleapis.com/youtube/v3/channels? part=contentDetails&forUsername='+ name +'&key=[my api key]';
return $http.get(url);
},
getVids: function(channel){
return $http.get('https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails&maxResults=50&playlistId='+channel+'&key=[my api key]');
}
};
})
.controller('MainCtrl', function ($scope, ytVids) {
$scope.cname = function(channel){
ytVids.getChannel(channel).success(function(response){
//console.log(response);
console.log(response.items[0].contentDetails.relatedPlaylists.uploads);
ytVids.getVids(response.items[0].contentDetails.relatedPlaylists.uploads)
.success(function(data){
console.log('in success');
console.log(data.items);
$scope.ytChannel = data.items; // This is the scope that is not seeming to want to work.
});
});
};
});
And here is the html that calls that ytChannel
<ul>
<li ng-repeat="item in ytChannel" class="col-xs-12 col-sm-6 col-md-4 vid-options">
<a href="#">
<div class="title">{{item.snippet.title}}</div>
<img src="{{item.snippet.thumbnails.maxres.url}}" />
</a>
</li>
</ul>
if a scope is in a function, does the html not have access to it? What can be done so I can have access to the returned data?
Error
This is the error the console gives in the dev tools GET http://localhost:9000/%7B%7Bitem.snippet.thumbnails.maxres.url%7D%7D 404 (Not Found)

The correct code is
<img ng-src="{{item.snippet.thumbnails.maxres.url}}" />
As the manual says,
Using Angular markup like {{hash}} in a src attribute doesn't work
right: The browser will fetch from the URL with the literal text
{{hash}} until Angular replaces the expression inside {{hash}}. The
ngSrc directive solves this problem.

Related

Angular JS === comparison not working

I am trying to compare the value passed from the url to a controller to a field in a json file.
galleryItem.html
<div class="filter-box">
<ul class="filter list-inline text-center" ng-repeat="gal in ParentData">
<li></li>
</ul>
</div>
<div class="container-fluid">
<div class="row">
<div class="portfolio-box" ng-repeat="x in data">
<div class="col-sm-4">
<div class="item-img-wrap">
<img ng-src={{x.url}} class="img-responsive" alt="">
<div class="item-img-overlay">
<a href={{x.url}} class="show-image">
<span></span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
The updated controller:
controllers.controller('GalleryViewCtrl', function GalleryViewCtrl($scope, $http, $stateParams) {
$scope.pageName = '';
$scope.Description = '';
$scope.GalleryID = $stateParams.id;
$http.get('/data/galleryItems.json')
.then(function (response) { $scope.ParentData = response.data.galleries });
$http.get('/data/galleryItemImages.json')
.then(function (response) {
$scope.data = response.data.images.galleryIdentifier === $stateParams.id;
});
});
I verified the correct value is being passed in to the controller, the values are static and so is the data being passed from the json file. I placed an if statement to check for null as suggested as well. I removed it temporarily to reduce what I'm working with.
If I remove the === $stateParams.id i get all of the images returned and displayed correctly.
If I replace $stateParams.id with a value that I know is in the list (4 or '4') i do not get anything returned. I also tried the value for the last item in the list.
There are no errors (loading scripts, reading json etc.) and all of the values are correct when I'm debugging.
I am still new to this and there is so much documentation with different solutions it all gets very confusing. If anyone has any ideas they would be greatly appreciated.
You are loading data to the $scope.data when the ajax call returns some data. I am assuming your view code is calling the galleryFiltered even before that. May be try to add a null check before returning the value from the method.
$scope.galleryFiltered = function () {
if($scope.data!=null)
{
return $scope.data.galleryIdentifier === $scope.GalleryID;
}
return false;
};
Remember that $http service returns a promise so your $scope.data will be undefined (or holding current state) until $http.get('/data/galleryItemImages.json') will return a success callback function and assign new value to $scope.data from response.
If you'll run $scope.galleryFiltered() before promise gets resolved you will have $scope.data == undefined or whatever data is stored on $scope.data at the time or $scope.galleryFiltered() execution.

Issues with $location in Angularjs

I have a single page app '(Backend in Python/Django)' where my functions return json response and that json response handled by angular js in front end . I am using angular ajax call to hit the function. Now we all know that on ajax call url in address bar do not get changed. But in angular js we can set url using $location.path(). So it keeps the history of url I have visited and on browser back button it changes the url in address bar to previous one . But it do not change the content of the page.
My angular ajax call :
app.controller('myController',
function($scope,$http, $location, $route, $timeout){
$scope.formData={}
$scope.getAllBrainframes=function(id){
if(id){
$scope.url = '/get-brainframe/'+id+'/';
}else{
$scope.url = '/get-brainframe/';
}
$http.post($scope.url)
.success(function(data,status){
.success(function(data,status){
$scope.title = data[0].title
$scope.brainframes = data;
$location.path($scope.url);
$scope.parent= data[0].id;
})
.error(function(data,status){
});
}
});
As I am setting $location.path() on ajax success , so it appends the current visited url in address bar and keeps history of every url i have visited. But when I click on browser back button it changes the url in address bar to previous one but not the content.
Now is there any function that i can trigger when I click on browser back button or how I can change the content of page ?
EDIT :
above ajax success function edited .
My html :
<div class="content">
<span ng-repeat="brainframe in brainframes">
<p ng-if = "brainframe.brainframes.length > 0 ">
<ul class="list-group col-md-5">
<div data-ng-repeat="brain in brainframe.brainframes" class="child-brainframes">
<a class="my-title" ng-click="getAllBrainframes(brain.brainframe_child.pk)">
<li class="list-group-item"><span class="badge">{$ brain.count_children $}</span>{$ brain.brainframe_child.title $}</li>
</a>
</div>
</ul>
</p>
<p ng-if = "brainframe.brainframes.length < 1 ">
<span>No brainframes are available.</span>
</p>
</span>
</div>
You need to look at $routeParams and change the content in the template.
DEMO
.controller("MainCtrl",
function($scope,$http, $location, $route, $timeout, $routeParams){
$scope.formData={};
$scope.id = $routeParams.id;
$scope.getAllBrainframes=function(id){
if(id){
$scope.url = '/home/'+id+'/';
}else{
$scope.url = '/home';
}
console.log($scope.url);
$location.path($scope.url);
};
});
In your template:
Check for the $scope property and show/hide
<script type="text/ng-template" id="Home.html">
<p>This is one template</p>
<p ng-if="id">This is in next template</p>
<a ng-click="getAllBrainframes('1')">next template</a>
</script>
Check full code here
UPDATED:
If you want to persist the ajax call data between routes, you need to probably store it in a service and access it in your controller based on the $routeParams from the service.

Angular UI Bootstrap Typeahead template ng-src with funciton

I'm wondering how to use a function in the ng-src attribute of the custom template for Typeahead. Here's my html template:
<script type="text/ng-template" id="customTemplate.html">
<a>
<img ng-src="getWikiImgs({{match.model}})" width="16">
<span bind-html-unsafe="match.label | typeaheadHighlight:query"></span>
</a>
</script>
<div class="col-xs-10 center alt-txt-light">
<span class="dash-pg-header-txt">Index an author!</span>
<br/>
<hr class="hr-separator"/>
<br/>
<div style="height: 1000px;">
<h4>Search Wikipedia:</h4>
<input type="text" ng-model="asyncSelected" placeholder="ie: John Bunyan" typeahead="item for item in getWikiResults($viewValue)" typeahead-wait-ms="500" typeahead-loading="loadingWikiResults" typeahead-template-url="customTemplate.html" class="form-control" />
<br/>
<i ng-show="loadingWikiResults" class="fa fa-refresh" style="text-align:left; float:left;"></i>
</div>
</div>
So in the custom template script, i'm trying to use a function in ng-src to get the corresponding image from Wikipedia based on the 'match.model' variable used by Typeahead.
And here's the Controller:
angular.module("app").controller("AuthorCreateController", function($scope, $state,
$stateParams, $http) {
$scope.getWikiResults = function($viewValue) {
return $http.get('http://en.wikipedia.org/w/api.php?', {
params: {
srsearch: $viewValue,
action: "query",
list: "search",
format: "json"
}
}).then(function($response){
var items = [];
angular.forEach($response.data.query.search, function(item){
items.push(item.title);
});
return items;
});
};
$scope.getWikiImgs = function(title) {
$.getJSON("http://en.wikipedia.org/w/api.php?callback=?",
{
action: "query",
titles: title,
prop: "pageimages",
format: "json",
pithumbsize: "70"
},
function(data) {
$.each(data.query.pages, function(i,item){
return item.thumbnail.source;
});
});
};
});
The issue is that your template does not have the scope you would reasonably expect it would.
The solution is (depending on where the template occurs) to chain some $parent. calls in front of your function.
See this git issue and this question for more details.
Your problem is NOT actually with calling a function from ng-src. It is rather with CORS [Cross-Resource-Origin-Sharing].
Here is a Plunker for your code:
http://plnkr.co/edit/CvqhU9?p=preview
Type "a", for example, in the input then check the console and you will find out that it did manage to call the function. BUT wait 2 secs and the following comes out:
XMLHttpRequest cannot load http://en.wikipedia.org/w/api.php?&action=query&format=json&list=search&srsearch=a. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://run.plnkr.co' is therefore not allowed access.
What this in short means is that when you are on www.foo.com domain, you cannot request a resource from www.bar.com unless www.bar.com enabled that. You can check out some answers about this here: XMLHttpRequest cannot load an URL with jQuery
I hope this helps.

Triggering function on ngclick with AngularJS

I have a function that makes a $http call to an external API and then populates some results within an ng-repeat array.
Right now the function gets triggered on every element on the ng-repeat, which creates a whole lot of server calls. I'd like for the function to only make the call once an element from the ng-repeat is clicked upon.
I've tried with ng-click, but i'd say i'm missing something.
The $http query that i'm trying to call on click is the second one:
function ImageCtrl($scope, $http) {
$scope.image = 'img/record-default.png';
$http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + $scope.artist.name + '&album=' + $scope.release.title + '&format=json').
success(function (data4) {
$scope.image = data4.album.image[2]['#text'];
}
)
function getVersions ($scope, $http){
$http.get('http://api.discogs.com/masters/' + $scope.release.id + '/versions').
success(function (data5) {
$scope.versions = data5.versions;
});
}
}
And the relevant html:
<div class="col-md-3" ng-controller="ImageCtrl" ng-repeat="release in releases | filter:album | filter:year | filter:{ role: \'main\' }" >
<div class="release" ng-click="getVersions()"> \
<img class="img-responsive" ng-src="{{image}}" /> {{release.title}}
<ul ng-controller="ImageCtrl">
<li ng-repeat="version in versions">{{version.format}}</li>
</ul>
</div>
</div>
And a working Plunker. Function in question is line 60 on script.js
So I ended up taking what you have shown and doing some refactoring.
I moved getVersions to the prototype, and use it to append versions to a release object instead of the $scope.
function ImageCtrl($scope, fakeService) {
var _this = this;
this.fakeService = fakeService;
this.$scope = $scope;
fakeService.getReleases()
.then(function (releases) {
$scope.releases = releases;
});
this.$scope.getVersions = function(release){
_this.getVersions(release);
};
}
ImageCtrl.$inject = ['$scope', 'fakeService'];
ImageCtrl.prototype.getVersions = function (release) {
this.fakeService.getVersions(release.id)
.then(function (versions) {
release.versions = versions;
});
};
The markup isn't terribly different, but you can see where I pass the actual release object into the getVersions function in the click event. This way it always acts directly on the object bound to that particular row.
<div class="row" ng-controller="ImageCtrl">
<div class="col-md-3" ng-repeat="release in releases">
<div class="release" ng-click="getVersions(release)">
<h1>{{release.title}}</h1>
<img class="img-responsive" height="100" width="100" ng-src="{{release.image}}" />
<ul>
<li ng-repeat="version in release.versions">{{version.format}}</li>
</ul>
</div>
</div>
</div>
And here is a working demo showing the whole thing in action: http://jsfiddle.net/jwcarroll/k6mkt/
I'm using a fake service here to mimic calling a web service in order to get the data. I highly recommend wrapping up your calls to $http in order to encapsulate data access in your controller.

Angular API calls and promises

I'm working on an angular app and having a difficult time with one seemly simple operation. Basically, I'm making a call to the soundcloud api, grabbing my tracks, then looping through those tracks and grabbing the iframe embed object, injecting that into the tracks object then sending that whole thing as a promise to be resolved and stored in a $scope.soundcloud object. Just fyi, the second SC call is necessary to generate the widget html. I wish it wasn't but it is hah.
This all happends as it should and i can see the object in $scope. My template picks up the initial data (main track data), and console.logging the object shows the track and embed data, but the template NEVER sees the embed data.
So, fundamentally, How do I get my template to see the embed data, so i can use it with a directive or ng-bind-html? Below is all my code, please ask if you need any more information! Thank you all very much.
HTML
<div class="track" ng-repeat="track in soundcloud.tracks">
<div class="front">
<img src="app/img/loading.gif" />
</div>
<div class="back" ng-bind-html="{{track.oembed}}">
</div>
</div>
Angular Service
getTracks: function(){
var deferred = $q.defer();
var promise = deferred.promise;
SC.get("/me/tracks", function(tracks){
$.each(tracks, function(k, v){
if(v.sharing != 'private'){
SC.oEmbed(v.uri, function(oembed){
v.oembed = $sce.trustAsHtml(oembed.html);
});
} else {
v.oembed = null;
}
});
deferred.resolve(tracks);
});
return $q.all({tracks: promise});
}
Angular Controller
.controller("GridCtrl", ['$scope', 'Soundcloud', function($scope, Soundcloud){
// Init the Soundcloud SDK config
Soundcloud.initialize();
// Get the tracks from soundcloud
Soundcloud.getTracks().then(function success(results){
// Store tracks in the $scope
$scope.soundcloud = results;
console.log(results);
});
}]);
Try creating a directive like this:
app.module('yourModule').directive('embedTrack', function() {
return function(scope, elem, attr) {
elem.replaceWith(scope.track.oembed);
};
});
You then use it like this:
<div class="track" ng-repeat="track in soundcloud.tracks">
<div class="front">
<img src="app/img/loading.gif" />
</div>
<div class="back">
<div embed-track></div>
</div>
</div>
In case you want to pass it as an attribute to the directive, you need to use attr.$observe to make sure you get the value after the interpolation.
<div embed-track={{ track.oembed }}></div>
The directive would then be:
app.module('yourModule').directive('embedTrack', function() {
return function(scope, elem, attr) {
attr.$observe('embedTrack', function(value) {
elem.replaceWith(value);
});
};
});

Categories

Resources