Im using Vue JS (2.0.8) to render a list of devices. I have a health status that is represented by a number, so I use a method to convert the number to a CSS class to display it correctly. The problem is that Vue does not render the result of the method, but the method call itself.
My Vue method:
methods: {
...
health: function (device) {
if (device !== null) {
switch (device.health.status) {
case 2:
return "connected";
break;
case 1:
return "warning";
break;
case 0:
return "disconnected";
break;
case -1:
default:
return "unsupported";
break;
}
}
},
...
}
My HTML (Im using Laravel, hence the '#'):
<div v-for="device in devices" class="device">
<div class="device-top">
<div class="device-bullet #{{ health(device) }}"></div>
<div v-on:click="device.open = !device.open" v-bind:class="{open: device.open}" class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
This renders the following HTML:
<div class="device>
<div class="device-top">
<div class="device-bullet {{ health(device) }}"></div>
<div class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
However, this is the odd bit. If I move the #{{ health(device) to inside the device-bullet div instead of using it in an attribute, it renders like this (correctly, "connected" being the result of the function).
<div class="device>
<div class="device-top">
<div class="device-bullet">connected</div>
<div class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
I have tried any combination I can think of to get it to render correctly but cannot seem to find the problem.
Any help is appreciated, thank you in advance.
Interpolation within attributes has been removed in Vue 2.x
Use v-bind:class instead:
<div class="device-bullet" v-bind:class="[health(device)]"></div>
Not sure of how laravel works, never used before.
But in "pure" vue can't use {{}} syntax to bind a html property. You need to use v-bind directive
<div v-bind:class="'device-bullet '+ health(device)" ></div>
Without v-bind: you're literally using device-bullet {{ health(device) }} as value.
For more clarification check this docs section
Related
I am still very weak in Ajax, but they told me that this is a way out of the situation that I have developed.
I have a javascript function that filters categories. There is also alternation of lists in php.blade.
When I click on "All", all blogs are displayed and the alternation is working properly. When I select the "desired category", all blogs from this category are displayed, but alternation does not work.
I was prompted that you can is to call one ajax functional on category selection and return the HTML response on that ajax call. But I don't know how to do this, can anyone help?
JavaScript
$('.category-filter_item').click(function(){
$('.category-filter_item').removeClass('active')
$(this).addClass('active')
var dataFilter = $(this).attr('data-filter');
$('.blog-list').hide()
$(dataFilter).show()
})
php.blade
#extends('layouts.app')
#section('content')
<div class="container">
<div class="category-filter" id="filter">
<div class="category-filter_item active" data-filter="*">All</div>
#foreach($categories as $category)
<div class="category-filter_item" data-filter=".category_{{$category->id}}">{{ $category->title }}</div>
#endforeach
</div>
#foreach ($blogs as $index => $blog)
<div class="blog-list">
#if ($index % 2 === 1) //Alternation
<div class="blog blog--left" >
<h2 class="blog_title">{{ $blog->title }}</h2>
</div>
#else
<div class="blog blog--right">
<h2 class="blog_title">{{ $blog->title }}</h2>
</div>
#endif
</div>
#endforeach
</div>
#endsection
Controller
public function index()
{
$blogs = Blog::all();
$categories = Category:all();
return view('blog', compact('blogs', 'categories'));
}
Need to create new route and controller function to return only the selected category blogs and that should be in json format
Once that is done need to use the ajax call to the above route in the click function defined. Capture the response and then iterate and generate the html that should be inside the blog-list in your script. then update the html inside blog-list value.
refer this link
refer this for making ajax call link
I have a ref variable (foxArticles ), which holds a list that contains 100 items. In a v-for loop i loop over each value. As a result, i have 100 values rendered on the page.
<template>
<div class="news_container">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput)"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const foxArticles = ref([]);
</script>
I also have a search function, which returns the value, if it includes the passed in keyword. The function is used in the child of the v-for loop.
<div class="search_input_container">
<input
type="text"
class="search_input"
v-model="keywordInput"
/>
</div>
<script>
const keywordInput = ref("");
function containsKeyword(article, keywordInput) {
if (article.title.toLowerCase().includes(keywordInput.toLowerCase())) {
return article;
}
}
</script>
The problem is, i can't use .slice() on the foxArticles array in the v-for loop, because that screws up the search functionality, as it returns only the values from the sliced range.
How can i have the access the all of the values of the array, while not rendering all 100 of returned articles on the initial load?
Any suggestions?
I think your approach will make it incredibly complex to achieve. It would be simpler to always iterate over some set, this set is either filtered based on a search-term, or it will be the first 100 items.
I'm not very familiar yet with the Vue 3 composition api so I'll demonstrate with a regular (vue 2) component.
<template>
<div class="news_container">
<div
v-for="article in matchingArticles"
v-bind:key="article"
class="article_single_cell"
>
... news_box ...
</div>
</div>
</template>
<script>
export default {
...
computed: {
matchingArticles() {
var articles = this.foxArticles;
if (this.keywordInput) {
articles = articles.filter(article => {
return this.containsKeyword(article, this.keywordInput)
})
} else {
// we will limit the result to 100
articles = articles.slice(0, 100);
}
// you may want to always limit results to 100
// but i'll leave that up to you.
return articles;
}
},
....
}
</script>
Another benefit is that the template does not need to worry about filtering results.
ok, so i kind of came up with another solution, for which you don't have to change the script part...
instead of having one v-for loop , you can make two of them, where each one is wrapped in a v-if statement div
The first v-if statement says, If the client has not used the search (keywordInput == ''), display articles in the range of (index, index)
The second one says = If the user has written something (keywordInput != ''), return those articles.
<template>
<div class="news_container">
<!-- if no search has been done -->
<div v-if="keywordInput == ''">
<div
v-for="article in foxArticles.slice(0, 4)"
v-bind:key="article"
class="article_single_cell"
>
<div class="news_box shadow hover:bg-red-100 ">
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
<!-- if searched something -->
<div v-else-if="keywordInput != ''">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput) && keywordInput != ''"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
im not sure how this impacts performance tho, but that's a problem for another day
I am trying to use a button to update a property of an Object in Vue. The Object is returned by an AJAX query to a database, and the isFetching boolean is then set to false, which attaches the containing div to the DOM. When I try and update the property, I get the following error:
TypeError: Cannot read property '_wrapper' of undefined
Below is my AJAX code:
axios.post("/api/myEndpoint", { id: router.currentRoute.query.id })
.then((response) => {
this.activities = response.data.activities;
this.isFetching = false;
})
.catch((errors) => {
console.log(errors);
router.push("/");
});
Here is my HTML:
<div v-if="isFetching">
<h2>Loading data...</h2>
</div>
<div v-else>
<div class="row no-gutters">
<div class="col">
<div class="d-flex flex-row justify-content-center">
<h4>Activities</h4>
</div>
<div class="custom-card p-2">
<div class="row no-gutters pb-4" v-for="(activity, index) in activities"
:key="activity.stage_id">
<button v-if="!activity.is_removed" class="btn custom-btn" :class="{'hov':
index == 0}" :disabled="index != 0"
#click="markRemoved(activity)">Remove</button>
</div>
</div>
</div>
</div>
</div>
Finally, here is markRemoved() as called by the button's click listener:
markRemoved(a) {
a.is_removed = true;
}
When I try and log a in markRemoved() the Object is logged to the console fine, exactly as expected. Having stepped through it in the debugger, the exception is thrown at the point I try and update the is_removed property of the Object.
Does anyone know what I'm doing wrong here?
Note: the id I pass to the AJAX query is a query parameter of Vue Router. This is also set correctly and passed as expected.
Here is an example activity Object:
{date_time_due: "2020-12-09T11:43:07.740Z"
date_time_reached_stage: "2020-12-02T11:43:07.740Z"
is_complete: true
is_removed: false
selected_option: "Some text"
stage_id: 1
stage_name: "Some text"}
The exception is only thrown on the first click of the button.
Posting in case anybody else comes across this error in the future.
Vue requires exactly one root element within a single-file component's <template> tags. I had forgotten about this and in my case had two <div> elements, shown one at a time, conditionally using v-if:
<template>
<div v-if="fetchingData">
<h2>Loading data...</h2>
</div>
<div v-else>
<!-- The rest of the component -->
</div>
</template>
This caused problems with Vue's reactivity, throwing the error whenever I tried to update some part of the component. After realising my mistake, I wrapped everything in a root <div>. This solved the issue for me:
<template>
<div id="fixedComponent">
<div v-if="fetchingData">
<h2>Loading data...</h2>
</div>
<div v-else>
<!-- The rest of the component -->
</div>
</div>
</template>
I have a rather sophisticated template for Kendo ListView using knockout-kendo.js bindings. It displays beautifully. My problem is that I need to use the visible and click bindings in parts of the template, but I can't get them to work. Below is a simplified version of my template. Basically, deleteButtonVisible determines whether the close button can be seen, and removeComp removes the item from the array.
<div class='template'>
<div >
<div style='display:inline-block' data-bind='visible: deleteButtonVisible, event: {click: $parent.removeComp}'>
<img src='../../../Img/dialog_close.png'></img>
</div>
<div class='embolden'>#= type#</div><div class='label1'> #= marketArea# </div>
<div class='label2'> #= address# </div>
<!-- more of the same -->
</div>
The view model:
function CompViewModel() {
var self = this;
self.compData = ko.observableArray().subscribeTo("compData");
self.template = kendo.template(//template in here);
self.removeComp = function (comp) {
//do something here
}
}
html:
<div class="row" >
<div class="col-md-12 centerouter" id="compDiv" >
<div class="centerinner" id="compListView" data-bind="kendoListView: {data: compData, template: template}"></div>
</div>
</div>
finally, sample data:
{
type: "Comparable",
marketArea: "",
address: "2327 Bristol St",
deleteButtonVisible: true
},
Take in count that the deleteButtonVisible must be a property on the viewModel linked to the view.You are not doing that right now. The click element can v¡be access from the outer scope of the binding and remove the $parent.He take the method from the viewmodel. Take in count that every thing that you take on the vie must be present on the view model for a easy access.
I'm trying to apply a background image to a div by using the angular ng-style ( I tried a custom directive before with the same behaviour ), but it doesn't seem to be working.
<nav
class="navigation-grid-container"
data-ng-class="{ bigger: isNavActive == true }"
data-ng-controller="NavigationCtrl"
data-ng-mouseenter="isNavActive = true; $parent.isNavActive = true"
data-ng-mouseleave="isNavActive = false; $parent.isNavActive = false"
data-ng-show="$parent.navInvisible == false"
data-ng-animate="'fade'"
ng-cloak>
<ul class="navigation-container unstyled clearfix">
<li
class="navigation-item-container"
data-ng-repeat="(key, item) in navigation"
data-ng-class="{ small: $parent.isNavActive, big: isActive == true }"
data-ng-mouseenter="isActive = true; isInactive= false"
data-ng-mouseleave="isActive = false; isInactive= true">
<div data-ng-switch on="item.id">
<div class="navigation-item-default" data-ng-switch-when="scandinavia">
<a class="navigation-item-overlay" data-ng-href="{{item.id}}">
<div class="navigation-item-teaser">
<span class="navigation-item-teaser-text" data-ng-class="{ small: isScandinavia }">{{item.teaser}}</span>
</div>
</a>
<span class="navigation-item-background" data-ng-style="{ 'background-image': 'public/img/products/{{item.id}}/detail/wide/{{item.images.detail.wide[0]}}' }"></span>
</div>
<div class="navigation-item-last" data-ng-switch-when="static">
<div class="navigation-item-overlay">
<div class="navigation-item-teaser">
<span class="navigation-item-teaser-text">
<img data-ng-src="{{item.teaser}}">
</span>
</div>
</div>
<span class="navigation-item-background">
<img class="logo" data-ng-src="{{item.images.logo}}">
</span>
</div>
<div class="navigation-item-default" data-ng-switch-default>
<a class="navigation-item-overlay" data-ng-href="{{item.id}}">
<div class="navigation-item-teaser">
<span class="navigation-item-teaser-text">{{item.teaser}}</span>
</div>
</a>
<span class="navigation-item-background" data-ng-style="{ 'background-image': 'public/img/products/{{item.id}}/detail/wide/{{item.images.detail.wide[0]}}' }"></span>
</div>
</div>
</li>
</ul>
</nav>
Though, if I do try a background color, it seems to be working fine. I tried a remote source and a local source too http://lorempixel.com/g/400/200/sports/, neither worked.
It is possible to parse dynamic values in a couple of way.
Interpolation with double-curly braces:
ng-style="{'background-image':'url({{myBackgroundUrl}})'}"
String concatenation:
ng-style="{'background-image': 'url(' + myBackgroundUrl + ')'}"
ES6 template literals:
ng-style="{'background-image': `url(${myBackgroundUrl})`}"
Correct syntax for background-image is:
background-image: url("path_to_image");
Correct syntax for ng-style is:
ng-style="{'background-image':'url(https://www.google.com/images/srpr/logo4w.png)'}">
IF you have data you're waiting for the server to return (item.id) and have a construct like this:
ng-style="{'background-image':'url(https://www.myImageplusitsid/{{item.id}})'}"
Make sure you add something like ng-if="item.id"
Otherwise you'll either have two requests or one faulty.
For those who are struggling to get this working with IE11
HTML
<div ng-style="getBackgroundStyle(imagepath)"></div>
JS
$scope.getBackgroundStyle = function(imagepath){
return {
'background-image':'url(' + imagepath + ')'
}
}
This worked for me, curly braces are not required.
ng-style="{'background-image':'url(../../../app/img/notification/'+notification.icon+'.png)'}"
notification.icon here is scope variable.
If we have a dynamic value that needs to go in a css background or
background-image attribute, it can be just a bit more tricky to
specify.
Let’s say we have a getImage() function in our controller. This
function returns a string formatted similar to this:
url(icons/pen.png). If we do, the ngStyle declaration is specified the
exact same way as before:
ng-style="{ 'background-image': getImage() }"
Make sure to put quotes around the background-image key name.
Remember, this must be formatted as a valid Javascript object key.
Just for the records you can also define your object in the controller like this:
this.styleDiv = {color: '', backgroundColor:'', backgroundImage : '' };
and then you can define a function to change the property of the object directly:
this.changeBackgroundImage = function (){
this.styleDiv.backgroundImage = 'url('+this.backgroundImage+')';
}
Doing it in that way you can modify dinamicaly your style.
The syntax is changed for Angular 2 and above:
[ngStyle]="{'background-image': 'url(path)'}"