I'm loading a radio-button-group in polymer with a set of 4 radio buttons as answer options to a question. I have 4 questions. Now if I select an option in the first question and select next(go to the next question), the second question has its option already selected(same as the first question). Why is this happening?
<paper-radio-group>
<template is="dom-repeat" items=[[_calculateQuestionOptions(index)]]>
<paper-radio-button name="{{_calculateId(index)}}">
[[item.questionOption]]
</paper-radio-button><br>
</template>
</paper-radio-group>
CODE for next and prev operations
next() {
if (this.index < this.repos.length-1) {
this.index +=1;
}
}
prev() {
if (this.index>0) {
this.index -= 1;
}
}
To solve this issue. I wanted to add all the answers to an array and clear the selection as soon as the next button is click. Somthing like this.
next() {
var questionId = this.repos[this.index].questionId;
if (Polymer.dom(this.root).querySelector("paper-radio-group")) {
this.answers[questionId] = Polymer.dom(this.root).querySelector("paper-radio-group").selected;
Polymer.dom(this.root).querySelector("paper-radio-group").selected = null;
}
if (this.index < this.repos.length-1) {
this.index +=1;
}
questionId = this.repos[this.index].questionId;
if (this.answers[questionId] !== null) {
Polymer.dom(this.root).querySelector("paper-radio-group").selected = this.answers[questionId];
}
}
Is this the only way or is there a better way to do this.
Why is this happening?
It happens because dom-repeat reuse old elements when it update (for better performance, re-create DOM is slow). Its means only bound data in that scope will be updated.
Is this the only way or is there a better way to do this.
I think your way is already fine but you still can reduce some code by using advantage of Polymer like:
Static node map here
Complex observers here
My example see here
Thanks.
Related
On click, I want to push to array. It works if I manually refresh the Vue extension in the console of Google Chrome but otherwise not
completeStep(index, key) {
let currentSet = index + key
if (this.exercise_completed[index].indexOf(currentSet) === -1) {
this.exercise_completed[index].push(currentSet)
} else {
this.exercise_completed[index].splice(this.exercise_completed[index].indexOf(currentSet), 1)
}
}
maybe try to use this.$forceUpdate(); when you make the change.
https://v2.vuejs.org/v2/api/#vm-forceUpdate
Hope this helps
I would recommend making a key and changing the key on changes in the array. See here:
https://michaelnthiessen.com/force-re-render
I am working on a polymer2 shadow dom template project need to select children elements from parent elements. I found this article introduces a way to select child shadow dom elements that like this:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
However, when I tried in my polymer2 project, like this:
//First: works!!
document.querySelector('container')
.shadowRoot.querySelector('app-grid')
.shadowRoot.querySelector('#apps');
//Second: Doesn't work!// got null
document.querySelector('container::shadow app-grid::shadow #apps')
// Thrird: document.querySelector('* /deep/ #apps') // Doesn't work, got null
I really need the second way or the third, which to put selectors in (), but both couldn't work. Does anyone know why the second one doesn't work? Thank you so much!
::shadow and /deep/ has never(?) worked in Firefox, and is depraved in Chrome 63 and later.
Source
Eric Biedelman has written a nice querySelector method for finding all custom elements on a page using shadow DOM. I wouldn't use it myself, but I have implemented it so I can "querySelect" custom elements in the console. Here is his modified code:
// EXAMPLES
// findCustomElement('app-grid') // Returns app-grid element
// findCustomElements('dom-if') // Returns an array of dom-if elements (if there are several ones)
// findCustomElement('app-grid').props // Returns properties of the app-grid element
function findCustomElement(customElementName) {
const allCustomElements = [];
customElementName = (customElementName) ? customElementName.toLowerCase() : customElementName;
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
el.props = el.__data__ || el.__data || "Doesn't have any properties";
if (customElementName && customElementName === el.tagName.toLowerCase()) {
allCustomElements.push(el);
} else if (!customElementName) {
allCustomElements.push(el);
}
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
if (allCustomElements.length < 2) {
return allCustomElements[0] || customElementName + " not found";
} else if (customElementName) {
allCustomElements.props = "Several elements found of type " + customElementName;
}
return allCustomElements;
}
Remove the if (isCustomElement(el)) { statement, and you can querySelect whatever element and get an array of it if several of them exists. You can change findAllCustomElements to implement a smarter querySelect using the recursive loop on shadowDoom as base. Again, I wouldn't use this myself – and instead pass on variables from parent element(s) to children where the children have observers that activates specific behaviors – but I wanted to give you a general implementation of a fallback if nothing else works.
The problem with your question is that you don't give any specifics about WHY you want to select the children in the first place.
I have complicated object for a table. Looks like this:
{
1510002000: {
date: "07.11.17"
hours: {
1510002000:{
activity:{
activity: "Тест",
color: "#00ff00",
end_at: 1510005600,
start_at: 1510002000,
type_id: 1
}
},
1510005600: {
...
},
...
}
},
....
}
This is a code from template that uses this object:
<tr v-for="date in this.tds">
<td>{{ date.date }}</td>
<td is="hour-td"
v-for="hour in date.hours"
:color="hour.activity.color"
:start_at="hour.activity.start_at"
:end_at="hour.activity.end_at"
:activity="hour.activity.activity"
:type_id="hour.activity.type_id"
>
</td>
</tr>
I evaluated it as a computed property, but I need to rerender table when parent component provides data assync, so I have a watcher for prop (prop called "activities"):
watch: {
activities: function(){
var vm = this;
let dth = new DateTimeHelper;
if (this.activities.length > 0){
this.activities.forEach(function(activity){
let dateTimestamp = dth.getDateTimestampFromTimestamp(activity.start_at); // just getting the key
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
});
}
console.log(vm.tds) // here I can see that the object is filled with new data
}
},
The problem is that the table doesn't rerender. More precisely, the component "hour-td" does not contain the new data.
Also I've tried to use Vue.set, but no success with that
Can you help me with the updating table? I've spent like 5 hours for refactoring and attempts.
Thanks in advance
SOLUTION
In my case there can be two states: there are activities, there are no activities. So I made two computed props for each case and render them separately and switch by v-if="activities.length"
I think that your problem is with Vue known issue for Change Detection Caveats (you can read here) with array direct assignation, that don't detect changes.
You should change this part of code (with direct array assignation):
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
With the Vue.set() option for arrays in order to detect the change and re-renders the component. It worked for me in differents occassions:
// Vue.set(object, key, value)
// something like this:
Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity.activity,1,activity.activity);
More info here: https://codingexplained.com/coding/front-end/vue-js/array-change-detection
Edit:
I see now that you said:
Also I've tried to use Vue.set, but no success with that
What you mean with: "no success" ? Can you share the code? I have the same issue and I resolved with Vue.set..
You can also take a look to vm.$forceUpdate(), try to execute after the last console.log or grab all the code inside a vm.$nextTick( [callback] ) in order to execute all the actions (load data in the table) and then, re-render the component on next tick.
More info here: https://v2.vuejs.org/v2/api/#vm-forceUpdate && https://v2.vuejs.org/v2/api/#Vue-nextTick
Edit 2:
I think that your problem is with the index of the array, you should take a look here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection .
Try changing the:
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
and simplify with:
if (vm.tds[dateTimestamp] && vm.tds[dateTimestamp].hours[activity.start_at]){
Vue.set( vm.tds, vm.tds.indexOf(vm.tds[dateTimestamp].hours[activity.start_at]), activity);
}
Hope it helps!
I need some tree structure in Aurelia. I got some link for that. It's working fine. But My requirement is like accordion with tree view. Means when I clicked on closed parent all opened parents should close and clicked one should open same as bootstrap accordion. same thing should happen When ever I clicked on child parent element with in parent repeat.
Below is my image for tree structure.
Gist run Link: Gist
The above gist is just tree structure with open and collapse. From that when I clicked on closed tree node , that should open and remaining tree nodes should be closed.
In the above gist "node-model.js" is having events for open and close. So when ever I clicked on icon the clicked event inside this variable will get only clicked node. How can I get other node in that method to hide.
Answer:
Inside your tree-view.js, add the following code (3 methods):
attached() {
window.addEventListener('goCollapseAll', (e) => {
this.closeOtherBranches(e.detail);
}, false);
}
closeOtherBranches(exceptNode) {
// traverse node tree to find current one
var found = null;
for(var i = 0; i < this.nodes.length; i++){
if (this.subSearch(this.nodes[i], exceptNode)) {
found = i;
}
}
if (found !== null) {
for(var i = 0; i < this.nodes.length; i++){
if ((i != found) && (this.nodes[i].expanded)) {
this.nodes[i].toggleNode();
}
}
}
}
subSearch(node, findNode) {
// recursive search of tree for findNode
var match = null;
if (node === findNode) {
match = node;
} else {
for(var i = 0; i < node.children.length; i++){
if (node.children[i] === findNode) {
match = node;
} else {
match = this.subSearch(node.children[i], findNode);
}
}
}
return match;
}
Then, inside your node-model.js, add the following lines at the beginning of toggleNode():
// close other node branches
if (!this.expanded) {
var event = new CustomEvent('goCollapseAll', { 'detail': this });
window.dispatchEvent(event);
}
Explanation:
When a node is expanded, it publishes a custom event to trigger the recursive search to close all nodes that are part of a different branch. It's not the prettiest solution and I think there might be a cleaner way if you adopt a different structure for the tree, but this solution definitely works well and accomplishes your purpose.
GistRun:
I've updated your GistRun to demonstrate the functionality. You can see it working here:
https://gist.run/?id=828c3c79bff0dfbaffec3252ed376c8c
I have a SharePointWebControls:UserField in a page layout that needs to be excluded from spell checking, as otherwise whenever a user is selected there are a large number of spelling errors are detected in the code-behind for the control.
It seems that in Sharepoint 2007 this behaviour could be implemented by using excludefromspellcheck = "true" but this doesn't seem to work for Sharepoint 2010. Has anyone come across the same problem and found a way around it?
Based on SpellCheckEntirePage.js, that appears to still be the way:
var elements=document.body.getElementsByTagName("*");
for (index=0; index < elements.length;++index)
{
if (null !=elements[index].getAttribute("excludeFromSpellCheck"))
{
continue;
}
// snipped - if (elements[index].tagName=="INPUT")
// snipped - else if (elements[index].tagName=="TEXTAREA")
}
But excludeFromSpellCheck is not a property of UserField, so it probably won't automatically copy down to the rendered HTML. When rendered, the UserField control is made up of several elements. I would try looking at the View Source to see if excludeFromSpellCheck is making it into the final HTML. But to set the attribute on the appropriate elements, you might need to use some jQuery like this:
$("(input|textarea)[id*='UserField']").attr("excludeFromSpellCheck", "true");
You can disable the spell check for certain fields by setting the "excludeContentFromSpellCheck" attribute to "true" on text area and input controls that you dont want to be spell checked.
I did this on all my page layouts. Now i dont get false positives anymore.
The solution is to add a div tag around the fields you don't want spell checked and adding a javascript that sets "excludeFromSpellCheck" to "true" for the elements within the div tag.
The solution i found is described here: Inaccurate Spell Check on SharePoint Publishing Pages
Joe Furner posted this solution, which has worked for me.
https://www.altamiracorp.com/blog/employee-posts/spell-checking-your-custom-lay
It excludes all PeoplePickers on the page:
function disableSpellCheckOnPeoplePickers() {
var elements = document.body.getElementsByTagName("*");
for (index = 0; index < elements.length; index++) {
if (elements[index].tagName == "INPUT" && elements[index].parentNode && elements[index].parentNode.tagName == "SPAN") {
var elem = elements[index];
if (elem.parentNode.getAttribute("NoMatchesText") != "") {
disableSpellCheckOnPeoplePickersAllChildren(elem.parentNode);
}
}
}
}
function disableSpellCheckOnPeoplePickersAllChildren(elem) {
try {
elem.setAttribute("excludeFromSpellCheck", "true");
for (var i = 0; i < elem.childNodes.length; i++) {
disableSpellCheckOnPeoplePickersAllChildren(elem.childNodes[i]);
}
}
catch(e) {
}
}
This code is working partially only,because if you put the people picker value again checking the people picker garbage value for one time.