Change avatar with vue.js without refresh? - vue.js

I have this in view:
<div class="seller_image" :style="{background: 'url(' + user_credentials.avatar +')', backgroundSize: 'cover ', display: 'block'}">
</div>
In vue i have this:
setAvatar:function(x,y,w,h){
this.setAvatarLoader = true;
var data = new FormData();
this.x1 = $('#x1').val();
this.y1 = $('#y1').val();
this.w = $('#w').val();
this.h = $('#h').val();
this.x2 = $('#x2').val();
this.y2 = $('#y2').val();
data.append('avatar',this.$els.fileAvatarImage.files[0]);
data.append('x1',this.x1);
data.append('x2',this.x2);
data.append('y1',this.y1);
data.append('y2',this.y2);
data.append('w',this.w);
data.append('h',this.h);
user_id = this.user_credentials.user_id;
this.$http.post('/profile/' + user_id + '/basic_info/set_avatar',data).then(function(response){
this.avatarImageSet = false;
public_path = response.data.public_path;
url_path = response.data.url_path;
filename = response.data.filename;
this.setAvatarLoader = false;
this.basic_status = true;
this.basic_success_message = response.data.successMsg;
this.profile_image = url_path;
this.user_credentials.avatar = url_path
this.getAvatar();
$("html, body").animate({ scrollTop: 0 }, "slow");
}, function(response) {
this.setAvatarLoader = false;
$('#myModal').modal('hide');
this.getAvatar();
console.log('error');
});
},
When I refresh the page I get the avatar but in time when I set it it does not change the image.
Any suggestion?

As #AWolf said, it's difficult to guess what's the problem with your code because I can see only a part of your code base.
Another possible issue could be the url_path. If it remains the same, will never change. So, you need to append the timestamp:
this.user_credentials.avatar = url_path + '?' + Date.now()
https://jsfiddle.net/pespantelis/fy0re26m/

As mentioned in the comments, try to avoid jQuery because it's most of the time not needed and it is making things more complicated.
Please have a look at the demo below for a simple image uploader/avatar changer or at this fiddle.
The demo just opens a file picker dialog and then the returned file is used to update the displayed image. (Posting to server is not added in the demo.)
To your code:
Something like $('#x1').val() shouldn't be done with Vue.js because in Vue you're doing that model driven.
So the only source of truth is your data model and not the stuff displayed in the DOM.
Not sure what you're trying to do with the x1,y1, ... code. That's not clear from your snippet with-out the html markup.
new Vue({
el: '#app',
data() {
return {
user_credentials: {
avatar: 'https://unsplash.it/100/100'
}
}
},
methods: {
changeAvatar() {
const input = document.createElement('input');
let self = this;
input.setAttribute("type", "file");
input.addEventListener('change', function(e) {
// uploading code from this fiddle: http://jsfiddle.net/vacidesign/ja0tyj0f/
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
// image is loaded callback
self.user_credentials.avatar = e.target.result;
// here you can post the data to your backend...
};
reader.readAsDataURL(this.files[0]);
}
})
input.click(); // opening dialog
return false; // avoiding navigation
}
}
})
.seller_image {
width: 200px;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div class="seller_image" :style="{background: 'url(' + user_credentials.avatar +')', backgroundSize: 'cover ', display: 'block'}">
</div>
<button #click="changeAvatar()">
Change
</button>
</div>

Related

Update v-html without misbehaving focus on typing VUE JS

I need help,
Requirement
when the user types in an input box I want to highlight the link with blue color if any
My Research
when I dig into it, I realize that without using a contenteditable div it's not possible to do, also there is no v-model associated with contenteditable div I am manually updating the state.
so far I have this, courtesy- contenteditable div append a html element and v-model it in Vuejs
<div id="app"><div class="flex">
<div class="message" #input="updateHtml" v-html="html" contenteditable="true"></div>
<br>
<div class="message">{{ html }}</div>
</div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
html: 'some text',
},
methods: {
updateHtml: function(e) {
this.html = e.target.innerHTML;
},
renderHtml: function(){
this.html += '<img src="https://cdn-images-1.medium.com/max/853/1*FH12a2fX61aHOn39pff9vA.jpeg" alt="" width=200px>';
}
}
});</script>
Issue
every time user types something, the focus is misbehaving which is strange to me, I want v-html to update along with user types #keyup,#keydown also have the same behavior.it works ok on #blur #focusout events, but that's not what I want
Appreciate Help.Thanks
I figured it out myself. Posting the answer so that may help other developers. v-HTML doesn't do all the trick. You’ll need to store the cursor position so it can be restored properly each time the content updates as well as parse the content so that it renders as expected. Here is the example
HTML
<p>
An example of live syntax highlighting in a content-editable element. The hard part is storing and restoring selection after changing the DOM to account for highlighting.
<p>
<div contentEditable='true' id='editor'>
Edit text here. Try some words like bold and red
</div>
<p>
Just a demo trivial syntax highlighter, should work with any syntax highlighting you want to implement.
</p>
JS
const editor = document.getElementById('editor');
const selectionOutput = document.getElementById('selection');
function getTextSegments(element) {
const textSegments = [];
Array.from(element.childNodes).forEach((node) => {
switch(node.nodeType) {
case Node.TEXT_NODE:
textSegments.push({text: node.nodeValue, node});
break;
case Node.ELEMENT_NODE:
textSegments.splice(textSegments.length, 0, ...(getTextSegments(node)));
break;
default:
throw new Error(`Unexpected node type: ${node.nodeType}`);
}
});
return textSegments;
}
editor.addEventListener('input', updateEditor);
function updateEditor() {
const sel = window.getSelection();
const textSegments = getTextSegments(editor);
const textContent = textSegments.map(({text}) => text).join('');
let anchorIndex = null;
let focusIndex = null;
let currentIndex = 0;
textSegments.forEach(({text, node}) => {
if (node === sel.anchorNode) {
anchorIndex = currentIndex + sel.anchorOffset;
}
if (node === sel.focusNode) {
focusIndex = currentIndex + sel.focusOffset;
}
currentIndex += text.length;
});
editor.innerHTML = renderText(textContent);
restoreSelection(anchorIndex, focusIndex);
}
function restoreSelection(absoluteAnchorIndex, absoluteFocusIndex) {
const sel = window.getSelection();
const textSegments = getTextSegments(editor);
let anchorNode = editor;
let anchorIndex = 0;
let focusNode = editor;
let focusIndex = 0;
let currentIndex = 0;
textSegments.forEach(({text, node}) => {
const startIndexOfNode = currentIndex;
const endIndexOfNode = startIndexOfNode + text.length;
if (startIndexOfNode <= absoluteAnchorIndex && absoluteAnchorIndex <= endIndexOfNode) {
anchorNode = node;
anchorIndex = absoluteAnchorIndex - startIndexOfNode;
}
if (startIndexOfNode <= absoluteFocusIndex && absoluteFocusIndex <= endIndexOfNode) {
focusNode = node;
focusIndex = absoluteFocusIndex - startIndexOfNode;
}
currentIndex += text.length;
});
sel.setBaseAndExtent(anchorNode,anchorIndex,focusNode,focusIndex);
}
function renderText(text) {
const words = text.split(/(\s+)/);
const output = words.map((word) => {
if (word === 'bold') {
return `<strong>${word}</strong>`;
}
else if (word === 'red') {
return `<span style='color:red'>${word}</span>`;
}
else {
return word;
}
})
return output.join('');
}
updateEditor();
Hope this helps...

"accept" drop option does not work in aurelia-interactjs

I'm testing out the aurelia-interactjs plugin with the Drag and Drop section of the interactjs demo code. Everything is working fine except the "accept" drop option on the target areas. The targets accept both draggable sources instead of just the one with an id of "yes-drop". In other words, the "inner-dropzone" and "outer-dropzone" targets accept a drop of the "no-drop" source even though the drop options specify accept: '#yes-drop'.
Here's the code:
drag-and-drop.html
<template>
<require from="./dragging-only.css"></require>
<require from="./drag-and-drop.css"></require>
<div interact-draggable.bind="dragOptions" interact-dragend.delegate="dragEnd($event)" interact-dragmove.delegate="dragMoveListener($event)" id="no-drop" class="draggable drag-drop"> #no-drop </div>
<div interact-draggable.bind="dragOptions" interact-dragend.delegate="dragEnd($event)" interact-dragmove.delegate="dragMoveListener($event)" id="yes-drop" class="draggable drag-drop"> #yes-drop </div>
<div interact-dropzone.bind="dropOptions" interact-dropactivate.delegate="dropActivate($event)" interact-dragenter.delegate="dragEnter($event)" interact-dragleave.delegate="dragLeave($event)" interact-drop.delegate="drop($event)" interact-dropdeactivate.delegate="dropDeactivate($event)" id="outer-dropzone" class="dropzone">
#outer-dropzone
<div interact-dropzone.bind="dropOptions" interact-dropactivate.delegate="dropActivate($event)" interact-dragenter.delegate="dragEnter($event)" interact-dragleave.delegate="dragLeave($event)" interact-drop.delegate="drop($event)" interact-dropdeactivate.delegate="dropDeactivate($event)" id="inner-dropzone" class="dropzone">#inner-dropzone</div>
</div>
</template>
drag-and-drop.js (dropOptions is defined at the end)
export class DragAndDrop {
dragMoveListener(event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.detail.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.detail.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the position attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
dragEnd(event) {
var textEl = event.target.querySelector('p');
textEl && (textEl.textContent =
'moved a distance of ' +
(Math.sqrt(event.detail.dx * event.detail.dx +
event.detail.dy * event.detail.dy) | 0) + 'px');
}
dropActivate(customEvent) {
let event = customEvent.detail;
// add active dropzone feedback
event.target.classList.add('drop-active');
}
dragEnter(customEvent) {
let event = customEvent.detail;
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target');
draggableElement.classList.add('can-drop');
draggableElement.textContent = 'Dragged in';
}
dragLeave(customEvent) {
let event = customEvent.detail;
// remove the drop feedback style
event.target.classList.remove('drop-target');
event.relatedTarget.classList.remove('can-drop');
event.relatedTarget.textContent = 'Dragged out';
}
drop(customEvent) {
let event = customEvent.detail;
event.relatedTarget.textContent = 'Dropped';
}
dropDeactivate(customEvent) {
let event = customEvent.detail;
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
dragOptions = {
// enable inertial throwing
inertia: true,
// keep the element within the area of it's parent
restrict: {
restriction: "parent",
endOnly: true,
elementRect: {
top: 0,
left: 0,
bottom: 1,
right: 1
}
},
// enable autoScroll
autoScroll: true,
};
dropOptions = {
// only accept elements matching this CSS selector
accept: '#yes-drop',
// Require a 75% element overlap for a drop to be possible
overlap: '0.75'
};
}
I added a debugger statement in the InteractDraggableCustomAttribute.prototype.attached function within the aurelia-interactjs code and inspected this.options. It is undefined even though the options are clearly set with interact-draggable.bind.
Version 1.0.10 of aurelia-interactjs fixes this issue.

Dojo - don't repeat yourself

Is there a simpler way to write something like this in dojo (instead of having a function for each thing i want to show or hide)? I know there must be a way to avoid such repetition but I'm not sure how to do it.
on(dom.byId("thing_toggle2"), "click", function(){
if(thing_list2.style.display == "none") {
thing_list2.style.display = "block";
dom.byId("toggle2_sign").innerHTML = "(-)";
} else {
thing_list2.style.display = "none";
dom.byId("toggle2_sign").innerHTML = "(+)";
};
});
on(dom.byId("thing_toggle3"), "click", function(){
if(thing_list3.style.display == "none") {
thing_list3.style.display = "block";
dom.byId("toggle3_sign").innerHTML = "(-)";
} else {
thing_list3.style.display = "none";
dom.byId("toggle3_sign").innerHTML = "(+)";
};
});
I didn't test this, but it should give you a starting point. Adding an additional section, would just involve adding to the array of data.
var fnToggle = function(nodeMap) {
var expand = domStyle.get(dom.byId(nodeMap.contentNode), 'display') == 'none';
domStyle.set(dom.byId(nodeMap.contentNode), 'display', expand ? 'block' : '');
html.set(dom.byId(nodeMap.expandoNode), expand ? '+' : '-');
};
var nodes = [
{ eventNode: 'thing_toggle2', contentNode: thing_list2, expandoNode: 'toggle2_sign' },
{ eventNode: 'thing_toggle3', contentNode: thing_list3, expandoNode: 'toggle3_sign' }
];
array.forEach(nodes, function(nodeMap) {
on(dom.byId(nodeMap.eventNode), "click", function(){ fnToggle(nodeMap); });
});
// domStyle -> dojo/dom-style
// html -> dojo/html
// array -> dojo/_base/array
You could use dojo/fx/Toggler which uses dojo/_base/fx.fadeOut and dojo/_base/fx.fadeIn
I actually decided to do this as a plain old javascript function which I can reuse elsewhere, including pages that might not need dojo. Something like this:
[1]
[2]
<div id="myName" style="display:none;">Antonio</div>
<div id="anothername" style="display:none;">Elliot</div>
<script type="text/javascript">
function showlayer(layer){
var myLayer = document.getElementById(layer).style.display;
if(myLayer=="none"){
document.getElementById(layer).style.display="block";
} else {
document.getElementById(layer).style.display="none";
};
}
</script>

Multiple Bing Map Pushpins from SQL not showing in Firefox & Chrome but do in IE

I'm displaying a Bing Map (v7) in my Webmatrix2 website with a series of pushpins & infoboxes drawn from a SQL Express database using a JSON enquiry.
While the maps appears in all 3 browsers I'm testing (IE, FF & Chrome) the pushpins are sometimes not showing in FF & Chrome, particularly if I refresh with Cntrl+F5
This is my first JSON and Bing Maps app so expect there's a few mistakes.
Any suggestions on how to improve the code and get display consistency?
#{
Layout = "~/_MapLayout.cshtml";
}
<script type="text/javascript" src="~/Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<link rel="StyleSheet" href="infoboxStyles.css" type="text/css">
<script type="text/javascript">
var map = null;
var pinLayer, pinInfobox;
var mouseover;
var pushpinFrameHTML = '<div class="infobox"><a class="infobox_close" href="javascript:closeInfobox()"><img src="/Images/close2.jpg" /></a><div class="infobox_content">{content}</div></div><div class="infobox_pointer"><img src="images/pointer_shadow.png"></div>';
var pinLayer = new Microsoft.Maps.EntityCollection();
var infoboxLayer = new Microsoft.Maps.EntityCollection();
function getMap() {
map = new Microsoft.Maps.Map(document.getElementById('map'), {
credentials: "my-key",
zoom: 4,
center: new Microsoft.Maps.Location(-25, 135),
mapTypeId: Microsoft.Maps.MapTypeId.road
});
pinInfobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), { visible: false });
AddData();
}
$(function AddData() {
$.getJSON('/ListSchools', function (data) {
var schools = data;
$.each(schools, function (index, school) {
for (var i = 0; i < schools.length; i++) {
var pinLocation = new Microsoft.Maps.Location(school.SchoolLat, school.SchoolLon);
var NewPin = new Microsoft.Maps.Pushpin(pinLocation);
NewPin.title = school.SchoolName;
NewPin.description = "-- Learn More --";
pinLayer.push(NewPin); //add pushpin to pinLayer
Microsoft.Maps.Events.addHandler(NewPin, 'mouseover', displayInfobox);
}
});
infoboxLayer.push(pinInfobox);
map.entities.push(pinLayer);
map.entities.push(infoboxLayer);
});
})
function displayInfobox(e) {
if (e.targetType == "pushpin") {
var pin = e.target;
var html = "<span class='infobox_title'>" + pin.title + "</span><br/>" + pin.description;
pinInfobox.setOptions({
visible: true,
offset: new Microsoft.Maps.Point(-33, 20),
htmlContent: pushpinFrameHTML.replace('{content}', html)
});
//set location of infobox
pinInfobox.setLocation(pin.getLocation());
}
}
function closeInfobox() {
pinInfobox.setOptions({ visible: false });
}
function getCurrentLocation() {
var geoLocationProvider = new Microsoft.Maps.GeoLocationProvider(map);
geoLocationProvider.getCurrentPosition();
}
</script>
<body onload="getMap();">
<div id="map" style="position:relative; width:800px; height:600px;"></div>
<div>
<input type="button" value="Find Nearest Schools" onclick="getCurrentLocation();" />
</div>
</body>
The JSON file is simply
#{
var db = Database.Open("StarterSite");
var sql = #"SELECT * FROM Schools WHERE SchoolLon != ' ' AND SchoolLon != 'null' ";
var data = db.Query(sql);
Json.Write(data, Response.Output);
}
Add your pinLayer, infobox, and infoboxLayer before calling the AddData function and see if that makes a difference. Also verify that school.SchoolLat and school.SchoolLon are numbers and not a string version of a number. If they are a string, then use parseFloat to turn them into a number. Other than that everything looks fine.

knockout.js api search form

The goal of my code is to search on the API search string:
So if you fill out the form you get the hits bij name.
I used the following Knockout.js script:
var viewModel=
{
query : ko.observable("wis"),
};
function EmployeesViewModel(query)
{
var self = this;
self.employees = ko.observableArray();
self.query = ko.observable(query);
self.baseUri = BASE + "/api/v1/search?resource=employees&field=achternaam&q=";
self.apiurl = ko.computed(function() {
return self.baseUri + self.query();
}, self);
//$.getJSON(baseUri, self.employees);
//$.getJSON(self.baseUri, self.employees);
$.getJSON(self.apiurl(), self.employees);
};
$(document).ready(function () {
ko.applyBindings(new EmployeesViewModel(viewModel.query()));
});
The html binding is:
<input type="text" class="search-query" placeholder="Search" id="global-search" data-bind="value: query, valueUpdate: 'keyup'"/>
But if i fill the text box i onley get the default "wis" employees? What am I doing wrong?
Not entirely sure what is wrong here, but have you debugged it and seen what the value of query is in apiurl?
One potential issue is that you are passing employees to getJSON as the observable, not the underlying array, so you could try:
$.getJSON(self.apiurl(), self.employees());
After some digging I found a solution.
var employeesModel = function(){
var self = this;
self.u = base +'/api/v1/search';
self.resource = 'employees';
self.field = 'achternaam';
self.employees = ko.observableArray([]);
self.q = ko.observable();
//Load Json when model is setup
self.dummyCompute = ko.computed(function() {
$.getJSON(self.u,{'resource': self.resource, 'field': self.field, 'q':self.q }, function(data) {
self.employees(data);
});
}, self);
};
ko.applyBindings(new employeesModel());