I'm very new to JS and I'm trying to sync the listing data by using local storage example below:
https://imgur.com/lN1x1TH
Song 1 -> Song 1 Detail page
Song 2 -> Song 2 Detail page
Song 3 -> Song 3 Detail page
There will be only one detail page associated with the 3 listings.
Anyway to achieve this without creating multiple pages for the individual detail listing?
index.js
fetch('x')
.then(function(res) {
return res.json();
})
.then(function(data) {
var Data = JSON.stringify(data);
if (localStorage.getItem("songStoreData") === null || typeof localStorage.getItem("songStoreData") === "undefined") {
localStorage.setItem("songStoreData", Data);
}
//get the stored data
var songJSON = localStorage.getItem("songStoreData");
//parse the data to JSON
var Obj = JSON.parse(songJSON);
//for debugging
console.log(Obj);
for (i = 0; i < Obj.songs.length; i++) {
homeList.innerHTML +=
'<li class="li_product_"' + i + '"><div class="li_product_image"><img src="' + Obj.songs[i].image + '"></div><div class="li_product_name">' + Obj.songs[i].name + ', ' + Obj.songs[i].artist + '<br><span class="li_product_duration">' + Obj.songs[i].duration + ' minutes</span></div></div></li>';
}
})
detail.js
var songJSON=localStorage.getItem("songStoreData");
var Obj=JSON.parse(songJSON);
console.log(Obj);
function addDetails(){
document.getElementById('span_header').innerHTML = '<img id="btn_product_details_back" src="img/back_white.png">' + Obj.songs.artist + '';
document.getElementById('div_product_details_img').innerHTML = '<img src="' + Obj.songs.image + '">';
document.getElementById('data_name').innerHTML = Obj.songs.name;
document.getElementById('data_genre').innerHTML = Obj.songs.genre;
document.getElementById('data_release').innerHTML = Obj.songs.release;
document.getElementById('data_duration').innerHTML = Obj.songs.duration;
var Data=JSON.stringify(Obj);
//store the string format data in local storage
localStorage.setItem("songStoreData", Data);
//window.location='index.html';
return false;
}
addDetails();
Related
I wrote a very basic web app that pulls recipe data back from an API. The data is rendered via being pushed to an html template defined in the javascript file. The layout is controlled via a float-grid in CSS.
The code portion that renders the result and pushes to the template:
function displayRecipeSearchData(data) {
var results = ' ';
if (data.hits.length) {
data.hits.forEach(function(item) {
results += template.item(item);
});
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
The html template through which responses are displayed:
const template = {
item: function(item) {
return '<div class ="col-4">' +
'<div class ="result">' +
'<div class="recipelabel">' +
'<div class="reclist">' + item.recipe.ingredientLines + '</div><!-- end reclist -->' +
'<p class="label">' + item.recipe.label + '</p>' +
'<div class="thumbnail">' +
'<a href="'+ httpsTransform(item.recipe.url) + '" target="_blank">' +
'<img src="' + item.recipe.image + '"alt="' + item.recipe.label + '">' +
'</a>' +
'<div class="recipesource">' +
'<p class="source">' + item.recipe.source + '</p>' +
'</div><!-- end recipesource -->' +
'</div><!-- end thumbnail -->' +
'</div><!-- end recipelabel -->' +
'</div><!-- end result -->' +
'</div><!-- end col-4 -->';
}
};
I am trying to change the logic in the displayRecipeSearchData function such that, for each group of three results, a <div></div> surrounds the block of three results. This is so the rows/columns always work in the flex grid. I have tried several ways but have yet to get the syntax/logic correct. Would an if statement nested in the existing statement be effective?
if(i % 3 === 0 ){ results. += '<div class="row">''</div>'}
Any suggestions would be appreciated.
You could use another variable for storing one row of HTML:
function displayRecipeSearchData(data) {
var results = ' ', row = '';
if (data.hits.length) {
data.hits.forEach(function(item, i) {
row += template.item(item);
if (i % 3 == 2) { // wrap row and add to result
results += '<div class="row">' + row + '</div>';
row = '';
}
});
if (row.length) { // flush remainder into a row
results += '<div class="row">' + row + '</div>';
}
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
you are definitely doing this the hard way in my opinion.
instead of manually writing the template as a string and trying to inject the string at the right place (potentially creating invalid html) you should use javascripts built-in element creation. also it'll be more modular to create children in their own functions. It will also be much easier to use a function instead of an object to hold your object creator. My version may have a lot more code, but it will be much easier to modify in the long run
const Itemizer = function(){
this.items = [];
const createEl = function(elType, classes, attributes, text, html){
let el = document.createElement(elType)
for(let i = 0; i < classes.length; i++){
el.classList.add(classes[i]
}
for(let attr in attributes){
el.setAttribute(attr, attributes[attr])
}
if(text){
el.innerText = text
}
if(html){
el.innerHTML = html
}
return el
};
const createThumbnail = function(url, image, alt, source){
let thumbnail = createEl("DIV", ["thumbnail"]),
link = createEl("A", [], {href: httpsTransform(url)}),
img = createEl("IMG", [], {src: image, alt: label});
rSource = createRecipeSource(source)
link.appendChild(img);
thumbnail.appendChild(link);
thumbnail.appendChild(rSource)
return thumbnail
};
const createRecipeSource = function(source){
let wrapper = createEl("DIV", ["recipe-source"]);
wrapper.appendChild(createEl("P", ["source"], {}, source))
return wrapper
}
const createRecipeLabel = function({
recipe: {
ingredientLines,
label,
url,
source
}
}){
let labelWrapper = createEl("DIV", ["recipe-label"),
ingredients = createEl("DIV", ["rec-list"], {}, false, ingredientLines),
recipeLabel = createEl("P", ["label"], {}, label),
thumbnail = createThumbnail(url, image, label, source)
labelWrapper.appendChild(ingredients)
labelWrapper.appendChild(recipeLabel)
labelWrapper.appendChild(thumbnail)
return labelWrapper
}
const createNewItem = function(data){
let columnWrapper = createEl("DIV", ["col-4"]),
result = createEl("DIV", ["result"]),
label = createRecipeLabel(data)
columnWrapper.appendChild(result)
result.appendChild(label)
this.items.push(columnWrapper)
return columnWrapper
}.bind(this)
const getItems = function(){
return this.items
}.bind(this)
const getRows = function(){
const rows = []
let row;
for(let i = 0; i < this.items.length; i++){
const item = this.items[i]
if(i % 3 === 0){
row = createEl("DIV", ["row"])
rows.push(row)
}
row.appendChild(item)
}
return rows;
}.bind(this)
return {
add: createNewItem,
get: getItems,
rows: getRows
}
}
You can then use the function like so:
const template = new Itemizer()
function displayRecipeSearchData(data) {
let rows
if (data.hits.length) {
for(let i = 0; i < data.hits.length; i++){
template.add(data.hits[i])
}
rows = template.rows()
} else {
const p = document.createElement("P")
p.innerText = "No Results")
rows = [p]
}
const resultsWrapper = document.getElementById("js-search-results");
for(let i = 0; i < rows.length; i++){
resultsWrapper.appendChild(rows[i])
}
}
it's also good form to separate css classes with hyphens, so I replaced a few of your class names to reflect that
It's also important to note that you don't actually need more than 1 row. if you wrap all of your items in one row section columns will automatically overflow to the next row when they hit the grid limit
My last note is never use target blank. it goes against proper UX, and creates security holes in your application. if your users need to open in a new tab they can hold ctrl or click "open in new tab"
I apologize up front for the possible lack of clarity for this question, but I'm new to Angular.js and my knowledge of it is still slightly hazy. I have done the Angular.js tutorial and googled for answers, but I haven't found any.
I have multiple select/option html elements, not inside a form element, and I'm populating them using AJAX. Each form field is populated by values from a different SharePoint list. I'm wondering if there is a way to implement this using Angular.js?
I would like to consider building this using Angular because I like some of it features such as data-binding, routing, and organizing code by components. But I can't quite grasp how I could implement it in this situation while coding using the DRY principle.
Currently, I have a single AJAX.js file and I have a Javascript file that contains an array of the different endpoints I need to connect to along with specific query parameters. When my page loads, I loop through the arrays and for each element, I call the GET method and pass it the end-point details.
The code then goes on to find the corresponding select element on the page and appends the option element returned by the ajax call.
I'm new to Angular, but from what I understand, I could create a custom component for each select element. I would place the component on the page and all the select and options that are associated with that component would appear there. The examples I've seen demonstrated, associate the ajax call with the code for the component. I'm thinking that I could use a service and have each component dependent on that service and the component would pass it's specific query details to the service's ajax call.
My current code - Program flow: main -> data retrieval -> object creation | main -> form build.
Called from index.html - creates the multiple query strings that are passed to ajax calls - ajax calls are once for each query string - the very last function in the file is a call to another function to build the form elements.
var snbApp = window.snbApp || {};
snbApp.main = (function () {
var main = {};
main.loadCount = 0;
main.init = function () {
function buildSelectOptions(){
//***
//Build select options from multiple SharePoint lists
//***
var listsArray = snbApp.splistarray.getArrayOfListsForObjects();
for(var i = 0; i < listsArray.length; i++){
var listItem = listsArray[i];
var qryStrng = listItem.list +
"?$select=" + listItem.codeDigits + "," + listItem.codeDescription + "," + listItem.ItemStatus + "&$orderby=" + listItem.codeDescription + "&$filter="+listItem.ItemStatus+" eq true" + "&$inlinecount=allpages"
var listDetails = {
listName: listItem.list,
listObj: listItem,
url: "http://myEnv/_vti_bin/listdata.svc/" + listItem.list +
"?$select=" + listItem.codeDigits + "," + listItem.codeDescription + "," + listItem.ItemStatus + "&$orderby=" + listItem.codeDescription + "&$filter="+listItem.ItemStatus+" eq true" + "&$inlinecount=allpages"
};
var clientContext = new SP.ClientContext.get_current();
clientContext.executeQueryAsync(snbApp.dataretriever.letsBuild(listDetails), _onQueryFailed);
}
//***
//Build select option from other API endpoint
//***
var listDetails = {
listName:"SNB_SecondaryActivityCodes",
url: "http://myEnv/requests/odata/v1/Sites?$filter=(IsMajor eq true or IsMinor eq true) and IsActive eq true and IsPending eq false and CodePc ne null and IsSpecialPurpose eq false&$orderby=CodePc"
};
snbApp.dataretriever.letsBuild(listDetails);
}
buildSelectOptions();
//***
//Add delay to populate fields to ensure all data retrieved from AJAX calls
//***
var myObj = setTimeout(delayFieldPopulate,5000);
function delayFieldPopulate(){
var optObj = snbApp.optionsobj.getAllOptions();
var osType = $("input[name=os_platform]:checked").val();
snbApp.formmanager.buildForm(osType, optObj);
}
};
function _onQueryFailed(sender, args) {
alert('Request failed.\nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
}
return main
})();
AJAX calls here - called from main/previous file:
var snbApp = window.snbApp || {};
snbApp.dataretriever = (function () {
var listsArray = snbApp.splistarray.getArrayOfListsForObjects();
function getListData(listItem) {
var eventType = event.type;
var baseURL = listItem.url;
$.ajax({
url: baseURL,
type: "GET",
headers: {
"accept": "application/json;odata=verbose",
}
})
.done(function(results){
snbApp.objectbuilderutility.buildObjectFields(results, listItem);
})
.fail(function(xhr, status, errorThrown){
//console.log("Error:" + errorThrown + ": " + myListName);
});
}
function _onQueryFailed(sender, args) {
alert('Request failed.\nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
}
return{
letsBuild:function(item) {
getListData(item);
}
};
})();
Builds a item name object - called from recursive AJAX calls / previous file
var snbApp = window.snbApp || {};
snbApp.objectbuilderutility = (function () {
function formatItemCode(itemCode, eventType){
if(eventType !== 'change'){ //for load event
var pattern = /^CE/;
var result = pattern.test(itemCode);
if(result){
return itemCode.slice(2);
}else{
return itemCode.slice(0,3);
}
}else{ //for change event
var pattern = /^CE/;
var result = pattern.test(itemCode);
if(result){
return itemCode.slice(2);
}else{
return itemCode.slice(3);
}
}
}
return{
buildObjectFields: function(returnedObj, listItem){ //results:returnedObj, prevItem:listItem
//***
//For SharePoint list data
//***
if (listItem.listName !== "SNB_SecondaryActivityCodes") {
var theList = listItem.listName;
var firstQueryParam = listItem.listObj.codeDigits;
var secondQueryParam = listItem.listObj.codeDescription;
var returnedItems = returnedObj.d.results;
var bigStringOptions = "";
//regex to search for SecondaryFunctionCodes in list names
var pattern = /SecondaryFunctionCodes/;
var isSecFunction = pattern.test(theList);
if(isSecFunction){
bigStringOptions = "<option value='0' selected>Not Applicable</option>";
}else{
bigStringOptions = "<option value='0' disabled selected>Select Option</option>";
}
$.each(returnedItems, function (index, item) {
var first = "";
var second = "";
for (var key in item) {
if (item.hasOwnProperty(key)) {
if (key != "__metadata") {
if (key == firstQueryParam) {
first = item[key];
}
if (key == secondQueryParam) {
second = item[key];
}
}
}
}
bigStringOptions += "<option value=" + first + " data-code=" + first + ">" + second + "</option>";
});
var str = theList.toLowerCase();
snbApp.optionsobj.updateFunctionOrActivity(theList.toLowerCase(), bigStringOptions);
//***
//For other API
//***
} else {
var theList = listItem.listName;
var bigStringOptions = "<option value='0' disabled selected>Select Option</option>";
var returnedItems = returnedObj.value;
for(var i = 0; i < returnedItems.length; i++){
var item = returnedItems[i];
//***
//change event type means the user selected a field
//***
if(listItem.eventType === "change"){
var siteCodeChange = item.SiteCodePc;
if (typeof siteCodeChange === "string" & siteCodeChange != "null") {
siteCodeChange = siteCodeChange < 6 ? siteCodeChange : siteCodeChange.slice(3);
}
bigStringOptions += "<option value='" + item.Id + "' data-code='" + siteCodeChange + "' data-isDivSite='" + item.IsDivisionSite + "' data-isDistSite='" + item.IsDistrictSite + "' data-divID='" + item.DivisionSiteId + "' data-distID='" + item.DistrictSiteId + "'>(" + siteCodeChange + ") " + item.Name + "</option>";
snbApp.formmanager.buildSelectSiteLocations(bigStringOptions);
//***
//load event which means this happens when the page is loaded
//***
}else{
var siteCodeLoad = item.SiteCodePc;
if (typeof siteCodeLoad === "string" & siteCodeLoad != "null") {
var siteCodeLoad = siteCodeLoad.length < 4 ? siteCodeLoad : siteCodeLoad.slice(0, 3);
}
bigStringOptions += "<option value='" + item.Id + "' data-code='" + siteCodeLoad + "' data-isDivSite='" + item.IsDivisionSite + "' data-isDistSite='" + item.IsDistrictSite + "' data-divID='" + item.DivisionSiteId + "' data-distID='" + item.DistrictSiteId + "'>(" + siteCodeLoad + ") " + item.Name + "</option>";
snbApp.optionsobj.updateFunctionOrActivity(theList.toLowerCase(), bigStringOptions);
}
}
}
}
};
})();
Form management - called from previous file, gets all select elements on page and appends items from the object in previous file to each select element.
var snbApp = window.snbApp || {};
//Direct interface to the form on the page
snbApp.formmanager = (function(){
var form = {};
form.content_holder = document.getElementById("content_holder");
form.sec_act_codes = document.getElementById("snb_secondary_activity_codes");
form.prim_func_codes = document.getElementById("snb_primary_function_codes");
form.sec_func_codes = document.getElementById("snb_secondary_function_codes");
form.sec_func_nums = document.getElementById("snb_secondary_function_numbers");
form.host_options = document.getElementById("snb_host_options");
form.site_locs_div = document.getElementById("site_locations_div");
form.site_locs = document.getElementById("snb_site_locations");
form.dc_or_off_prem_div = document.getElementById("dc_or_off_premise_div");
form.dc_off_prem_codes = document.getElementById("snb_dc_offpremise_codes");
var snb_secondary_activity_codes = "";
var snb_primary_function_codes = "";
var snb_secondary_function_codes = "";
var snb_secondary_function_numbers = "";
var snb_host_options = "";
var snb_site_locations = "";
var snb_dc_op = "";
//builds the server location hosting options selection
function buildLocationTypeSelector() {
var locationOptionsString = "<option value='0' disabled selected>Select Option</option>";
for (var i = 0; i < locationOptions.length; i++) {
var location = locationOptions[i];
locationOptionsString += "<option value=" + location.hostLocale + " data-code=" + location.code + ">" + location.hostLocale + "</option>";
}
$("#snb_host_options").append(locationOptionsString);
}
function buildSiteLocations(bigString){
if(bigString === undefined){
var siteLocs = document.getElementById("snb_site_locations");
var newOption = document.createElement("option");
newOption.setAttribute("value", 0);
newOption.setAttribute("disabled","disabled");
newOption.setAttribute("checked","checked");
var newText = document.createTextNode("Select Option");
newOption.appendChild(newText);
siteLocs.appendChild(newOption);
} else{
var siteLocs = document.getElementById("snb_site_locations");
siteLocs.innerHTML = bigString;
}
}
return {
form:form,
buildSelectSiteLocations: function(bigString){
buildSiteLocations(bigString);
},
buildForm: function (osType, optObj) {
buildLocationTypeSelector();
buildSecondaryFunctionNumberSelector();
buildSiteLocations();
if(osType === 'windows'){
$("#snb_secondary_activity_codes").append(optObj.windows.secondary_activity);
$("#snb_primary_function_codes").append(optObj.windows.primary_function);
$("#snb_secondary_function_codes").append(optObj.windows.secondary_function);
$("#snb_site_locations").append(optObj.windows.site_location);
$("#snb_dc_offpremise_codes").append(optObj.windows.dc_offpremise);
}else{
$("#snb_secondary_activity_codes").append(optObj.unix.secondary_activity);
$("#snb_primary_function_codes").append(optObj.unix.primary_function);
$("#snb_secondary_function_codes").append(optObj.unix.secondary_function);
$("#snb_site_locations").append(optObj.unix.site_location);
$("#snb_dc_offpremise_codes").append(optObj.unix.dc_offpremise);
}
}
};
})();
Thanks in advance.
Need some help to get a normal or larger image from posts using the Facebook Graph API, at the moment it only gives a 130 x 130 px image in the object.
function fbFetch() {
var access_token = "";
var url = "https://graph.facebook.com/?ids=intel&fields=posts.limit(5){message,created_time,picture.type(normal)}&access_token=' + access_token;
$.getJSON(url, function(response) {
var messages = [];
Object.getOwnPropertyNames(response).forEach(function(page, idx, array) {
response[page].posts.data.forEach(function(post, idx, array) {
messages.push(post);
});
});
function compare(a, b) {
if (a.created_time < b.created_time)
return -1;
if (a.created_time > b.created_time)
return 1;
return 0;
}
var html = "<ul>";
$.each(messages.sort(compare), function(i, fb) {
if (typeof fb.picture != "undefined") {
html += "<li>" + fb.message + "</br>" + '<img SRC="' + fb.picture + '">' + "</br>" + fb.created_time + "</li></br>";
} else {
html += "<li>" + fb.message + "</br>" + fb.created_time + "</li></br>";
}
});
html += "</ul>";
$('.facebookfeed').html(html);
});
}
fbFetch();
<div class="facebookfeed"></div>
Fiddle here: http://jsfiddle.net/6fhq3dat/17/
use full_picture instead of picture
var url = "https://graph.facebook.com/?ids=intel&fields=posts.limit(3){message,created_time,full_picture}&access_token=" + access_token;
demo
I am at my wits end here.
I am trying to build a report which retrieves data from a JSON array. Which is stored in a local stored file (using HTML5 FileAPI).
I have divided the build into two functions: Report and FileReader
I am able to read the file, and send the content to console.log inside the FileReader function. But I am NOT ABLE to send that data outside the function. I've tried so many different approaches that I've lost count on which and how many. I tried Global Variable, but that stays blank outside the FileReader Function. I tried Promise.deferred etc. etc.
Please help!
var JsData = '';
function fileReader(hente) {
var deferred = $.Deferred();
fs.root.getFile(hente, {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.readAsText(file, "utf-8");
reader.onloadend = function(e) {
JsData = this.result;
console.log('JsData: ' + JsData ); //Check that data has been read
// try this
//return JsData;
// Try Defferred
return deferred.promise(JsData);
};
}, errorHandler);
}, errorHandler);
};
function createDokument(dokName) {
var ClientTable = '';
var kake = Cookies.get(dokNavn);
var visits = JSON.parse(kake);
$.each(visits, function(index, value) {
var hente = value.dok + '_' + value.nr;
//Make sure the variable is blank, for no reason at all
JsData = '';
var promise = fetchVisitInfo(hente + '.oln');
promise.then(function(result) {
/// Test if variable has retrieved the data
console.log('JsData: ' + JsData );
$.each(JsData, function (i,v) {
ClientTable += ' <tr>';
ClientTable += ' <td colspan="3">' + v['orderSum'].Knr + ', ' + v['orderSum'].firma + '</td>';
ClientTable += ' <td width="3%">' + v['orderSum'].oldV + v['orderSum'].newV + '</td>';
ClientTable += ' <td width="15%">' + v['orderSum'].newS + '</td>';
ClientTable += ' <td width="15%">' + v['orderSum'].oldS + '</td>';
ClientTable += ' </tr>';
});
});
});
Any ideas ??
I have a list of my recent Tumblr audio posts coming in through the Tumblr API and with each post, an artist summary from the Last.fm API is attached. I have the data coming in correctly, but the audio posts are not showing up in chronological order. Every time I refresh the page, they appear in random order. I would like to have the most recent audio post appear at the top. Here is my code:
$(document).ready(function() {
var user = "nycxnc";
var key = "###";
var type = "audio";
var limit = "5";
var url = "https://api.tumblr.com/v2/blog/" + user +
".tumblr.com/posts/" + type + "?callback=?&filter=" + type +
"&limit=" + limit + "" + key + "";
$.getJSON(url, function(data) {
$.each(data.response.posts, function(i, item) {
var artist = item.artist;
var track = item.track_name;
var url2 =
"http://ws.audioscrobbler.com/2.0/?method=artist.getInfo&track=" +
track + "&artist=" + artist +
"&api_key=###&autocorrect=1&format=json&callback=?";
$.getJSON(url2, function(data) {
$.each(data, function(i, item) {
artist_summary = item.bio.summary;
artist_image = item.image[4]["#text"];
$(".au-wrapper").append(
'<div class="audio-wrapper"><img src=' + artist_image +
'><div class="audio-meta"><span class="audio-title">' +
track + '</span><div class="audio-artist">' + artist +
'</div></div><div class="url">' + artist_summary +
'</div></div>');
});
});
});
});
});
How can I combine the data from two API in the correct order, if the second JSON url (Last.fm) is dependent on data from the Tumblr API? I'm new to Javascript and I'm creating this script as a learning experiment, so any help would be very appreciated. (: Thank you.