strange behavior of polymer data biding - data-binding

Assume that I have three tabs together,content in each tab are fetched via core-ajax
<core-ajax auto url="http://example.com/tab.json" handleAs="json"
response="{{response}}"></core-ajax>
<paper-tabs id="tabs" selected="{{response[0].name}}" flex >
<template repeat="{{mytab as t}}">
<paper-tab name="{{t.name}}">{{t.name}}</paper-tab>
</template>
</paper-tabs>
Polymer({
responseChanged:function(response){
this.mytab= this.response;
console.log(this.mytab[0].name);
}
});
codes above display normal,but what is confusing me is that,when i click the other two tabs other than the first one,the first one tab name(i.e. content) changes accordingly to the name of which tab was clicked.i can't get that ,can anyone explain to me ?
Thanks
Peng

I find a solution to solve this problem,but i don't know if this is the right way or the best:
<core-ajax auto url="http://example.com/tab.json" handleAs="json"
response="{{response}}"></core-ajax>
(changed)
<paper-tabs id="tabs" selected="{{defaulttab}}" flex >
<template repeat="{{mytab as t}}">
<paper-tab name="{{t.name}}">{{t.name}}</paper-tab>
</template>
</paper-tabs>
Polymer({
responseChanged:function(response){
this.mytab= this.response;
this.defaulttab = this.response[0].name;(added)
console.log(this.mytab[0].name);
}
});
This works as expected.

Related

Vue - requests and watch data on unmounted components

I'm new to vue and strugling with some props and attributes.
I have a vue application where the main app is calling three different components:
Navbar,
Sidebar
MapContainer
In Navbar user will fill a selector with information about city and states. After user presses search, the list of results will then show up in the Sidebar menu.
Sidebar itself is a simple component carrying only a router-view for it's children components which are Results and Details
Results will receive the result of the search performed in the Navbar component. When user clicks any item in the Results component, Sidebar will then load Details taking the place of Results with detailed information about that place.
the problem is that the data used to make the request(city and states) comes from the first component navbar. I'm passing this data to sub-components using vue-router params option. When component Results gets unmounted, I lost all the data that was passed, even adding a Watch couldn'd fix the problem and thus can't return back to the previous page. Even adding a Watch couldn'd fix the problem. What's the proper way to handle data across components that area unmounted?
Navbar.vue
<template>
<div class="navbar">
<div class="logo">
Logo
</div>
<div class="middle">
<div class="flex-selectors">
<div class="navbar-options">
<select v-model="state_id" class="main-selectors" #click="load_cities">
<option v-for="state in states" :value="state.state_id" :key="state">
{{ state.state }}
</option>
</select>
<select v-model="city_id" class="main-selectors">
<option v-for="city in cities" :value="city.city_id" :key="city">
{{ city.city }}
</option>
</select>
</div>
<div class="search">
<button #click="seach">
<router-link :to="{name: 'results', params: {state: state_id, city: city_id} }" aria-current="page" title="Resultados">
<div>Search</div>
</router-link>
</button>
</div>
</div>
</div>
</template>
Sidebar.vue
<template>
<div class="sidebar">
<router-view></router-view>
</div>
</template>
Results.vue
<template>
<div v-if="areas.length">
<router-link :to="{name: 'details' }" tag="div" class="container" #click="load(area)" v-for="area in areas" :key="area" :value="area">
<!-- BUNCH OF DIVS AND V-FOR -->
</router-link>
</div>
<div v-else>
NA
</div>
</template>
<script>
export default {
data() {
return {
state_id: this.$route.params.state,
city_id: this.$route.params.city,
areas: [],
}
},
methods: {
search(state_id, city_id) {
load_areas.get(state_id, city_id).then(
result => {
this.areas = result.data
}
)
}
},
mounted() {
this.emitter = inject('emitter')
this.searchAreas(this.state_id, this.city_id)
},
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
this.searchAreas(toParams.state, toParams.city)
}
)
},
watch: {}
}
</script>
What's the proper way to handle data across components that are
unmounted?
You cannot access data from an unmounted component.
When multiple components need to access or modify the same data, a good option to look into is state management. Pinia is the new official library recommendation for state management.
Instead of passing data through vue-router params, create a store for it. Set results of your search query from Navbar component and access it in Results component. When Results component gets unmounted, you won't lose the data.

dropzone-meteor howto fire events

i'm new to Meteor and and i want to learn something about it. therefore i want to build a page were i can upload images via meteor-dropzone.
the upload is working with meteor-uploads.
now i want to get events, like 'addedfile' or 'drop' from the dropzone to fire some new functions.
HTML Page Profile2:
<template name="profile2">
<div class="ibox-content">
{{> dropzone url='http://localhost:3000/uploads' maxFilesize=5 addRemoveLinks=true acceptedFiles='image/*,jpg,jpeg,png' id='dropzoneDiv'}}
</div>
</template name="profile2">
In The JS File for Profile2 i wrote this:
Template.dropzone.events({
'addedfile #dropzoneDiv': function(e, template){
e.preventDefault();
console.log("Hello");
}
});
But i don't see something in the console.log output.
I'm sure i'm doing something wrong. But i have no i idea where the problem or the wrong understanding is.
Can somebody help me please.
Thanks.
Michael
after try and error. i found the solution. Maybe someone can explain it to me. because i don't understand it completely, why it's working now but so different to the normal Meteor event version.
Dropzone.options.dropzoneDiv = {
init: function() {
this.on("addedfile", function(file) { alert("Added file."); });
}
};
The Template like that:
<!-- Page heading -->
{{> pageHeading title='File upload' category='Forms' }}
<div class="wrapper wrapper-content animated fadeIn">
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Dropzone Area</h5>
{{>iboxTools}}
</div>
<div class="ibox-content">
<!-- For more info about dropzone plugin see this: https://github.com/devonbarrett/meteor-dropzone/ -->
{{> dropzone url='/uploads' id='dropzoneDiv'}}
</div>
</div>
</div>
</div>
</div>
</template>
Try the dropped event:
'dropped #dropzoneDiv' (e, template) => {
e.preventDefault();
console.log(e.originalEvent.dataTransfer.files); // this will contain the list of files that were dropped
}

VueJS component won't render on front end (possibly Drupal issue)

The site I'm currently working on is built in Drupal 7. I have one form that requires user input so I'm attempting to build it with VueJS. The form is contained all within one template file (.tpl.php) and all the content is provided in this template file or via the VueJS Javascript (nothing is coming from the CMS).
The issue I have is that the Vue components are not rendering on the front-end, but when I copy the code into a JSFiddle they do, so I'm guessing it is an issue with the interaction between VueJS and Drupal. Here is a screenshot of my markup when inspecting...
Here is the code from the .tpl.php file...
<div id="app">
<form>
<div>
<label for="year">Per Year</label>
<input type="radio" name="frequency" id="year" value="year" v-model="frequency" checked>
<label for="month">Per Month</label>
<input type="radio" name="frequency" id="month" value="month" v-model="frequency">
</div>
</form>
<ul class="plans">
<template id="plan-component">
<h2 class="plan-name">{{ name }}</h2>
<h2 class="plan-cost">{{ price }}</h2>
<h2 class="plan-tagline">{{ tagline }}</h2>
Choose this plan
</template>
<li>
<plan-component :frequency="frequency"
name="Basic"
tagline="Basic tagline"
price-yearly="Free"
price-monthly="Free"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Rec"
tagline="Rec tagline"
price-yearly="3"
price-monthly="4"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Team"
tagline="Team tagline"
price-yearly="4"
price-monthly="5"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Club"
tagline="Club tagline"
price-yearly="5"
price-monthly="6"
></plan-component>
</li>
</ul>
</div>
..and the code from my JS file...
Vue.component('plan-component', {
template: '#plan-component',
props: ['frequency', 'name', 'tagline', 'priceYearly', 'priceMonthly'],
computed: {
'price': function() {
if (this.frequency === 'year') {
return this.priceYearly;
} else {
return this.priceMonthly;
}
}
},
methods: {
makeActivePlan() {
// We dispatch an event setting this to become the active plan
this.$dispatch('set-active-plan', this);
}
}
});
new Vue({
el: '#app',
data: {
frequency: 'year',
activePlan: {name: 'no', price: 'You must select a plan!' }
},
events: {
'set-active-plan': function(plan) {
this.activePlan = plan;
}
},
});
And here is the JSFiddle which outputs the components correctly - https://jsfiddle.net/2xgrpLm6/
What browser are you using? <template> tags are not supported in IE.
Another idea is to make sure you are never using fragment components (meaning wrap everything inside your template with a div like so:
<template id="foobar">
<div>
CONTENT HERE
</div>
</template>
Lastly, have you turned on Vue debug mode? Before you instantiate your Vue instance, set Vue.config.debug = true and see if you get console errors then.
Try moving the <template id="plan-component">...</template> code outside of the Vue instance. I.e., such that it is not contained within <div id="app">...</div>.
This has solved a similar problem for me in the past, though I'm not sure if it applies here.
For anyone having a similar issue, the solution was simple. After Jeff suggested turning on Vue debug mode (and downloading the Dev version of Vue JS instead of minified - https://vuejs.org/guide/installation.html) the console gave the error [Vue warn]: Cannot find element: #app.
The issue was that Drupal was loading my scripts in the <head>, before <div id="app"> was loaded in the DOM. As such #app couldn't be found. After outputting the scripts before the closing <body> tag all was sorted. See here for more information [Vue warn]: Cannot find element

Masonry doesn't work properly with Meteor

I have created a test project with Meteor which uses Masonry. I added the package mrt:jquery-masonry(or isotope:isotope), and it works well at the beginning. However, the problem comes now.
Basically, I want to implement the feature that when user clicks the button, the page will be added one more div. Below is my code:
main.html
<body>
<div class="container">
{{> masonryContent}}
</div>
<script>
(function($){
var $container = $('.masonry-container');
$container.masonry({
columnWidth: 300,
gutterWidth: 50,
itemSelector: '.masonry-item'
})
}(jQuery));
</script>
</body>
style.css
.masonry-item {
width: 300px;
}
masonry-content.html
<template name="masonryContent">
<div class="masonry-container">
<div class="masonry-item">
<p>blabla...</p>
<p>
Button
</p>
</div>
<div class="masonry-item">
<p>test...</p>
</div>
<div class="masonry-item">
<p>another test...</p>
</div>
{{#if showItem}}
<div class="masonry-item">
<p>new added item...</p>
</div>
{{/if}}
</div>
</template>
masonry-content.js
Template.masonryContent.events({
"click #click-me": function(e) {
e.preventDefault();
Session.set('show_me', true);
}
});
Template.masonryContent.helpers({
showItem: function() {
return !!Session.get('show_me');
}
});
The problem is when I click the button, the new div was created; however, it wasn't placed by following Masonry rules. The new created item just overlapped to the first item, but I expect it performs the way to append to the last item.
I would appreciate if anyone could help me on this.
Thanks in advance!
As meteor does partial rendering the element needs to be there in the DOM for masonry to work. So there are two ways of getting over the problem
1) Hide or unhide the element when the button click happens
Or
2) re-render the DOM
You can use the chrome dev tools to see what DOM elements are touched/refreshed (Green color).
There is a typo in masonry in the template name insertion.
Check the package state, many mrt packages are not well supported anymore.

FullpageJs and Meteor : Capture event from template

I'm struggling with fullpage.js in Meteor.
When I add a button inside a template, the Template.templatename.events function does not fire on event.
For example:
Template HTML (messages.html)
<template name="messages">
<ul>
{{#each mess}}
<li>{{ messContent}}</li>
{{/each}}
<button class="addMessage">Add Message!</button>
</ul>
</template>
Template JavaScript (messages.js)
Template.messages.helpers({
mess: function(){
return Messages.find();
}
});
Template.messages.events({
'click .addMessage' : function(event){
console.log("clicked")
}
})
Main HTML
<head>
<title>fulltest</title>
</head>
<body>
{{> full}}
</body>
<template name="full">
<div id="fullpage">
<div class="section active">
<h1>First slide</h1>
{{> messages}}
</div>
<div class="section">
<h1>Second slide</h1>
</div>
</div>
</template>
My fullpage initialisation:
Template.full.rendered = function(){
$('#fullpage').fullpage();
}
If I remove the fullpage initialisation then the click event gets logged. Still new at Meteor, I didn't manage to grasp what's going wrong here.
All help much appreciated,
Joris
Use delegation or use verticalCentered:false and scrollOverflow:false.
From the fullPage.js FAQs:
My javascript/jQuery events don't work anymore when using fullPage.js
Short answer: if you are using options such as verticalCentered:true or overflowScroll:true in fullPage.js initialization, then you will have to treat your selectors as dynamic elements and use delegation. (by using things such as on from jQuery). Another option is to add your code in the afterRender callback of fullpage.js
Explanation: if you are using options such as verticalCentered:true or overflowScroll:true of fullPage.js, your content will be wrapped inside other elements changing its position in the DOM structure of the site. This way, your content would be consider as "dynamically added content" and most plugins need the content to be originally on the site to perform their tasks. Using the afterRender callback to initialize your plugins, fullPage.js makes sure to initialize them only when fullPage.js has stopped changing the DOM structure of the site.

Resources