I'm learning Polymer;
I can't get a conditional class name to appear in my tabs (parent) component. A 'active' class should be added to a <li> element depending on the 'selected' property of a child component.
I'm not really sure my way of communicating between parent and child component is right in the first place. It is working, but it doesn't feel right..
My index.html file
<link rel="import" href="components/tabs.html">
<link rel="import" href="components/tab.html">
<ikb-tabs>
<ikb-tab heading="Tab #1">
<p>Content of the first tab</p>
</ikb-tab>
<ikb-tab heading="Tab #2" selected>
<p>Content of the second tab</p>
</ikb-tab>
</ikb-tabs>
My components/tabs.html file
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="ikb-tabs">
<template>
<style>
.active button {
color: red;
}
</style>
<nav>
<ul>
<template is="dom-repeat" items="{{tabs}}">
<li>
<button on-tap="openTab">{{item.heading}}</button>
</li>
</template>
</ul>
</nav>
<content></content>
</template>
<script>
Polymer({
is: 'ikb-tabs',
properties: {
activeTab: Number
},
ready: function () {
this.tabs = Polymer.dom(this).children;
},
openTab: function (e) {
Polymer.dom(this).children.forEach(function (tab, index) {
tab.selected = index === e.model.index;
});
}
});
</script>
</dom-module>
My components/tab.html file
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="ikb-tab" attributes="heading">
<template>
<template is="dom-if" if="{{selected}}">
<div>
<content></content>
</div>
</template>
</template>
<script>
Polymer({
is: 'ikb-tab',
properties: {
heading: String,
selected: {
type: Boolean
}
}
});
</script>
</dom-module>
I've figured it out myself, there were two main issues with my code:
Updating an Array didn't trigger an update:
Only adding, removing are replacing items in an Array triggers an update. I changed a property of an Object inside an Array. Polymer only checks the reference to that Object, that reference remained unchanged. So no change got triggered. Solution: update the Array with the set function (see code below).
Computed classes need a special syntax:
My class="{{getClassName(item.selected}}" wasn't adding any classes. I now know the correct syntax is class$="{{getClassName(item.selected)}}".
More info: https://www.polymer-project.org/1.0/docs/devguide/data-binding#native-binding
The simplified working code:
<dom-module id="ikb-tabs">
<template>
<nav>
<ul>
<template is="dom-repeat" items="{{tabs}}">
<li class$="{{getClassName(item.selected)}}">
<button on-tap="openTab">{{item.heading}}</button>
</li>
</template>
</ul>
</nav>
<div>
<content></content>
</div>
</template>
<script>
Polymer({
is: 'ikb-tabs',
properties: {
tabs: {
type: Array
}
},
ready: function () {
this.tabs = Polymer.dom(this).children;
},
openTab: function (e) {
var self = this;
this.tabs.forEach(function (tab, index) {
self.set('tabs.' + index + '.selected', index === e.model.index)
});
},
getClassName: function (isSelected) {
return isSelected ? 'active' : null;
}
});
</script>
</dom-module>
Related
I find myself creating the plugin named "owl.client.js" and enter this part to add a component consuming owl-carousel.
import carousel from 'v-owl-carousel'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('carousel', carousel)
})
However when I call this component within a page example:"App.vue" and enter this script it renders me as if the carousel component does not exist, despite checking in vue-devtools. For reference I say that in the same way I called it in an application in Nuxt2 where everything worked.
<template>
<div>
Hi
<section class="expertos-section">
<client-only>
<carousel
:rewind="false"
:items="3"
:margin="20"
:nav="false"
:dots="false"
:responsive="{
0: { items: 1, nav: false,stagePadding:50 },
768: { items: 2, nav: false },
992: { items: 3, nav: false },
}"
>
<div v-for="item in 6" :key="item">
<div class="post__inside">
<figure class="post__imagen">
<img src="https://www.10wallpaper.com/wallpaper/medium/1205/twilight_blue_moon_mountains-HD_Space_Wallpapers_medium.jpg" alt=" " />
</figure>
</div>
<div class="post__info">
<strong>Testeado</strong>
<h3 class="event-title">Description de target</h3>
</div>
</div>
</carousel>
</client-only>
</section>
</div>
</template>
<script>
const carousel = () =>{ typeof window !== "undefined" ? import("v-owl-carousel") : null}
export default {
components:{
carousel
}
}
</script>
I'm making a list of items with v-for loop. I have some API data from server.
items: [
{
foo: 'something',
number: 1
},
{
foo: 'anything',
number: 2
}
]
and my template is:
<div v-for(item,index) in items #click=toggleActive>
{{ item.foo }}
{{ item.number }}
</div>
JS:
methods: {
toggleActive() {
//
}
}
How can i toggle active class with :class={active : something} ?
P.S I don't have boolean value in items
You can try to implement something like:
<div
v-for="(item, index) in items"
v-bind:key="item.id" // or alternativelly use `index`.
v-bind:class={'active': activeItem[item.id]}
#click="toggleActive(item)"
>
JS:
data: () => ({
activeItem: {},
}),
methods: {
toggleActive(item) {
if (this.activeItem[item.id]) {
this.removeActiveItem(item);
return;
}
this.addActiveItem(item);
},
addActiveItem(item) {
this.activeItem = Object.assign({},
this.activeItem,
[item.id]: item,
);
},
removeActiveItem(item) {
delete this.activeItem[item.id];
this.activeItem = Object.assign({}, this.activeItem);
},
}
I had the same issue and while it isn't easy to find a whole lot of useful information it is relatively simple to implement. I have a list of stores that map to a sort of tag cloud of clickable buttons. When one of them is clicked the "added" class is added to the link. The markup:
<div class="col-sm-10">
{{ store.name }}
</div>
And the associated script (TypeScript in this case). toggleAdd adds or removes the store id from selectedStoreIds and the class is updated automatically:
new Vue({
el: "#productPage",
data: {
stores: [] as StoreModel[],
selectedStoreIds: [] as string[],
},
methods: {
toggleAdd(store: StoreModel) {
let idx = this.selectedStoreIds.indexOf(store.id);
if (idx !== -1) {
this.selectedStoreIds.splice(idx, 1);
} else {
this.selectedStoreIds.push(store.id);
}
},
async mounted () {
this.stores = await this.getStores(); // ajax request to retrieve stores from server
}
});
Marlon Barcarol's answer helped a lot to resolve this for me.
It can be done in 2 steps.
1) Create v-for loop in parent component, like
<myComponent v-for="item in itemsList"/>
data() {
return {
itemsList: ['itemOne', 'itemTwo', 'itemThree']
}
}
2) Create child myComponent itself with all necessary logic
<div :class="someClass" #click="toggleClass"></div>
data(){
return {
someClass: "classOne"
}
},
methods: {
toggleClass() {
this.someClass = "classTwo";
}
}
This way all elements in v-for loop will have separate logic, not concerning sibling elements
I was working on a project and I had the same requirement, here is the code:
You can ignore CSS and pick the vue logic :)
new Vue({
el: '#app',
data: {
items: [{ title: 'Finance', isActive: false }, { title: 'Advertisement', isActive: false }, { title: 'Marketing', isActive: false }],
},
})
body{background:#161616}.p-wrap{color:#bdbdbd;width:320px;background:#161616;min-height:500px;border:1px solid #ccc;padding:15px}.angle-down svg{width:20px;height:20px}.p-card.is-open .angle-down svg{transform:rotate(180deg)}.c-card,.p-card{background:#2f2f2f;padding:10px;border-bottom:1px solid #666}.c-card{height:90px}.c-card:first-child,.p-card:first-child{border-radius:8px 8px 0 0}.c-card:first-child{margin-top:10px}.c-card:last-child,.p-card:last-child{border-radius:0 0 8px 8px;border-bottom:none}.p-title .avatar{background-color:#8d6e92;width:40px;height:40px;border-radius:50%}.p-card.is-open .p-title .avatar{width:20px;height:20px}.p-card.is-open{padding:20px 0;background-color:transparent}.p-card.is-open:first-child{padding:10px 0 20px}.p-card.is-open:last-child{padding:20px 0 0}.p-body{display:none}.p-card.is-open .p-body{display:block}.sec-title{font-size:12px;margin-bottom:10px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div id="app" class="p-5">
<div class="p-wrap mx-auto">
<div class="sec-title">NEED TO ADD SECTION TITLE HERE</div>
<div>
<div v-for="(item, index) in items" v-bind:key="index" class="p-card" v-bind:class="{'is-open': item.isActive}"
v-on:click="item.isActive = !item.isActive">
<div class="row p-title align-items-center">
<div class="col-auto">
<div class="avatar"></div>
</div>
<div class="col pl-0">
<div class="title">{{item.title}}</div>
</div>
<div class="col-auto">
<div class="angle-down">
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="angle-down" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"
class="svg-inline--fa fa-angle-down fa-w-10 fa-3x">
<path fill="currentColor"
d="M151.5 347.8L3.5 201c-4.7-4.7-4.7-12.3 0-17l19.8-19.8c4.7-4.7 12.3-4.7 17 0L160 282.7l119.7-118.5c4.7-4.7 12.3-4.7 17 0l19.8 19.8c4.7 4.7 4.7 12.3 0 17l-148 146.8c-4.7 4.7-12.3 4.7-17 0z"
class=""></path>
</svg>
</div>
</div>
</div>
<div class="p-body">
<div class="c-card"></div>
<div class="c-card"></div>
<div class="c-card"></div>
</div>
</div>
</div>
</div>
</div>
I'm a newbie in Vuejs (i'm learning).
I'm trying to do many modals in same page using Vuejs2
Like many people, i have this console warning :
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated:
I read the official doc. I understand that i'm trying to mutating a props and now with vuejs2 its no longer possible.
I tried by using data and computed methods but still doesn't work.
I know the props are now in one way and i think that i understood i need to $emit some props
Here my code:
app.js
Vue.component('modal', Modal);
Vue.component('modal-add-equipe', ModalContent);
Vue.component('modal-user-team', ModalContentTeamUser);
new Vue({
delimiters: ['${', '}'],
el: '#app',
data: {
showModal: false,
showModalAddEquipe: false,
showModalUserTeam: false
},
methods: {
openModal: function(name) {
console.log(this.$refs[name]);
this.$refs[name].show = true;
//this.showModal = true;
},
closeModal: function(name) {
this.$refs[name].show = false;
//this.showModal = false;
}
}
})
Modal.vue
<template>
<transition name="modal">
<div class="modal-mask" #click="close" v-show="show">
<div class="modal-wrapper">
<div class="modal-container with-border" #click.stop>
<slot></slot>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
props: ['show', 'onClose'],
methods: {
close: function () {
this.onClose();
}
},
ready: function () {
document.addEventListener("keydown", (e) => {
if (this.show && e.keyCode == 27) {
this.onClose();
}
});
}
}
</script>
ModalContent.vue
<template>
<modal :show.sync="show" :on-close="close">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
</slot>
</div>
</modal>
</template>
<script>
var Modal = require('./Modal.vue');
export default {
props: ['show'],
methods: {
close: function () {
this.$emit('close', false);
},
},
components:{
'modal': Modal
}
}
</script>
the call in twig :
{% for user in usersTeamed %}
<span #click="openModal('userTeam{{ user.getId() }}')"><strong>Equipes</strong></span>
<modal-user-team ref="userTeam{{ user['id'] }}" :show.sync="showModal" #close="closeModal('userTeam{{ user['id'] }}')">
<h3 slot="header">{{ 'equipe_modal_user_equipe'|trans }}</h3>
<div slot="body">
{{ form_start(formUsersTeamed[user['id']]) }}
{{ form_rest(formUsersTeamed[user['id']]) }}
{{ form_end(formUsersTeamed[user['id']]) }}
</div>
</modal-user-team>
{% endfor %}
My goal it's to open one modal (but many in same page and avoid 1k variables like do the example https://v2.vuejs.org/v2/examples/modal.html ) find by "rel"
This code is working but i have console worning !
If someone can help me please :)
PS: I use also gulp with vueify, babelify and aliasify
PS2 : Sorry for my english
I have images that are loaded from database. When I click on an image, I want to show that image in a Modal Pop-up. My problem is that, I am not able to call the partial view from jquery. In fact, that action is not getting called from JQuery. Please help... I am a fresher. Below is my code:
_Layout.cshtml
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>_Layout</title>
#Styles.Render("~/bundle/ProfileStyle")
#Scripts.Render("~/bundle/JQuery")
#Scripts.Render("~/bundle/JQueryUI")
#Scripts.Render("~/bundle/CustomJS")
</head>
<body>
<div>
#RenderBody()
</div>
<div id="dialog">
#Html.Partial("_ProfileDetail")
</div>
</body>
</html>
Index.cshtml
#model IEnumerable<Profile.Models.TestProfile>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<div class="tableOuterBlock">
#foreach (var item in Model)
{
<div class="tableInnerBlock">
<span>
#*<a href="#Url.Action("Edit", "Profile", new {#item.upi_Id})">*#
<img id="imgOpenDialog" src="#Url.Content(#item.upi_ImgData)" alt="No Image" width="100" height="100" />
#*</a>*#
</span>
</div>
}
</div>
Partial view
#model Profile.Models.TestProfile
<div>
#if(Model != null)
{
<img id="imgOpenDialog" src="#Url.Content(#Model.upi_ImgData)" alt="No Image" width="80%" height="50%" />
}
</div>
JQuery
$(function () {
$("[id*=imgOpenDialog]").click(function () {
var imgDetail = $(this).prop("src");
$("#dialog").dialog({
autoOpen: true,
position: { my: "center" },
modal: true,
resizable: false,
open: function () {
//parameter to c# function
data: { strImg = imgDetail }
$(this).load('#Url.Action("ShowProfileDetail","Profile")');
}
});
});
});
Controller
public PartialViewResult ShowProfileDetail(string strImg)
{
strImg = strImg.Substring(strImg.IndexOf('/'));
List<TestProfile> tpList = db.TestProfiles.Where(x => x.upi_ImgData ==strImg).ToList();
TestProfile testProfile = db.TestProfiles.Find(tpList[0].upi_Id);
return PartialView("_ProfileDetail", testProfile);
}
it may be a bit late but if you check out this blog, and download his code, you will see his use of jquery to create a modal pop up from a partial view.
I am using Kendo Grid UI. The following is an example of the same.
<!DOCTYPE html>
<html>
<head>
<title></title>
<link href="http://cdn.kendostatic.com/2013.3.1324/styles/kendo.common.min.css" rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.3.1324/styles/kendo.rtl.min.css" rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.3.1324/styles/kendo.silver.min.css" rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.3.1324/styles/kendo.dataviz.min.css" rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.3.1324/styles/kendo.dataviz.silver.min.css" rel="stylesheet" />
<link href="/kendo-ui/content/shared/styles/examples.css" rel="stylesheet" />
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2012.1.515/js/kendo.all.min.js"></script>
</head>
<body>
<div id="main">
<h1 id="exampleTitle">
<span class="exampleIcon gridIcon"></span>
<strong>Grid /</strong> Column resizing </h1>
<div id="theme-list-container"></div>
<div id="exampleWrap">
<script>preventFOUC()</script>
<div id="example" class="k-content">
<div id="grid"></div>
<script>
$(document).ready(function() {
gridDataSource = new kendo.data.DataSource({
transport: {
read: "http://demos.telerik.com/kendo-ui/service/Northwind.svc/Orders"
},
});
$("#grid").kendoGrid({
dataSource: gridDataSource,
scrollable: true,
resizable: true,
columns: [
{
field: "OrderID",
title: "ID"
}, {
field: "OrderDate",
title: "Order Date"
},
{
field: "ShipCountry",
title: "Ship Country"
},
{
field: "ShipCity",
title: "Ship City"
},
{
field: "ShipName",
title: "Ship Name"
},
{
field: "ShippedDate",
title: "Shipped Date"
}
]
});
});
</script>
</div>
</div>
</div>
I want a customized reorder on columns. I have disabled drag and drop on OrderID. But columns other than OrderID can be reordered and these columns can be placed before OrderID column.
Is there a way where I can disable dropping of columns before OrderID?
You should do it in two steps:
Disable dropping into first column.
Disable dragging first column.
For the first part after creating the grid you can do:
$("th:nth(0)", "#grid").data("kendoDropTarget").destroy();
This gets from a grid which identifier is grid and the first head cell th:nth(0) the KendoUI DropTarget and destroys it (no longer a valid drop target).
For the second part, you should define a dragstart event that checks that you are dragging the first column and if so, you do not allow to drag it.
$("#grid").data("kendoDraggable").bind("dragstart", function(e) {
if (e.currentTarget.text() === "ID") {
e.preventDefault();
}
});
NOTE: Here I detected the first column asking for its text (ID) but you might change it to check for its position in the list of th in the grid and if so invoke preventDefault.
Check it running here: http://jsfiddle.net/OnaBai/jzZ4u/1/
check this for more elegant implementation:
kendo.ui.Grid.fn._reorderable = function (reorderable) {
return function () {
reorderable.call(this);
var dropTargets = $(this.element).find('th.disable-reorder');
dropTargets.each(function (idx, item) {
$(item).data("kendoDropTarget").destroy();
});
var draggable = $(this.element).data("kendoDraggable");
if (draggable) {
draggable.bind("dragstart", function (e) {
if ($(e.currentTarget).hasClass("disable-reorder"))
e.preventDefault();
});
}
}
}(kendo.ui.Grid.fn._reorderable);
where .disable-reorder class is for disabling column