Vue v-if not reading variable - laravel-5.3

I am using Vue 2 on Laravel 5.3 (using bxslider for carousel)
The following code gives me a single image (img1) slider.
If I remove v-ifs it does give me 3 image (with img1 img2 img3 in the slider).
Does productChoiceSelected.img2 returns true if it is not null from the database?
EDIT (added full code of the component)
<div class="col-md-4">
<ul class="bxslider">
<li>
<img :src="productChoiceSelected.img1">
</li>
<li v-if="productChoiceSelected.img2">
<img :src="productChoiceSelected.img2">
</li>
<li v-if="productChoiceSelected.img3">
<img :src="productChoiceSelected.img3">
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data(){
return{
product:[],
productChoiceSelected:[],
productChoices:[]
}
},
props:[
'productId'
],
mounted(){
this.getProduct()
this.getAllProductChoices()
},
methods:{
getProduct(){
var vm = this;
vm.$http.get('/getProduct/'+vm.productId).then((response)=>{
vm.product = response.data.data.product;
vm.productChoiceSelected = response.data.data.productChoiceDefault;
});
},
getAllProductChoices(){
var vm = this;
vm.$http.get('/getAllProductChoices/'+vm.productId).then((response)=>{
vm.productChoices = response.data.data.productChoices;
});
}
}
}
</script>
EDIT #2
I guess it is because productChoiceSelected.img2 is a url directory? it is http://localhost:8000/img/products/1-2.bmp

I can figure out few issues in your code:
You need to have img1 and these variables defined as these are to be set reactively to be used in vue templated.
You need to move $('.bxslider').bxSlider({}) code in the updated section as this needs to be executed once you get the data updated from backend.
Following are code changes:
var demo = new Vue({
el: '#demo',
data: function(){
return {
productChoiceSelected: {
img1:'',
img2:'',
img3:''
}
};
},
mounted(){
this.getProduct()
},
updated(){
$('.bxslider').bxSlider({});
},
methods:{
getProduct(){
var vm = this;
setTimeout(function () {
vm.productChoiceSelected.img1 = 'http://bxslider.com/images/730_200/hill_trees.jpg'
// vm.productChoiceSelected.img2 = 'http://bxslider.com/images/730_200/houses.jpg'
vm.productChoiceSelected.img3 = 'http://bxslider.com/images/730_200/hill_fence.jpg'
}, 300)
}
}
})
see updated fiddle.

Thanks for saurabh contribution above, as I am still unsure where the problem is, here is how I solve the problem by myself for you guys' reference.
Change in code:
<template>
<div class="col-md-5">
<div class="col-md-12">
<ul class="bxslider">
<li>
<img :src="productChoiceSelected.img1">
</li>
<li v-if="img2">
<img :src="productChoiceSelected.img2">
</li>
<li v-if="img3">
<img :src="productChoiceSelected.img3">
</li>
</ul>
</div>
</div>
.....
.....
.....
methods:{
getProduct(){
var vm = this;
vm.$http.get('/getProduct/'+vm.productId).then((response)=>{
vm.product = response.data.data.product;
vm.productChoiceSelected = response.data.data.productChoiceDefault;
if(vm.productChoiceSelected.img1){
vm.img1 = true
}
if(vm.productChoiceSelected.img2 != null){
vm.img2 = true
}
if(vm.productChoiceSelected.img3 != null){
vm.img3 = true
}
});
},
......
......
......

Related

Vue3 Dropdown Click ul show

I want to do something like this in my project below, but every time I click it, it seems as if each element is selected.
What I want to do is simply,
When handleShow(depth) is clicked;
I want to print the clicked depth value in selected and show its ul, but it does not satisfy the condition I wrote.
v-show="selected === depth"
<template>
<li v-if="items.children" class="item dropdown">
<router-link
:link="items"
:key="depth"
#click.stop="handleShow(depth)"
/>
<ul class="dropdown-menu" v-show="selected === depth">
<MenuItems
v-for="(child, childIndex) in items.children"
:key="childIndex"
:items="child"
/>
</ul>
</li>
<li v-else class="item">
<router-link
:link="items"
:key="depth"
/>
</li>
</template>
<script>
import {ref, defineComponent} from "vue"
export default defineComponent({
name: 'MenuItems',
components: {MenuLink},
props: {
items: {type: Object, required: true},
depth: Number,
},
setup() {
let selected = ref(null);
const handleShow = (depth) => {
selected.value = depth;
}
return {
handleShow,
selected
}
},
})
</script>

Vue JS - Rendering Multidemensional Obj

I'm new to VUE, and having a hard time figuring out how to render an Object where i don't know how many levels deep it will go.
i get this object returned from a web-api
var result = {
"1":{
"id":"1",
"parent_id":"-1",
"name":"Master",
"level":1,
"children":{
"2":{
"id":"2",
"parent_id":"1",
"name":"Category A",
"level":2,
"children":{
"5":{
"id":"5",
"parent_id":"2",
"name":"Category D",
"level":3
},
"6":{
"id":"6",
"parent_id":"2",
"name":"Category E",
"level":3,
"children":{
"10":{
"id":"10",
"parent_id":"6",
"name":"Category F",
"level":4
}
}
}
}
},
"3":{
"id":"3",
"parent_id":"1",
"name":"Category B",
"level":2,
"children":{
"4":{
"id":"4",
"parent_id":"3",
"name":"Category C",
"level":3
}
}
}
}
}
};
I ignore the first level and push it in my store. Now i'm looking for a recursive way to go over all the parent's and theire children.
(this would be an example of the desired output)
<ul>
<li>Master</li>
<ul>
<li>Category A</li>
<ul>
<li>Category D</li>
<li>Category E</li>
<ul>
<li>Category F</li>
</ul>
</ul>
<li>Category B</li>
<ul>
<li>Category C</li>
</ul>
</ul>
</ul>
However i do not get further then:
<ul>
<li>Master</li>
<ul>
<li>Category A</li>
<li>Category B</li>
</ul>
</ul>
And offcourse i could go on like this and then i'll reach the end of my obj. but in real situation i don't know how deep it will go, that's why i want a solution that doesn't care about that and continiu's till there are no more children.
I saw a little something in the doc's but that code is so different from mine that i cant really understand how to apply ( https://v2.vuejs.org/v2/guide/components.html#Circular-References-Between-Components )
My init code is basically this is everything (simplified) together :
store = function() {
var self = this;
self.hierarchies = [];
self.subView = 'hierarchies';
self.tree = [];
};
Vue.component('hierarchies', {
template: '#hierarchies',
props: ['store']
});
Vue.component('products', {
template: '#products',
props: ['store']
});
Vue.component('treeview', {
template: '#treeview',
props: ['store']
});
app = new Vue({
el: '#content',
data: function () {
return {
store: {},
tree: {}
}
},
created: function () {
this.store = new store();
}
});
// DO AXJAX REQUEST GET HIERARCHY'S
var response = // AJAX {};
app.store.tree.push(response[1]);
<template id="treeview">
<div>
<ul v-if="store.tree.length">
<li v-for="m in store.tree">
{{ m.name }}
<ul v-if="m.children">
<li v-for="c in m.children">
{{ c.name }}
</li>
</ul>
<data :tree="m"></data>
</li>
</ul>
</div>
</template>
All idea's suggestions are welcome :)
Best,
Bastian
Your data is in a pretty unpractical format since you cannot loop over object keys using the v-for directive. So in order to consume that format, you have to transform it into an array first. I’m using a computed property for that, but a better solution for the long run might be to transform the data once in the beginning or even changing the result format of your web service (if you can).
var result = {"1":{"id":"1","parent_id":"-1","name":"Master","level":1,"children":{"2":{"id":"2","parent_id":"1","name":"Category A","level":2,"children":{"5":{"id":"5","parent_id":"2","name":"Category D","level":3},"6":{"id":"6","parent_id":"2","name":"Category E","level":3,"children":{"10":{"id":"10","parent_id":"6","name":"Category F","level":4}}}}},"3":{"id":"3","parent_id":"1","name":"Category B","level":2,"children":{"4":{"id":"4","parent_id":"3","name":"Category C","level":3}}}}}};
Vue.component('tree-root', {
props: ['items'],
template: `
<ul>
<tree-item v-for="item in items" v-bind:item="item"></tree-item>
</ul>
`
});
Vue.component('tree-item', {
props: ['item'],
computed: {
children: function () {
return Object.keys(this.item.children).map(k => this.item.children[k]);
}
},
template: `
<li>
{{item.name}}
<tree-root v-if="item.children" v-bind:items="children"></tree-root>
</li>
`
});
var app = new Vue({
el: '#app',
data: {
rootItem: result['1']
}
});
<ul id="app">
<tree-item v-bind:item="rootItem"></tree-item>
</ul>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
What I’m doing here is use two components: tree-root which is responsible for rendering a list of items, and tree-item which is responsible for rendering a single item, eventually recursively rendering another tree-root for the children of each item.
The computed children property is used to transform the children object into a list of children.
Finally, in order to render the root component, we just render the result['1'] which is the root element as a tree-item. We could also render a tree-root instead with the whole result but then we would have to convert it first into an array (just like in the computed property), so I just opted with the simpler solution here.

Display User Data with React/Meteor Application

I have been trying various methods to display data from a Meteor fetch within a React template. I don't fully understand why this is so hard to do.
When I use getMeteorData within the template it doesn't display the data when using {this.data.user._id}
getMeteorData() {
return {
user: Meteor.user()
};
},
This is the same story when using componentDidMount.
I have followed the react & meteor tutorial on the meteor website and they use props. This however feels a lot of work for simply pulling and fetching data.
This is an example of an attempt
export var MenuLoggedIn = React.createClass({
displayName: 'MenuLoggedIn',
mixins: [
Router.State, Router.Navigation
],
getMeteorData() {
return {
user: Meteor.user()
};
},
getInitialState(){
return {
avatarImagePreview: null,
avatarImage: null
};
},
render: function() {
return (
<div className="container">
<div className="menu-structure col-md-12">
{this.data.user._id}
<div className="left-menu pull-left">
<img className="logo" src="/img/logo.png"/>
<h1 className="eventburger-title">eventburger</h1>
</div>
<div className="right-menu">
<div className="search-bar">
<i className="fa fa-search"></i>
<input type="text" name="Search" placeholder="Harpist, Photographer, Caterer..."/>
</div>
{this.data
? <img src="/img/avatar.png"/>
: <img src="/img/placeholder-person.jpg"/>}
<span>{this.data}
Feeney</span>
<a href="/app/inbox">
<i className="fa fa-envelope"></i>
</a>
<a className="head-spacing" href="#" onClick={this.logout}>LOG OUT</a>
</div>
</div>
</div>
);
},
logout: function(e) {
e.preventDefault();
Meteor.logout();
window.location.href = "/login"; //Need to be moved to History
}
});
You need to use the ReactMeteorData mixin to make getMeteorData() work.
mixins: [
Router.State, Router.Navigation, ReactMeteorData
],
https://atmospherejs.com/meteor/react-meteor-data

Adding custom right click menu to the nodes of treeview in MVC

This is my helper for generating a treeview.
Using this i am able to generate the treeview in the mvc5.
#helper GetTreeView(List<MvcTreeview.Models.Category> siteMenu, int parentID)
{
foreach (var i in siteMenu.Where(a => a.ParentID.Equals(parentID)))
{
<li>
#{var submenu = siteMenu.Where(a => a.ParentID.Equals(i.ID)).Count();}
#if (submenu > 0)
{
<span class="collapse collapsible"> </span>
}
else
{
<span style="width:15px; display:inline-block"> </span>
}
<span id="Category">
#i.CategoryName
#*oncontextmenu="return false"*#
</span>
#if (submenu > 0)
{
<ul>
#Treeview.GetTreeView(siteMenu, i.ID)
#* Recursive Call for Populate Sub items here*#
</ul>
}
</li>
}
}
This is my View for displaying
#model List<MvcTreeview.Models.Category>
#{
ViewBag.Title = "Simple";
}
<div class="gridbox gridleft">
<div class="left">
<div style="padding:10px; background-color:#FAFAFA">
<div class="treeview">
#if (Model != null && Model.Count() > 0)
{
<ul>
#Treeview.GetTreeView(Model, Model.FirstOrDefault().ParentID)
</ul>
}
</div>
</div>
</div>
</div>
<div id="onSuccess">
</div>
#* Here We need some Jquery code for make this treeview collapsible *#
#section Scripts{
<script type="text/javascript">
$(document).ready(function () {
$(".treeview li>ul").css('display', 'none'); // Hide all 2-level ul
$(".collapsible").click(function (e) {
e.preventDefault();
$(this).toggleClass("collapse expand");
$(this).closest('li').children('ul').slideToggle();
});
});
function PassingFunction(clicked_id) {
url = '#Url.Action("Details", "TestDetails")';
$.ajax({
url: url,
type: 'GET',
data: { 'id': clicked_id },
success: function (returnData) {
$("#onSuccess").html(returnData);
console.log(returnData);
},
error: {
}
});
}
</script>
}
Now i want to add the custom functionalities to the nodes of the treeview
Add
Delete
Edit
How can i do that?
There is a lot of JQuery Context Menu options on this link.
I have just picked the most forked of them, jQuery contextMenu.
I created a JSFeed fork in an example of treeview using UL/LI and created this JSFiddle with the Context-Menu to help you:
http://jsfiddle.net/mqueirozcorreia/0h82qto6/
Explaining the code:
I have added the externals resources:
http://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.min.css
https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js
http://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.min.js
All the magic goes in the javascript code, configuring the context-menu.
The selector property will put the context menu in every element of type <span> and having class attribute with value "contextMenuItem":
selector: 'span.contextMenuItem',
When the user clicks the callback function below runs. In this example, it alerts/logs what key was selected and the id attribute value.
callback: function(key, options) {
var m = "clicked: " + key + " on element of id " + options.$trigger.attr("id");
window.console && console.log(m) || alert(m);
},

Translate Handlebars block helpers using end tags into Meteor?

In Meteor, I have an app where I make a list of items grouped by tags, where non-tagged items come first and then tagged items are hidden under a "tag header" drop down.
I haven't touched this app since 0.8 came out, and I was using a block helper in a template which worked fine in pre-0.8...
See working jsfiddle here
Handlebars.registerHelper('eachItem', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
if(!context[i].tag){
ret = ret + options.fn(context[i]);
} else {
if(i===0||!context[i-1].tag ||context[i-1].tag !== context[i].tag){
ret = ret + '<li class="list-group-item"><a data-toggle="collapse" data-target="#'+ context[i].tag +'"> Items tagged '+context[i].tag + '</a></li><div id="'+context[i].tag+'" class="collapse">';
}
ret = ret + options.fn(context[i]);
if(i+1<j && context[i+1].tag !== context[i].tag){
ret = ret + '</div>';
}
}
}
return ret;
});
But I'm struggling a bit to translate this into post-0.8 Meteor
The inserted HTML must consist of balanced HTML tags. You can't, for example,
insert " </div><div>" to close an existing div and open a new one.
One idea I had was to render the non-tagged items and also the containers in a vanilla {{#each}} loop (with 2 different templates), and then do something like this
Template.myListContainer.rendered = function(){
_.each(tags, function(tag){
var tagged_items = _.filter(items, function(item){ return item.tag == tag; });
_.each(tagged_items, function(item){
UI.insert(UI.RenderWithData(listItemTemplate, { item : item }), tagContainer);
});
});
}
Is there a simpler way to do this ? If the items come from a collection, will they keep their reactivity ?
Many thanks in advance !
If you haven't already, it's worth reading the Meteor wiki on migrating to blaze.
Anyhow, this one seems difficult to implement as a straight iteration.
If you don't need them in a specific order, I would just list items w/ and w/o tags, eg:
Template.example.helpers({
dataWithoutTags: function(){
return items.find({tag:{exists: false}});
},
tagList: function(){
// create a distinct list of tags
return _.uniq(items.find({tag:{exists: true}}, {tag: true}));
},
dataForTag: function(){
// use `valueOf` as `this` is a boxed-string
return items.find({tag: this.valueOf()});
}
});
Template:
<template name="example">
<div class='panel panel-default'>
<ul class='list-group'>
{{#each dataWithoutTags}}
<li class='list-group-item'>{{name}}</li>
{{/each}}
{{#each tagList}}
<li class="list-group-item">
<a data-toggle="collapse" data-target="#{{this}}"> Items tagged {{this}}</a>
</li>
<div id="{{this}}" class="collapse">
{{#each dataForTag}}
<li class='list-group-item'>{{name}}</li>
{{/each}}
</div>
{{/each}}
</ul>
</div>
</template>
If you do need them in a specific order - eg. grouping only consecutive items (as per your example). The only option would be to pre-process them.
eg:
Template.example.helpers({
itemsGrouped: function(){
dataGroups = [];
currentGroup = null;
items.find({}, {sort: {rank: 1}}).forEach(function(item){
if (currentGroup && (!item.tag || currentGroup.tag != item.tag)){
dataGroups.push(currentGroup);
currentGroup = null;
}
if (item.tag){
if (!currentGroup){
currentGroup = {
group: true,
tag: item.tag,
items: [item]
};
} else {
currentGroup.items.push(item);
}
} else {
dataGroups.push(item);
}
});
if (currentGroup){ dataGroups.push(currentGroup); }
return dataGroups;
}
});
Template:
<template name="example">
<div class='panel panel-default'>
<ul class='list-group'>
{{#each itemsGrouped}}
{{#if group}}
<li class="list-group-item">
<a data-toggle="collapse" data-target="#{{tag}}"> Items tagged {{tag}}</a>
</li>
<div id="{{tag}}" class="collapse">
{{#each items}}
<li class='list-group-item'>{{name}}</li>
{{/each}}
</div>
{{else}}
<li class='list-group-item'>{{name}}</li>
{{/if}}
{{/each}}
</ul>
</div>
</template>
If you log the value you are returning from the eachItem helper, you will see you are not closing the last div element. Fixing that may get it working again. However, you have div elements as direct children of the ul element, which you are not supposed to according to the HTML5 specification:
Permitted contents: Zero or more li elements
Also, I think it would be a better option for you to prepare your context in a format that makes it easier to let simple templates take care of the rendering. For example, using the same data from your jsfiddle, suppose you had it in the following format:
tagGroups = [{
names: ["item1", "item2"]
},{
tag: "red",
names: ["item3", "item4"]
},{
tag: "blue",
names: ["item5", "item6", "item7"]
}]
The following templates would give you the expected result in a valid html format. The ending result is a bootstrap panel containing collapsible list-groups:
<template name="accordion">
<div class="panel panel-default">
{{#each tagGroups}}
{{> tagGroup}}
{{/each}}
</div>
</template>
<template name="tagGroup">
{{#if tag}}
{{> namesListCollapse}}
{{else}}
{{> namesList}}
{{/if}}
</template>
<template name="namesList">
<ul class="list-group">
{{#each names}}
<li class="list-group-item">{{this}}</li>
{{/each}}
</ul>
</template>
<template name="namesListCollapse">
<div class="panel-heading"><a data-toggle="collapse" data-target="#{{tag}}">Items tagged {{tag}}</a></div>
<ul id="{{tag}}" class="panel-collapse collapse list-group">
{{#each names}}
<li class="list-group-item">
{{this}}
</li>
{{/each}}
</ul>
</template>
And here is an example of a helper to transform your data so you can make a quick test to see it working:
Template.accordion.tagGroups = function () {
var data = [{
name : 'item1'
},{
name : 'item2'
},{
name : 'item3',
tag : 'red'
},{
name : 'item4',
tag : 'red'
},{
name : 'item5',
tag : 'blue'
},{
name : 'item6',
tag : 'blue'
},{
name : 'item7',
tag : 'blue'
}];
data = _.groupBy(data, 'tag');
data = _.map(data, function (value, key) {
var obj = {};
if (key !== 'undefined') {
obj.tag = key;
}
var names = _.map(value, function (item) {
return item.name;
});
obj.names = names;
return obj;
});
//console.dir(data);
return data;
};
And yes, if the items come from a collection, you will get reactivity by default.

Resources