Data Driven CSS Grid Vue Component - javascript

I want to create a Grid component that accepts the number of columns from the user, accepts data and renders all it's children into consecutive cells.
Something like this.
<Grid :cells="12" :columns="6">
<div>Child1 Cell1</div>
<div>Child2 Cell2</div>
<div>Child3 Cell3</div>
<div>Child4 Cell4</div>
<div>Child5 Cell5</div>
<div>Child6 Cell6</div>
</Grid>
In the Grid.vue component in the template, this is what I expect to do.
<div class="nugget-grid-item" v-for="cell of cells" :key="cell">
{cell}
</div>
This will render something like this on the UI.
The dashed border on each cell is due to the nugget-grid-item CSS class, but CSS is not relevant here, so let's ignore that.
What I am not able to figure out is how do I get this Grid component to display the following.
Isn't there something like this.children from React in Vue?

What you need are slots. See docs here. As you'll see slots allow a parent component to pass DOM elements into a child component. A basic look at them could go like this:
//ChildComponent.vue
<template>
<div>
<p>I'm the child component!</p>
<!-- Content from the parent gets rendered here. -->
<slot></slot>
</div>
</template>
And then you inject content into the slot tags like this:
//ParentComponent.vue
<template>
<div>
<child-component>
<p>I'm injected content from the parent!</p>
<p>I can still bind to data in the parent's scope, like this! {{myVariable}}</p>
</child-component>
</div>
</template>
Slots can get pretty complex and do a lot of things so are well worth looking into.
Further to your below comment, you can put a v-for in the grid. This outputs what you seem to be after. I've put an input in to accept the users number of columns as you said and it then renders that number of cells. You can of course use multiple slots and named slots and scoped slots but I'll leave it up to you how you expand on this.
//Grid.vue
<template>
<div class="cell">
<slot></slot>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.cell {
height: 40px;
width: 60px;
border: 1px solid gray;
}
</style>
and parent:
<template>
<div class="content">
<label>Enter number of columns</label>
<input v-model.number="col" type="number">
<Grid v-for="(n, i) in col" :key="i" >
<div>Child{{n}} Cell{{n}}</div>
</Grid>
</div>
</template>
<script>
import Grid from '#/components/admin/Grid'
export default {
layout: 'admin',
components: {
Grid
},
data: () => ({
col: 4
}),
}
</script>

Related

Vue mouseover not causing changes to the data

I am trying to attach a v-mouseover directive to a bootstrap Vue element b-list-group-item as shown below.
<b-row>
<b-col cols="3">
<b-list-group>
<b-list-group-item :active="register"
#click="switchRegister" button
#mouseover="isRegisterHover = true"
#mouseleave="isRegisterHover = false"
class="border-0 bg-transparent register"> Register </b-list-group-item>
</b-list-group>
</b-col>
<b-col cols="9">
<div id="action-screen-canvas-register v-if="isRegisterHover"> </div>
</b-col>
</b-row>
The variable isRegisterHover is tied to the boolean value in the data which determines whether or not the div will be shown.
export default {
name: 'Home',
components: {
Navi
},
data() {
return {
isRegisterHover: false,
// ...
}
},
// ...
}
Thing is that the action-canvas-register div remains hidden when I hover the item, Vue devtool also shows that the data remains unchanged when I mouseover them. How do I make the isRegisterHover value change when I mouse-over the item?
You could also do this with pure CSS if you wanted to. You could do something like:
<template>
<div id="target">Hover this</div>
<div>...</div> // This will be hidden when #target is hovered
</template>
<script>
...
</script>
<style>
#target:hover + div {
visibility: hidden; // Hides the element like v-show
or
display: none; // Hides the element like v-if
}
</style>
Your code should work. See this:
I would recommend using v-show instead of v-if. See this.
<script>
export default {
data() {
return {
isRegisterHover: false
}
}
}
</script>
<template>
<div #mouseover="isRegisterHover = true"
#mouseleave="isRegisterHover = false">Hover this</div>
<div v-show="isRegisterHover">This will show/hide (v-show)</div>
<div v-if="isRegisterHover">This will show/hide (v-if)</div>
</template>

How can i listen for an event from a sub component in vue?

I have 3 components
Stage
StageComponent
StageElementComponent
the Stage contains the default data and the basic control of all 3 components.
the StageComponent i filter the StageElementComponent elements, and have some design, validations and user interactions in it.
the StageElementComponent display and element based on the user choise.
Things to take in account
I want to know if it is possible to emit an event from StageElementComponent and listen to it in the Stage component? like
<template>
//StageElementComponent
<div class="element d-flex flex-column justify-content-center"
#dblclick="clear">
<i class="material-icons" v-if="element" :class="[`bg-${element.type.color}`]">{{ element.type.icon }}</i>
</div>
</template>
<script>
export default {
props : {
element : {
type : Object,
required : true
}
},
methods : {
clear : function($event) {
this.$emit('clear', {
index : this.element.index
})
}
}
}
</script>
<style scoped lang="scss">
.element {
cursor: pointer;
}
</style>
and in the Stage component listen to it like
<v-stage id="ticktr" class="section center"
:rows="height"
:columns="width"
:elements="elements"
#clear="clearElement"></v-stage>
I do need these 3 components.
I don't want to listen and emit in the StageComponent as i would be repeating myself
I don't want to use $root.$emit.
It is possible to do this? how?

Nested Iron Ajax

Ok. So my last post was too ambiguous. For my second post, let me try to approach the same problem in hopefully a little more straighforward manner. Below is the code. Here is a screenshot of the results I get. Regarding the second iron-ajax call, if I use curl in terminal with this () I get what I want (it's a link preview service, so title, img, desc etc). Trying to accomplish the same with iron-ajax post with required parameters defined per spec. I don't get any console errors (for the first time) and based on the [object.Object] result I get when I output the last-response variable in the body of second dom-repeat, appears to be returning a json object just like the first iron-ajax call (which does work, includes the link but not enough data about it, hence running link through second service that returns the data I want to display).
Result from running code locally
CODE:
<dom-module id="my-new-view">
<template>
<!-- Defines the element's style and local DOM -->
<style>
:host {
display: block;
padding: 16px;
}
</style>
<iron-ajax auto
url="https://api.rss2json.com/v1/api.json?rss_url=http://feeds.feedburner.com/DrudgeReportFeed"
params="{"fmt":"xml-rss"}"
handle-as="json"
last-response="{{ajaxResponse}}"></iron-ajax>
<p>First: {{ajaxResponse}}</p>
<template is="dom-repeat" items="[[ajaxResponse.items]]" as="item" index-as="item_no">
<p>{{item.title}}</p>
<iron-ajax auto
method="post"
url="https://guteurls.de/api/"
params="{"u":"{{item.guid}}", "r":"https://127.0.0.1", "e":"s652imb8et42xd0bd", "t":"json"}"
handle-as="json"
last-response="{{newAjaxResponse}}"></iron-ajax>
<p>Second: {{newAjaxResponse}}</p>
<template is="dom-repeat" items="[[newAjaxResponse.newItems]]" as="newItem" index-as="newItem_no">
<p>{{newItem.title}}</p>
<paper-card heading="{{newItem.title}}" image="{{newItem.image.url}}" alt="{{newItem.title}}">
<div class="card-content">
<h1>Description: {{newItem.desc}}</h1>
<p>Test</p>
</div>
<div class="card-actions">{{newItem.title}}
<paper-button>Share</paper-button>
<paper-button>Explore!</paper-button>
</div>
</paper-card>
</template>
</template>
</template>
<script>
class MyNewView extends Polymer.Element {
static get is() { return 'my-new-view'; }
}
customElements.define(MyNewView.is, MyNewView);
</script>
</dom-module>
Problems and Solutions:
params="{"fmt":"xml-rss"}"
Quoting not done properly. You can you single quote as well like
params='{"fmt":"xml-rss"}' or params="{'fmt':'xml-rss'}"
First: {{ajaxResponse}} and Second: {{newAjaxResponse}}
You can use console to debug since you cannot display object like that
params="{"u":"{{item.guid}}", "r":"https://127.0.0.1", "e":"s652imb8et42xd0bd", "t":"json"}"
Quoting not done properly.
Attribute binding i.e. {{item.guid}} must be followed by $.
Change to params$='{"u":"{{item.guid}}", "r":"https://127.0.0.1", "e":"s652imb8et42xd0bd", "t":"json"}'
newAjaxResponse.newItems
There is no newItems in newAjaxResponse. Just use newAjaxResponse
[Note: newAjaxResponse is returned as Object which must be converted to Array since dom-repeat works only with Array.]
Before you define fields like desc, image.url make sure it exists.
Working code:
<dom-module id="my-new-view">
<template>
<!-- Defines the element's style and local DOM -->
<style>
:host {
display: block;
padding: 16px;
}
</style>
<iron-ajax auto url="https://api.rss2json.com/v1/api.json?rss_url=http://feeds.feedburner.com/DrudgeReportFeed" params='{"fmt":"xml-rss"}' handle-as="json" last-response="{{ajaxResponse}}"></iron-ajax>
<p>First: {{ajaxResponse}}</p>
<template is="dom-repeat" items="[[ajaxResponse.items]]" as="item" index-as="item_no">
<p>{{item.title}}</p>
<iron-ajax auto method="post" url="https://guteurls.de/api/" params$='{"u":"{{item.guid}}", "r":"https://127.0.0.1", "e":"s652imb8et42xd0bd", "t":"json"}' handle-as="json" last-response="{{newAjaxResponse}}"></iron-ajax>
<p>Second: {{newAjaxResponse}}</p>
<template is="dom-repeat" items="[[_toArray(newAjaxResponse)]]" as="newItem" index-as="newItem_no">
<paper-card heading="{{newItem.title}}" image="{{newItem.img}}" alt="{{newItem.title}}">
<div class="card-content">
<h1>Description: {{newItem.description}}</h1>
<p>Test</p>
</div>
<div class="card-actions">{{newItem.title}}
<paper-button>Share</paper-button>
<paper-button>Explore!</paper-button>
</div>
</paper-card>
</template>
</template>
</template>
<script>
class MyNewView extends Polymer.Element {
static get is() { return 'my-new-view'; }
_toArray(obj) {
var tempArray = [];
tempArray.push(obj);
//console.log(tempArray);
return tempArray;
}
}
customElements.define(MyNewView.is, MyNewView);
</script>
</dom-module>
You can check the working demo here.

Polymer iron-list not updating view on dynamic add

I am using Polymer 1.7.0 and Angular2 to build a app. I created a custom Polymer element to wrap the iron-list template in order to be able to use it with Angular2 but i have a problem when it comes to dynamicly adding items to the iron-list.
The add function modifies the items array but it doesnt render the new element even if i am triggering the iron-resize event after modifying the items array.
If i delete a item first, and then try to add a element, then it gets rendered.
This is the polymer element i use:
<!--.... dependencies imports .... -->
<dom-module id="role-users-list">
<template>
<style is="custom-style" include="iron-flex iron-flex-alignment custom-layout-classes">
:host {
display: block;
}
</style>
<iron-media-query query="(min-width: 600px)" on-query-matches-changed="queryValueChanged" query-matches="{{wide}}"></iron-media-query>
<iron-list items="{{items}}" class="test" style="height:85%">
<template>
<paper-card class="verticalJustified">
<div class="horizontalJustified">
<div class="card-content horizontalJustified">
<paper-icon-button class="cardIcons" icon="group-work"></paper-icon-button>
<div class="verticalStart">
<span class="cardTitle">{{item.fullName}}</span>
<template is="dom-if" if="{{item.direct}}">
<span class="cardSubTitle">User associated directly </span>
</template>
<template is="dom-if" if="{{!item.direct}}">
<span class="cardSubTitle">Role granted through {{item.groupName}} </span>
</template>
</div>
</div>
<template is="dom-if" if="{{item.direct}}">
<div class="horizontalJustified">
<paper-icon-button class="cardIcons" icon="delete" target-user="{{item.username}}" on-tap="onDelete" (click)="deleteUser(user)"></paper-icon-button>
</div>
</template>
</div>
</paper-card>
</template>
</iron-list>
</template>
<script>
Polymer({
is: 'role-users-list',
onDelete: function(e){
this.fire("deleteTrigger",{data:e.model.item});
},
queryValueChanged: function(e){
this.fire("mediaQueryTrigger",{data: e.detail.value})
},
updateIronList:function(){
this._nodes.filter(function(value){return value.localName === "iron-list"})[0].fire("iron-resize");
},
focusElem:function(){
var test = this._nodes.filter(function(value){return value.localName === "iron-list"})[0];
test.selectItem(this.items[0]);
},
properties: {
items: {
type: Array,
notify: true,
value:[],
}
}
});
</script>
</dom-module>
After adding or removing a element i call the updateIronList function to trigger the rendering with the iron-resize event.
PS: The add functionality is in the parent Angular2 component where i modify the items array.
The only difference i could notice was that since the delete button is inside the polymer custom element, it causes it to focus that item when its clicked.
If i didnt make myself understood, ask and i will clarify.
I fixed my issue by updating the updateIronList function to:
updateIronList:function(){
let ironListElem = this._nodes.filter(function(value){return value.localName === "iron-list"})[0];
ironListElem.fire("iron-resize");
ironListElem._virtualCount = (this.items.length <= 20) ? this.items.length : 20;
},
It seems the issue was caused by the fact that when the items array length changed, iron-list didnt automaticly update the number of items displayed.

Import different components react from different files

How can I use component from another JS file in ReactJS.It says "Header is not defined".BTW I am using ReactJS without NodeJS.Here's the code:
<script type="text/babel" src="../Assets/js/react/Dashboard_components.js"></script>
//Here in Dashboard_components.js
var Dashboard_comp=React.createClass(
{
componentDidMount:function()
{
$('.ui.modal').modal();
},
render:function()
{
return(
<div>
//Header exists in Base component js
<Header/>
<div className="ui two columns grid">
<div className="three column wide">
<SideNav/>
</div>
<div className="thirteen column wide">
<Stats/>
<Class_boxes/>
</div>
</div>
</div>
)
}
})
At the top of your Dashboard_components.js file, and above the
var Dashboard_comp=React.createClass( line, add the line
var Header = require('./yourpath/to/header.js')
If you're using ES6 you can simply
import Header from './path/header'
Hmm..Seems like no-one got the answer so I am answering my own question.
Suppose there is a Header component in a Base_component.js file.So in order to use it in Other html's.Use this:
window.Header=Header
It would make component publicaly available for other files as well not the same js.

Categories

Resources