Images in JSON AngularJS - javascript

I'm new to AngularJS, so sometimes when I do some mistake that is obvious, I still can't figure out what is going wrong with my code. So saying, here is my doubt:
HTML code:
<body ng-controller = "Ctrl">
<script id="Page6.html" type="text/ng-template">
<div class="list card" style="background-color: beige">
<div class="item item-icon-left">
<i class="icon ion-home"></i>
<input type="text" placeholder = "Enter display name" ng-model="user.nam">
</div>
<a ng-click = "saveedit(user)"<button class="button button-clear">SAVE DETAILS</button></a>
</div>
</script>
</body>
CONTROLLER.JS
.controller('Ctrl',function($scope,$rootScope,ContactService){
$rootScope.saveedit=function(user) {
ContactService.save({names: user.nam, image:"images.jpg"},ContactService.getid("Donkey"));
}
});
THIS IS THE SERVICE:
.service('ContactService', function () {
var items = [
{ id: 1, names: 'Dolphin', image: 'dolphin.jpg',}, { id: 2, names: 'Donkey', image: 'donkey.jpg'}, { id: 3, empid: 'FG2043', image: 'penguin.jpg'}];
var im = [{image: ''}];
var ctr=0;
var uid=3;
this.save = function (contact,id) {
ctr=0;
for (i=0;i<items.length;i++) {
if(items[i].id == id)
{
im[0].image= items[i].image;
ctr=100;
break;
}
}
uid = (uid+1);
contact.id = uid;
items.push(contact);
if (ctr==100 ) {
alert("in save putting the image");
items[contact.id].image = im[0].image; //doubt
alert("finished putting image");
}
}
//simply search items list for given id
//and returns the object if found
this.getid = function (name) {
for (i=0;i<items.length;i++) {
if (items[i].names == name) {
return (i+1);
}
}
}
//simply returns the items list
this.list = function () {
return items;
}
});
The problem I am facing is this: Everything works, except one thing. In ContactService, push() function, the line I have commented as //doubt is not getting executed.
The alert before it "in save putting the image" runs, but the alert "finished putting image" doesn't. What is the mistake there??

The problem here is that you're using the id's, which start at 1, to navigate in an array whose indexes start at 0.
To access the most recently pushed element, you should rather do :
items[contact.id - 1].image = im[0].image;
But you actually don't need to access the array : items[contact.id - 1] will return the object that you just pushed, and which is already referenced by variable contact, so you could just do :
contact.image = im[0].image;

Related

javascript InnerHTML adding cards only one time

I'm trying to add a bootstrap card inside a div called [itemscontainer] using javascript
by document.getElementById("itemscontainer").innerHTML so i want the cards to be inserted inside the itemscontainer only one time like this :-
but the problem is the items cards keeps reapet them salves more than one time like:-
what i want is to clear the itemscontainer first before adding the cards and this is what i have tried so that the items will be only one cards for each item
// clear function
function clear(){
document.getElementById("ssst").innerHTML = ""
}
// listener append all items to the inventory
window.addEventListener('message', (event) => {
let data = event.data
if(data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
if(document.getElementById("ssst").innerHTML == ""){
clear()
}else{
document.getElementById("ssst").innerHTML +=
"<div class='card holder'>"+
'<div class="card-body">'+
'<img src="icons\\'+icon+'" style="position:absolute;left:15%;width:40px; height:36px;" alt="">'+
'<h4 id="counter">'+count+'</h4>'+
'</div>'+
'<span class="itemname">'+name+'</span>'+
'</div>";'
}
}
})
The real solution is to figure out why you are getting the items more than once. With the information you provided that is impossible for me to answer. So the only thing we can recommend is how to prevent items from being added more than once.
If your messaging system returns duplicates you can determine if you have seen it. If you do, replace it. Otherwise add it.
window.addEventListener('message', (event) => {
const data = event.data;
console.log(data)
if (data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
const html = `
<div class='card holder' data-name="${name}">
<div class="card-body">
<img src="icons/${icon}" style="position:absolute;left:15%;width:40px; height:36px;" alt="${icon}">
<h4 id="counter">${count}</h4>
</div>
<span class="itemname">${name}</span>
</div>`;
const elemExists = document.querySelector(`[data-name="${name}"]`);
if (elemExists) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
elemExists.replaceWith(doc.body);
} else {
document.getElementById("ssst").insertAdjacentHTML("beforeend", html);
}
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1,
icon: 'foo'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'bar',
count: 40,
icon: 'barrrrrr'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1000,
icon: 'foo'
}
});
<div id="ssst"></div>
Why are you using the if statement, what are you checking for?
remove the if statement, I can't see the reason for it to be used here.
clear()
and the rest of your code.

How to loop through HTML elements and populate a Json-object?

I'm looping through all the html tags in an html-file, checking if those tags match conditions, and trying to compose a JSON-object of a following schema:
[
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' },
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' },
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' }
]
But I'd like to create the new entry only for elements, classed "header", all the other elements have to be added to earlier created entry. How do I achieve that?
Current code:
$('*').each((index, element) => {
if ( $(element).hasClass( "header" ) ) {
jsonObject.push({
title: $(element).text()
});
};
if( $(element).hasClass( "date" )) {
jsonObject.push({
date: $(element).text()
});
}
//links.push($(element))
});
console.log(jsonObject)
Result is:
{
title: 'TestA'
},
{ date: '10.10.10' },
{
title: 'TestB'
},
{ date: '10.10.11' }
I'd like it to be at this stage something like:
{
title: 'TestA'
,
date: '10.10.10' },
{
title: 'TestB'
,
date: '10.10.11' }
UPD:
Here's the example of HTML file:
<h1 class="header">H1_Header</h1>
<h2 class="date">Date</h2>
<p>A.</p>
<p>B.</p>
<p>С.</p>
<p>D.</p>
<a class="source">http://</a>
<h1 class="header">H1_Header2</h1>
<h2 class="date">Date2</h2>
<p>A2.</p>
<p>B2.</p>
<p>С2.</p>
<p>D2.</p>
<a class="source">http://2</a>
Thank you for your time!
Based on your example Html, it appears everything you are trying to collect is in a linear order, so you get a title, date, body and link then a new header with the associated items you want to collect, since this appears to not have the complication of having things being ordered in a non-linear fasion, you could do something like the following:
let jsonObject = null;
let newObject = false;
let appendParagraph = false;
let jObjects = [];
$('*').each((index, element) => {
if ($(element).hasClass("header")) {
//If newObject is true, push object into array
if(newObject)
jObjects.push(jsonObject);
//Reset the json object variable to an empty object
jsonObject = {};
//Reset the paragraph append boolean
appendParagraph = false;
//Set the header property
jsonObject.header = $(element).text();
//Set the boolean so on the next encounter of header tag the jsobObject is pushed into the array
newObject = true;
};
if( $(element).hasClass( "date" )) {
jsonObject.date = $(element).text();
}
if( $(element).prop("tagName") === "P") {
//If you are storing paragraph as one string value
//Otherwise switch the body var to an array and push instead of append
if(!appendParagraph){ //Use boolean to know if this is the first p element of object
jsonObject.body = $(element).text();
appendParagraph = true; //Set boolean to true to append on next p and subsequent p elements
} else {
jsonObject.body += (", " + $(element).text()); //append to the body
}
}
//Add the href property
if( $(element).hasClass("source")) {
//edit to do what you wanted here, based on your comment:
jsonObject.link = $(element).next().html();
//jsonObject.href= $(element).attr('href');
}
});
//Push final object into array
jObjects.push(jsonObject);
console.log(jObjects);
Here is a jsfiddle for this: https://jsfiddle.net/Lyojx85e/
I can't get the text of the anchor tags on the fiddle (I believe because nested anchor tags are not valid and will be parsed as seperate anchor tags by the browser), but the code provided should work in a real world example. If .text() doesn't work you can switch it to .html() on the link, I was confused on what you are trying to get on this one, so I updated the answer to get the href attribute of the link as it appears that is what you want. The thing is that the anchor with the class doesn't have an href attribute, so I'll leave it to you to fix that part for yourself, but this answer should give you what you need.
$('*').each((index, element) => {
var obj = {};
if ( $(element).hasClass( "header" ) ) {
obj.title = $(element).text();
};
if( $(element).hasClass( "date" )) {
obj.date = $(element).text()
}
jsonObject.push(obj);
});
I don't know about jQuery, but with JavaScript you can do with something like this.
const arr = [];
document.querySelectorAll("li").forEach((elem) => {
const obj = {};
const title = elem.querySelector("h2");
const date = elem.querySelector("date");
if (title) obj["title"] = title.textContent;
if (date) obj["date"] = date.textContent;
arr.push(obj);
});
console.log(arr);
<ul>
<li>
<h2>A</h2>
<date>1</date>
</li>
<li>
<h2>B</h2>
</li>
<li>
<date>3</date>
</li>
</ul>
Always use map for things like this. This should look something like:
let objects = $('.header').get().map(el => {
return {
date: $(el).attr('date'),
title: $(el).attr('title'),
}
})

Mapping a nested object as an observable from a complex JSON using the create callback

I've got a complex object in a JSON format. I'm using Knockout Mapping, customizing the create callback, and trying to make sure that every object that should be an observable - would actually be mapped as such.
The following code is an example of what I've got:
It enables the user to add cartItems, save them (as a JSON), empty the cart, and then load the saved items.
The loading part fails: It doesn't display the loaded option (i.e., the loaded cartItemName). I guess it's related to some mismatch between the objects in the options list and the object bounded as the cartItemName (see this post), but I can't figure it out.
Code (fiddle):
var cartItemsAsJson = "";
var handlerVM = function () {
var self = this;
self.cartItems = ko.observableArray([]);
self.availableProducts = ko.observableArray([]);
self.language = ko.observable();
self.init = function () {
self.initProducts();
self.language("english");
}
self.initProducts = function () {
self.availableProducts.push(
new productVM("Shelf", ['White', 'Brown']),
new productVM("Door", ['Green', 'Blue', 'Pink']),
new productVM("Window", ['Red', 'Orange'])
);
}
self.getProducts = function () {
return self.availableProducts;
}
self.getProductName = function (product) {
if (product) {
return self.language() == "english" ?
product.productName().english : product.productName().french;
}
}
self.getProductValue = function (selectedProduct) {
// if not caption
if (selectedProduct) {
var matched = ko.utils.arrayFirst(self.availableProducts(), function (product) {
return product.productName().english == selectedProduct.productName().english;
});
return matched;
}
}
self.getProductColours = function (selectedProduct) {
selectedProduct = selectedProduct();
if (selectedProduct) {
return selectedProduct.availableColours();
}
}
self.addCartItem = function () {
self.cartItems.push(new cartItemVM());
}
self.emptyCart = function () {
self.cartItems([]);
}
self.saveCart = function () {
cartItemsAsJson = ko.toJSON(self.cartItems);
console.log(cartItemsAsJson);
}
self.loadCart = function () {
var loadedCartItems = ko.mapping.fromJSON(cartItemsAsJson, {
create: function(options) {
return new cartItemVM(options.data);
}
});
self.cartItems(loadedCartItems());
}
}
var productVM = function (name, availableColours, data) {
var self = this;
self.productName = ko.observable({ english: name, french: name + "eux" });
self.availableColours = ko.observableArray(availableColours);
}
var cartItemVM = function (data) {
var self = this;
self.cartItemName = data ?
ko.observable(ko.mapping.fromJS(data.cartItemName)) :
ko.observable();
self.cartItemColour = data ?
ko.observable(data.cartItemColour) :
ko.observable();
}
var handler = new handlerVM();
handler.init();
ko.applyBindings(handler);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://rawgit.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js
"></script>
<div>
<div data-bind="foreach: cartItems">
<div>
<select data-bind="options: $parent.getProducts(),
optionsText: function (item) { return $parent.getProductName(item); },
optionsValue: function (item) { return $parent.getProductValue(item); },
optionsCaption: 'Choose a product',
value: cartItemName"
>
</select>
</div>
<div>
<select data-bind="options: $parent.getProductColours(cartItemName),
optionsText: $data,
optionsCaption: 'Choose a colour',
value: cartItemColour,
visible: cartItemName() != undefined"
>
</select>
</div>
</div>
<div>
<button data-bind="text: 'add cart item', click: addCartItem" />
<button data-bind="text: 'empty cart', click: emptyCart" />
<button data-bind="text: 'save cart', click: saveCart" />
<button data-bind="text: 'load cart', click: loadCart" />
</div>
</div>
What needs to be changed to fix it?
P.S.: I've got another piece of code (see it here) that demonstrates a persistance of the selected value even after changing the options - though there optionsValue is a simple string, while here it's an object.
EDIT:
I figured out the problem: the call ko.mapping.fromJS(data.cartItemName) creates a new productVM object, which is not one of the objects inside availableProducts array. As a result, none of the options corresponds to the productVM contained in the loaded cartItemName, so Knockout thereby clears the value altogether and passes undefined.
But the question remains: how can this be fixed?
In the transition from ViewModel -> plain object -> ViewModel you loose the relation between the products in your cart and the ones in your handlerVM.
A common solution is to, when loading a plain object, manually search for the existing viewmodels and reference those instead. I.e.:
We create a new cartItemVM from the plain object
Inside its cartItemName, there's an object that does not exist in handlerVM.
We look in handlerVM for a product that resembles this object, and replace the object by the one we find.
In code, inside loadCart, before setting the new viewmodels:
loadedCartItems().forEach(
ci => {
// Find out which product we have:
const newProduct = ci.cartItemName().productName;
const linkedProduct = self.availableProducts()
.find(p => p.productName().english === newProduct.english());
// Replace the newProduct by the one that is in `handlerVM`
ci.cartItemName(linkedProduct)
}
)
Fiddle: https://jsfiddle.net/7z6010jz/
As you can see, the equality comparison is kind of ugly. We look for the english product name and use it to determine the match. You can also see there's a difference in what is observable and what isn't.
My advice would be to use unique id properties for your product, and start using those. You can create a simpler optionsValue binding and matching new and old values happens automatically. If you like, I can show you an example of this refactor as well. Let me know if that'd help.

retrieving JSON object using a html link and displaying the json data on a html page

I want to retrieve a JSON object when the user clicks the html link. i want to retrieve it by it's ID and display everything in the object on the html page. I am also using session storage..... I want the user to be taken to the info.html page after they follow the html link.
my html code so far:
<div class="col">
<!-- team-img -->
<div class="team-block">
<div class="team-content">
<h4 class="text-white mb0">Chocolate Cake </h4>
<p class="team-meta">Large</p>
</div>
<div class="overlay">
<div class="text">
<h4 class="mb0 text-white"> Chocolate Cake </h4>
<p class="mb30 team-meta"> Large </p>
<p>Large Chocolate cake. 15 servings.</p>
<p>Further info</p>
</div>
</div>
</div>
</div>
The ID of the cake is "1" In the JSON file. I want all the details of the cake to be displayed when the user clicks the link. I want the user to be taken to info.html. The data of the cake which is stored in the JSON should be displayed there. The problem is with this line:
<p>Further info</p>
My JSON file is called cakes.json
below is my .js file
var ajax=function(url,success)
{
var ajaxRequest = new XMLHttpRequest();
var handleResponse=function()
{
if(ajaxRequest.readyState===4)
{
if(ajaxRequest.status===200)
{
var data=JSON.parse(ajaxRequest.responseText);
success(data); //this will call populateList
}
}
}
ajaxRequest.addEventListener("readystatechange",handleResponse,false);
ajaxRequest.open('GET', url, true);
ajaxRequest.send(null);
}
var navList;
var contentDiv;
function createHandler(car)
{
return function(){
sessionStorage.setItem("cake",JSON.stringify(cake));
}
}
function populateList(cakes)
{
navList=document.getElementById("nav");
contentDiv=document.getElementById("content");
cakes.forEach(function(cake){
var newLi=document.createElement("ul");
var newLink=document.createElement("a");
newLink.innerHTML=cake.name;
newLink.setAttribute("href","info.html");
newLink.addEventListener("click", createHandler(cake), false)
newLi.appendChild(newLink);
navList.appendChild(newLi);
})
}
function init(){
ajax("data/cakes.json",populateList);
}
init();
JSON file:
[
{
"id":1,
"cake":"chocolate cake",
"servings":"15",
"size":"10",
"code":"ed39"
},
]
Any Help would be greatly appreciated.
I've done an example of how this can be done using your structure of array containing multiple objects. Also I've modified your html added data-id parameter instead of id usually you don't save the id of an object in the id of html element(not best practice), you can use the data- params, there is also a function in jQuery that reads these parametes $(selector).data('identifier')
var json = [{
"id": 1,
"cake": "chocolate cake",
"servings": "15",
"size": "10",
"code": "ed39"
}, {
"id": 2,
"cake": "vanilla cake",
"servings": "15",
"size": "10",
"code": "ed39"
}];
$(document).on('click', '.cakeDetailsButton', function() {
var id = $(this).data('id'),
details = getDetailsById(id), //Here all the details for the cake
resultDiv = $('.result');
resultDiv.text('');
$.each(details, function(key, value) {
resultDiv.append(key + ' -- ' + value + '<br/>') // Here you can do whatever with the details
})
});
function getDetailsById(id) {
for (var i = 0; i < json.length; i++) {
if (json[i].id == id) {
return json[i];
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p><a class="cakeDetailsButton" href="#" data-id="1">Further info 1</a></p>
<p><a class="cakeDetailsButton" href="#" data-id="2">Further info 2</a></p>
<div class="result">
</div>
EDIT - Update
In order to add your json in the localStorage in order to access it from anywhere update the following
.js file
function populateList(cakes)
{
localStorage.setItem('cakesJson', cakes); //This adds an object to the localStorage with the key cakesJson and value the cakes object
navList=document.getElementById("nav");
contentDiv=document.getElementById("content");
cakes.forEach(function(cake){
var newLi=document.createElement("ul");
var newLink=document.createElement("a");
newLink.innerHTML=cake.name;
newLink.setAttribute("href","info.html");
newLink.addEventListener("click", createHandler(cake), false)
newLi.appendChild(newLink);
navList.appendChild(newLi);
})
}
Also then in the code provided by me above you should change the following
function getDetailsById(id) {
var json = localStorage.getItem("cakesJson"); // Here you retrieve the values stored in the localStorage
for (var i = 0; i < json.length; i++) {
if (json[i].id == id) {
return json[i];
}
}
}
The event listener remains the same as in the snippet
$(document).on('click', '.cakeDetailsButton', function() {
var id = $(this).data('id'),
details = getDetailsById(id), //Here all the details for the cake
resultDiv = $('.result');
resultDiv.text('');
$.each(details, function(key, value) {
resultDiv.append(key + ' -- ' + value + '<br/>') // Here you can do whatever with the details
})
});

Knockout.js - Data binding outputting function text when not using parens

I am new to Knockout and have been trying to follow code examples and the documentation, but keep running into an issue. My data bindings printing the Knockout observable function, not the actual values held by my observable fields. I can get the value if I evaluate the field using (), but if you do this you do not get any live data-binding / updates.
Below are some code snippets from my project that are directly related to the issue I am describing:
HTML
<div class="col-xs-6">
<div data-bind="foreach: leftColSocialAPIs">
<div class="social-metric">
<img data-bind="attr: { src: iconPath }" />
<strong data-bind="text: name"></strong>:
<span data-bind="text: totalCount"></span>
</div>
</div>
</div>
Note: leftColSocialAPIs contains an array of SocialAPIs. I can show that code too if needed.
Initializing the totalcount attribute
var SocialAPI = (function (_super) {
__extends(SocialAPI, _super);
function SocialAPI(json) {
_super.call(this, json);
this.totalCount = ko.observable(0);
this.templateName = "social-template";
}
SocialAPI.prototype.querySuccess = function () {
this.isLoaded(true);
appManager.increaseBadgeCount(this.totalCount());
ga('send', 'event', 'API Load', 'API Load - ' + this.name, appManager.getRedactedURL());
};
SocialAPI.prototype.toJSON = function () {
var self = this;
return {
name: self.name,
isActive: self.isActive(),
type: "social"
};
};
return SocialAPI;
})(API);
Updating totalcount attribute for LinkedIn
var LinkedIn = (function (_super) {
__extends(LinkedIn, _super);
function LinkedIn(json) {
json.name = "LinkedIn";
json.iconPath = "/images/icons/linkedin-16x16.png";
_super.call(this, json);
}
LinkedIn.prototype.queryData = function () {
this.isLoaded(false);
this.totalCount(0);
$.get("http://www.linkedin.com/countserv/count/share", { "url": appManager.getURL(), "format": "json" }, this.queryCallback.bind(this), "json").fail(this.queryFail.bind(this));
};
LinkedIn.prototype.queryCallback = function (results) {
if (results != undefined) {
results.count = parseInt(results.count);
this.totalCount(isNaN(results.count) ? 0 : results.count);
}
this.querySuccess();
};
return LinkedIn;
})(SocialAPI);
In the <span data-bind="text: totalCount"></span>, I expect to see a number ranging from 0-Integer.MAX. Instead I see the following:
As you can see, its outputting the knockout function itself, not the value of the function. Every code example I've seen, including those in the official documentation, says that I should be seeing the value, not the function. What am I doing wrong here? I can provide the full application code if needed.
Not sure, but KO view models obviously tend to bind own (not inherited through prototypes) observable properties only. So you should rewrite your code to supply totalCount observable for every social network separately.

Categories