I'm trying to use infinite-scroll to lazy load images. I'm getting the following error when it's called though:
TypeError: undefined is not a function
at handler (http://onfilm.us/ng-infinite-scroll.js:31:34)
Here's a very watered down look of what I have thus far.
function tagsController($scope) {
$scope.handleClick = function(tags) {
// Parse Tags
$scope.finished_tags = parsed_data;
};
$scope.$emit( 'handleEmit', { tags = $scope.finished_tags; });
};
function imagesController($scope,$http) {
var rows_per = 5;
$scope.$on('handleBroadcast', function(event, args) {
// Sort the images here, put them in matrix
// Example: matrix[row_number] = { picture1, picture2, picture3 }
$scope.data = matrix;
$scope.loadMore();
};
$scope.loadMore() = function() {
var last = $scope.images.length;
for ( var i = 0; i < rows_per; i++ ) {
$scope.images[last + i] = new Array();
$scope.images[last + i] = $scope.data[last + i].slice( 0 );
}
}
}
The rough idea is that the page loads the first time (w/ no tags) and get images from a PHP script. All of them. They are stored, and loadMore() is called which will populate $scope.images with 5 rows of images. It does, and they are loaded.
The line in that script is accessing $window.height and $window.scrollup. I'm still pretty green w/ Javascript, so feel free to lambast me if I'm doing something horribly wrong.
This is the broken version I'm testing with:
http://onfilm.us/test.html
Here is a version before the lazy loading was implemented, if seeing how the tags work will help. I don't think that's the issue here though.
http://onfilm.us/image_index.html
EDIT: I do think this is a problem w/ the ng-infinite-scroll.js script. The error is on line 31 (of version 1.0.0). It's telling me:
TypeError: undefined is not a function
It doesn't like $window apparently.
My JS Kung Fu is not really equipped to say why. YOu can see a literal copy/paste job from the simple demo here (with the error) onfilm.us/scroll2.html
By refering your site, It appears at first instance that your HTML-markup is not appropriate. You should move infinite-scroll to the parent of ng-repeat directive so that it will not make overlapping calls for each row generated. Please visit http://binarymuse.github.io/ngInfiniteScroll/demo_basic.html
Related
I am working on a Preact-CLI project with a Preact-Router and it works fine on the localhost. But the production doesn't work well after the build.
I have created a one page object which gets its content dynamically from a JSON file (inside the project not external). So I've loaded the same page object 2 times for each different page.
I get the page url (using this.props.permalink) and compare it with the JSONObject.title. If they are the same I want to get the corresponding JSON content to display it on the corrrct page. Works like a charm on localhost, but not in production.
Issue:
Somehow all pages get the content of the first JSON element. First I thought it was a server issue but I was wrong. The builded files are wrong after the prerendering/build. So the prerendered html of page B contains the content of the prerendered page A.
My guess is that during the build this.props.permalink doesn't work. How should I handle this?
Additional info:
I use the prerender function but not the service worker for the build.
Thanks!
UPDATE:
I have rewritten the function. I guessed I needed to set the dynamic content through a loop, so that during the build the compiler loops through it and is able to prerender all the pages.
The iteration and setting the state works, but only the final element of the PrerenderUrls array gets stored. So now all pages gets the JSON content of the first element.
componentWillMount() {
for (var i = 0; i <= PrerenderUrls.length; i++) {
// the code you're looking for
let removeDash = new RegExp("-")
var needle = PrerenderUrls[i].title
var needle1 = needle.replace(removeDash, " ")
alert("1")
// iterate over each element in the array
if (needle1 != "Homepage") {
for (var x = 0; x < Data.length; x++) {
// look for the entry with a matching `code` value
let removeDash = new RegExp("-")
var nodash = Data[x].title.replace(removeDash, " ")
var nocaps = nodash.toLowerCase()
if (nocaps == needle1) {
alert("needle2: "+ needle1 + " nocaps: " + nocaps)
//alert("data "+ Data[x].title)
this.setState({
pageTitle: Data[x].title,
descShort: Data[x].descShort,
description: Data[x].desc,
img: Data[x].img
})
alert("state "+ this.state.pageTitle)
}
}
}
}
From your description it seems you have a standard Javascript closure problem. I noticed you use both let and var. If let is supported, use it instead of var. It will automagically solve your closure issues, because let creates variables with the block scope, instead of a function scope. Otherwise, you can try to replicate how let does it under the hood - throw the variable to the callback function. Something in the lines of:
// ...
for (var x = 0; x < Data.length; x++) {
try { throw x }
catch(iterator) {
this.setState({
pageTitle: Data[iterator].title
});
}
}
PS. It is very difficult to follow your code, when it is so specific to your functionality. You could simplify it, and focus on the troubling issue. Most of the code you provided is not relevant to your problem, but makes us going through it anyway.
I have angular code that fetches 8 json files asynchronously each via $http.get. This is called using ng-init="someFunct()" in a template code that is attached. Everything works great including filtering when a user types into an input text box. Filtering is especially important to my application.
To make filtering even better, I extract keywords from the said json files which I then wrap with <span class="tag" ng-click="filterWith='kywd'">{{kywd}}</span> in the hope that a user can click on the tags instead of type. This ONLY works if I embed the tags statically - in the real application I cannot know the keywords in advance. If I insert dynamically via $("#someContainerID").append(TAG_HTML_CODE) or similar it NEVER works!
In a nutshell this is what I need to achieve:
1) Dynamically inject multiple (in hundreds) such tags into DOM;
2) Inject the tags ONLY after everything else has loaded and compiled - but especially after the json files have been read and keywords extracted;
3) The tags that I inject need to respond to something like ng-click="filterWith='some_keyword'"
If there was a way to tell when AngularJS has finished all other processing - how great this would be! I have read everywhere and it seems so cryptic and confusing - pls HELP!
I have even tried the following code to no avail:
$timeout(function () {
$scope.$apply(function () {
//code that works on the keywords - works perfect!
var filterRegex = /\s*([\w\d.%]+)\s*/i;
var dom_elem = angular.element(document.querySelector("#filter_tags"));
dom_elem.html("");
for (var m = 0; m < tags.length; m += 1) {
var match = filterRegex.exec(tags[m][0]);
if (match != null) {
dom_elem.append($compile("<span data-ng-model=\"filterWith\" data-ng-click=\"filterWith='" + match[1] + "'\" title=\"" + tags[m][1] + "\" class=\"sk3tag clk\">" + match[1] + "</span>")($scope));
}
}
});
}, 10000, false);
}
EDIT: Narrowed the scope of my challenge to mainly one!
The bigger challenge for me is how to enable ng-click in the dynamically injected code and how to do it right.
Use Promise.all() to trigger when everything is loaded.
Earlier I had asked the question above. Somebody suggested I read further on directives instead. I did, fairly well. I came up with the following solution, to use click events on html code injected dynamically to DOM. I thank truly God for helping me figure it out, eventually. I no longer need to wait for the asynch data, whenever it comes and hence updates the model, my html tags are updated automatically - MVC magic! It seems to work great!
ANGULAR
//excerpt
myNgApp.controller('ctlTodayLatest', ['$scope', '$timeout', '$compile', '$http', function () {
$http.get('/filtertags.json').then(function (response) {
$scope.filterTags = response;
},
function (response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log(response);
}
);
}]);
myNgApp.directive("myFilterTag", function () {
return {
template: "<span data-ng-repeat=\"tag in filterTags\" title=\"{{tag[0]}}\" class=\"mytag clk\" ng-click=\"filterWith(tag[0])\">{{tag[0]}}</span>",
link: function (scope, element, attrs) {
scope.filterWith = function (term) {
scope.fQ = term;
};
}
};
});
HTML
//excerpt
<div id="filter_tags" class="xip2 TAj" my-filter-tag></div>
i'm putting together a personal website as a portfolio and i'm having trouble getting a photo gallery to work. I found a free Javascript gallery (http://ettrics.com/lab/demo/material-photo-gallery/) that I decided to implement. When putting the page together locally, the javascript runs no problem, however when I upload the page to the site (which already has plenty of other javascript running) I get the following error when scrolling on the page, or when trying to 'fullscreen' one of the images by clicking on it:
TypeError: this._fullImgs is undefined
I tried to isolate the issue and found that a line of code was executing differently on the server, than locally, the excerpt is below:
Gallery.prototype._loadFullImgsDone = function() {
var imgLoad = imagesLoaded(this._fullBox);
imgLoad.on('done', function(instance) {
var imgArr = instance.images;
this._fullImgs = [];
this._fullImgDimensions = [];
this._fullImgsTransforms = [];
for (var i = 0, ii = imgArr.length; i < ii; i++) {
var rect = imgArr[i].img.getBoundingClientRect();
this._fullImgs.push(imgArr[i].img);
this._positionFullImgs.call(this, imgArr[i].img, i);
this._fullImgDimensions.push(rect);
}
this._fullImgsLoaded = true;
}.bind(this));
};
I have found that the images are being found from their source location, however
imgLoad.on('done', function(instance) {
...
executes differently. The site is located at http://http://samueller.tech/photo-best.html id anybody would like to see for themselves the error I am getting.
Thanks in advance, i'm at a complete loss of how to fix this.
I'm seeing (on that site) the resizeHandler is getting called before the images are loaded
Gallery.prototype._handleScroll = debounce(function() {
this._resetFullImg.call(this);
}, 25);
Gallery.prototype._handleResize = function() {
this._resetFullImg.call(this);
};
Then this._resetFullImg fails because there are no images loaded yet which is why this._fullImgs is empty. The code seems to have another variable called _fullImgsLoaded and probably the _resetFullImg method should do nothing if images haven't been loaded.
You could try adding that like this:
// in material-photo-gallery.js line 1379
Gallery.prototype._resetFullImg = function() {
if (!this._fullImagesLoaded) {
return
}
this._fullImgsTransforms = [];
for (var i = 0, ii = this._fullImgs.length; i < ii; i++) {
...
I don't know how this will affect the reset of the gallery code, but it might work. It makes some sense that on your production system, the page load time (with extra JS and stuff) is such that these events might get called before things are ready which is something you don't see locally.
good luck.
I am currently using a JS script called Timeframe.js with the Prototype javascript framework, to create a calendar widget. In my local copy it works great, however in my staging environment I am getting the following javascript error:
TypeError: this.element.down(...) is undefined
this.element.down('div#' + this.element.id + '_container').insert(calendar);
The error is referring to line 97 in timeframe.js library file. It is a part of the following method:
// Scaffolding
createCalendar: function() {
var calendar = new Element('table', {
id: this.element.id + '_calendar_' + this.calendars.length, border: 0, cellspacing: 0, cellpadding: 5
});
calendar.insert(new Element('caption'));
var head = new Element('thead');
var row = new Element('tr');
this.weekdayNames.length.times(function(column) {
var weekday = this.weekdayNames[(column + this.weekOffset) % 7];
var cell = new Element('th', { scope: 'col', abbr: weekday }).update(weekday.substring(0,3));
row.insert(cell);
}.bind(this));
head.insert(row);
calendar.insert(head);
var body = new Element('tbody');
(6).times(function(rowNumber) {
var row = new Element('tr');
this.weekdayNames.length.times(function(column) {
var cell = new Element('td');
row.insert(cell);
});
body.insert(row);
}.bind(this));
calendar.insert(body);
this.element.down('div#' + this.element.id + '_container').insert(calendar);
this.calendars.push(calendar);
this.months = this.calendars.length;
return this;
},
I've also verified that the contents of this.element.id are valid. The value ends up being 'div#calendars_container' which is an element that exists within the markup, so it's not missing from the DOM.
I've been unable to determine the root of this issue, I've ensured I have the latest version of Prototype and Timeframe but the error still occurs. I've been using these scripts in this way for some time now and haven't run into this before now, can anyone put me on the right track?
You will need to share a little more context to get this resolved, I think. If the code runs fine locally but not on your (presumably off-site) staging environment, then it could be a matter of not all of your code being loaded by the time execution reaches the line in question.
I appears that your snippet is inside an event handler; even if all your code is loaded prior to reaching this line, perhaps a timing issue exists where Prototype hasn't yet extended the this.element object with the down() function?
Most likely the greater latency between your staging environment is causing some network-bound function to execute after another, in a way opposite to how it routinely does it for you locally.
Put a breakpoint on this line and see what happens.
No idea what I'm doing or why it isn't working. Clearly not using the right method and probably won't use the right language to explain the problem..
Photogallery... Trying to have a single html page... it has links to images... buttons on the page 'aim to' modify the path to the images by finding the name currently in the path and replacing it with the name of the gallery corresponding to the button the user clicked on...
example:
GALLERY2go : function(e) {
if(GalleryID!="landscapes")
{
var find = ''+ findGalleryID()+'';
var repl = "landscapes";
var page = document.body.innerHTML;
while (page.indexOf(find) >= 0) {
var i = page.indexOf(find);
var j = find.length;
page = page.substr(0,i) + repl + page.substr(i+j);
document.body.innerHTML = page;
var GalleryID = "landscapes";
}
}
},
There's a function higher up the page to get var find to take the value of var GalleryID:
var GalleryID = "portfolio";
function findGalleryID() {
return GalleryID
}
Clearly the first varGalleryID is global (t'was there to set a default value should I have been able to find a way of referring to it onLoad) and the one inside the function is cleared at the end of the function (I've read that much). But I don't know what any of this means.
The code, given its frailties or otherwise ridiculousness, actually does change all of the image links (and absolutely everything else called "portfolio") in the html page - hence "portfolio" becomes "landscapes"... the path to the images changes and they all update... As a JavaScript beginner I was pretty chuffed to see it worked. But you can't click on another gallery button because it's stuck in a loop of some sort. In fact, after you click the button you can't click on anything else and all of the rest of the JavaScript functionality is buggered. Perhaps I've introduced some kind of loop it never exits. If you click on portfolio when you're in portfolio you crash the browser! Anyway I'm well aware that 'my cobbled together solution' is not how it would be done by someone with any experience in writing code. They'd probably use something else with a different name that takes another lifetime to learn. I don't think I can use getElement by and refer to the class/id name and parse the filename [using lots of words I don't at all understand] because of the implications on the other parts of the script. I've tried using a div wrapper and code to launch a child html doc and that come in without disposing of the existing content or talking to the stylesheet. I'm bloody lost and don't even know where to start looking next.
The point is... And here's a plea... If any of you do reply, I fear you will reply without the making the assumption that you're talking to someone who really hasn't got a clue what AJAX and JQuery and PHP are... I have searched forums; I don't understand them. Please bear that in mind.
I'll take a stab at updating your function a bit. I recognize that a critique of the code as it stands probably won't help you solve your problem.
var currentGallery = 'landscape';
function ChangeGallery(name) {
var imgs = document.getElementsByTagName("img") // get all the img tags on the page
for (var i = 0; i < imgs.length; i++) { // loop through them
if (imgs[i].src.indexOf(currentGallery) >= 0) { // if this img tag's src contains the current gallery
imgs[i].src = imgs[i].src.replace(currentGallery, name);
}
}
currentGallery = name;
}
As to why I've done what I've done - you're correct in that the scope of the variables - whether the whole page, or only the given function, knows about it, is mixed in your given code. However, another potential problem is that if you replace everything in the html that says 'landscape' with 'portfolio', it could potentially change non-images. This code only finds images, and then replaces the src only if it contains the given keyword.