Not able to bind key property with Alpine.js - alpine.js

The following code does not work as expected.
<head>
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body x-data="{name: 'test', value: 1}">
<button :key="name" #click="alert(Alpine.bound($el, 'key', 'unknown'))">
click
</button>
</template>
</body>
After clicking the button, it should display "test", while it display "unknown" instead. What's wrong with it ?

In Alpine.js :key works with x-for. It might be a bug. :key becomes a reserved property. You could check the implementation of x-bind.
directive('bind', (el, { value, modifiers, expression, original }, { effect }) => {
...
if (value === 'key') return storeKeyForXFor(el, expression)
let evaluate = evaluateLater(el, expression)
effect(() => evaluate(result => {
// If nested object key is undefined, set the default value to empty string.
if (result === undefined && typeof expression === 'string' && expression.match(/\./)) {
result = ''
}
mutateDom(() => bind(el, value, result, modifiers))
}))
})
So just avoid use :key, choose another name.
<head>
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body x-data="{name: 'test', value: 1}">
<button :alter-key="name" #click="alert(Alpine.bound($el, 'alter-key', 'unknown'))">
click
</button>
</template>
</body>

Related

AlpineJS X-ref Binding to display HTML from another file

I am currently learning Alpine JS and trying to find examples of binding using x-ref ( As per to the Docs: https://github.com/alpinejs/alpine#x-ref )but there isn't many as it only became a feature late year year. I am just wondering could anyone provide an example on how to x-ref a variable so it can be used in a Fetch Method.
Currently I am not using x-ref and using x-html which is not rendering the HTML code for some reason, so I'm hoping by using x-ref it will work(?)
<div x-data="foo()" x-init="init()" >
<div>
<template x-for="foo in list" :key="foo.id">
<button
#click="activeTab = itemfooid"
x-text="foo.name"
x-ref="foo.name"
x-on:click="getHTML( item )"
>
</template>
<div x-ref="myxRef" x-show="activeTab === 0" x-html ="foo[0].name"> post </div>
</div>
Fetch Foo
function foo(){
activeTab: 0, // Set active tab to POST
list: [],
init(){
this.list = { id: 1, name: 'foo', code: 'null' },
}
}
getHTML( foo){
fetch('url/example.html')
.then(response => response.text() )
.then(html => {
if( foo.code=== undefined ){
foo.code= html
}
})
.catch( error => console.log ( error ) )
},
Example HTML:
<code language-php> $test = 1 <div x-if="country === undefined"> echo "Hey"; </div> return $test </code>
My code will only have the code brackets and the rest inside will be plain text.
Below is an example I found on which I'm trying to follow but that doesn't work either. https://laracasts.com/discuss/channels/javascript/laravel-alpinejs-fetch-and-x-ref
<div x-ref="test" class="window-content"></div>
<script>
function windowManager()
fetch('/frontend/blog')
.then(response => response.text())
.then(html => { this.$refs.test.innerHTML = html })
}
</script>

VueDraggable and Laravel

I'm confused as how to correctly use vueDraggable together with Laravel.
I can drag and sort the elements in the browser but the array is not changing (when I check in the console)/ it seems to me the changes aren't reflected in the array. Shouldn't the array index numbers change after moving items?
In the overview.blade.php I have the component:
<qm-draggable :list="{{ $mylaravelarray }}"></qm-draggable>
In the qm-draggable.vue I have:
<template>
<draggable group="fragenblatt" #start="drag=true" #end="endDrag" handle=".handle">
<li v-for="(item, index) in draggablearray" :key="item.index">
// list items here
</li>
</draggable>
</template>
<script>
data() {
return {
draggablearray:{},
};
},
props: {
list: Array,
},
mounted: function(){
this.draggablearray = this.list; // create a new array so I don't alter the prop directly.
},
[..]
</script>
In the documentation it says, one way to pass the array is:
value
Type: Array
Required: false
Default: null
Input array to draggable component. Typically same array as referenced by inner element v-for directive.
This is the preferred way to use Vue.draggable as it is compatible with Vuex.
It should not be used directly but only though the v-model directive:
<draggable v-model="myArray">
But where do I do that? in overview.blade.php or in the component (.vue), or both?
Try setting v-model on your draggable as that's what will update draggablearray.
Also if draggablearray is supposed to be an array, initialise it as one, so draggablearray:{} should be draggablearray:[].
new Vue({
el: '#app',
data: () => {
return {
drag: false,
draggablearray: [{
id: 1,
name: "1"
}, {
id: 2,
name: "2"
}, {
id: 3,
name: "3"
}]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js">
</script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs#1.7.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.16.0/vuedraggable.min.js"></script>
<div class="container">
<div id="app">
<draggable v-model="draggablearray" group="fragenblatt">
<li v-for="(item, index) in draggablearray">
{{item.name}}
</li>
</draggable>
{{draggablearray}}
</div>
</div>
<script type="text/x-template" id="tree-menu">
<div class="tree-menu">
<div class="label-wrapper">
<div :style="indent" :class="labelClasses" #click.stop="toggleChildren">
<i v-if="nodes" class="fa" :class="iconClasses"></i>
<input type="checkbox" :checked="selected" #input="tickChildren" #click.stop /> {{label}}
</div>
</div>
<draggable v-model="nodes" :options="{group:{ name:'g1'}}">
<tree-menu v-if="showChildren" v-for="node in nodes" :nodes="node.nodes" :label="node.label" :depth="depth + 1" :selected="node.selected" :key="node">
</tree-menu>
</draggable>
</div>
</script>
Ah, I solved it, now I get the altered array back, I achieved it with this:
Had to add v-model="draggablearray" in the component .vue file
Needed to change my 'draggablearray' in data to an Array, instead of
object.
It looks like this now:
In the overview.blade.php I have the component:
<qm-draggable :list="{{ $mylaravelarray }}"></qm-draggable>
In the qm-draggable.vue I have:
<template>
<draggable v-model="draggablearray" group="fragenblatt" #start="drag=true" #end="endDrag" handle=".handle">
<li v-for="(item, index) in draggablearray" :key="item.index">
// list items here
</li>
</draggable>
</template>
<script>
data() {
return {
draggablearray:[], //has to be an Array, was '{}' before
};
},
props: {
list: Array,
},
mounted: function(){
this.draggablearray = this.list; // create a new array so I don't alter the prop directly.
},
[..]
</script>

Find a matching value in Vue component

I have passed this collection (postFavourite) to my vue component via props.
[{"id":1,"user_id":1,"post_id":2,"created_at":"2018-07-24 09:11:52","updated_at":"2018-07-24 09:11:52"}]
How do I then check if any instance of user_id in the collection is equal to userId which is the current logged in user (also sent via props).
Tried
let pf = _.find(this.postFavourite, { "user_id": this.userId})
Keep getting undefined as the value of the pf variable even though this.userID is equal to 1.
New to JS and Vue.js so any help would be great.
Here is the vue component code.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
data() {
return {
toggle: 0,
}
},
props: ['postData', 'postFavourite', 'userId'],
mounted() {
console.log("Post is :"+ this.postData)
console.log("User id is: "+ this.userId)
console.log("Favourite Object is :" +this.postFavourite);
console.log(this.postFavourite.find(pf => pf.user_id == this.userId));
},
methods: {
onClick() {
console.log(this.postData);
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
This is how I passed the props to vue
<div id="app">
<favorite :post-data="'{{ $post->id }}'" :post-favourite="'{{Auth::user()->favourite }}'" :user-id="'{{ $post->user->id }}'"></favorite>
</div>
I gave up on lodash and find and just messed around with the data in the chrome console to work out how to check the value I wanted.
Then I built a loop to check for the value.
If it found it toggle the like heart on of not leave it off.
This will not be the best way to solve this problem but I'm just pleased I got my first real vue component working.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
props: ['postData', 'postFavourite', 'userId']
,
data() {
return {
toggle: 0,
favs: [],
id: 0
}
},
mounted () {
var x
for(x=0; x < this.postFavourite.length; x++){
this.favs = this.postFavourite[x];
if(this.favs['post_id'] == this.postData) {
this.toggle = 1
this.id = this.favs['id']
}
}
},
methods: {
onClick() {
console.log(this.postData)
if(this.toggle == 1){
axios.post('favourite/delete', {
postid: this.id
})
.then(response => {})
.catch(e => {
this.errors.push(e)
})
}
else if(this.toggle == 0){
axios.post('favourite', {
user: this.userId,
post: this.postData
})
.then(response => {
this.id = response.data
})
.catch(e => {
this.errors.push(e)
})
}
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
Where I pass my props.
<favorite :post-data="'{{ $post->id }}'"
:post-favourite="{{ Auth::user()->favourite }}"
:user-id="'{{ Auth::user()->id }}'"></favorite>
Thanks to all that tried to help me.
From just the code you provided, I see no issue. However lodash is not required for this problem.
Using ES2015 arrow functions
let pf = this.postFavourite.find(item => item.user_id === this.userId);
Will find the correct item in your array
You can read more about this function in the mdn webdocs
You can use find() directly on this.postFavourite like this:
this.postFavourite.find(pf => pf.user_id == this.userId);
Here is another way to do it that might help you as well.
[EDIT]
In order to use find() the variable needs to be an array, this.postFavourite is sent as a string if you didn't use v-bind to pass the prop thats what caused the error.
To pass an array or an object to the component you have to use v-bind to tell Vue that it is a JavaScript expression rather than a string. More informations in the documentation
<custom-component v-bind:post-favourite="[...array]"></custom-component>

Redux: Cannot read property 'id' of undefined

The code below works as intended unless clickable list item has been clicked. So, mapped id is fine when we add items and is undefined when the <li> is clicked.
Please, what's wrong with this pretty basic piece of code?
let nextId = 0;
export default React.createClass({
addItem(){
let text = ReactDOM.findDOMNode(this.refs.doos).value;
store.dispatch({
type: 'ADD_TODO',
text,
id: nextId++
})
ReactDOM.findDOMNode(this.refs.doos).value = '';
},
render(){
return <div>
<p>Toddos!</p>
<input ref="doos" type="text"/>
<button onClick={this.addItem}>Add Item</button>
<ul>
{this.props.todos.map(todo =>
<li key={todo.id} // Can not read property when clicked!
onClick={() => {
store.dispatch({
type: 'TOGGLE_TODO',
id: todo.id
});
}}
style={{
textDecoration: todo.completed ?
'line-through' : 'none',
cursor: 'pointer'
}}>
{todo.text}
</li>
)}
</ul>
</div>
}
});
It has probably something to do with your reducers not returning state. In your reducer that maps over all todos, check if it returns your state for every todo.
What could be a possible problem is that you are using curly brackets in your map function for your todosReducer. However using curly brackets in ES6 means you should explicitely return values. Omitting the curly brackets automatically returns the value.
In your todosReducer:
case 'TOGGLE_TODO':
return state.map(todo => {
todoReducer(todo, action) // You should use return here
});
Or:
case 'TOGGLE_TODO':
return state.map(todo =>
todoReducer(todo, action) // Omitted curly brackets
);

Kendo UI Cascading Dropdown initial value not setting up

I have cascade dropdownlists and when trying to define initial value (under cascade, too) instead to call the ajax controller methods with parent selected value it's passing blank values.
I have equirement like, i can save my last search values and store it int database and after coming to that screen again, values will be selected by default.
Everything is in Javascript means storing the values/ retriving and applying to controls :
My Cascade dropdown code :
<div style="width: 400px; clear: both; float: left; position: relative; display: table-cell;">
<ul style="width: 450px; clear: both; float: none;">
<li>
<div class="editor-row">
<div class="editor-label-short">Home Country</div>
<div class="editor-field">
#(Html.Kendo().DropDownList()
.Name("HOME_COUNTRY_ID")
.HtmlAttributes(new { style = "width:200px" })
.OptionLabel("Select Home Country")
.DataTextField("Text")
.DataValueField("Value")
.DataSource(source => source.Read(read => read.Action("GetCountryJSON", "Employee")))
)
</div>
</div>
</li>
<li>
<div class="editor-row">
<div class="editor-label-short">Home City</div>
<div class="editor-field">
#(Html.Kendo().DropDownList()
.Name("HOME_CITY_ID")
.HtmlAttributes(new { style = "width:200px" })
.OptionLabel("Select Home City")
.DataTextField("Text")
.DataValueField("Value")
.DataSource(source => source.Read(read => read.Action("GetCityByCountryJSON", "Employee")
.Data("getCountryId"))
.ServerFiltering(true))
.AutoBind(false)
.CascadeFrom("HOME_COUNTRY_ID")
)
</div>
</div>
</li>
</ul>
</div>
Javacript Code which assigning Values in Page load means After Dom ready:
function applyvaluetoControls() {
setdefaultvaluestoControls();
$.each(activeLayout.controls, function(index) {
$.each(options.emementArray, function(innerindex) {
if (options.emementArray[innerindex].attr("id") === activeLayout.controls[index]["id"]) {
if ((options.emementArray[innerindex]).is(':checkbox')) {
options.emementArray[innerindex].prop('checked', activeLayout.controls[index]["value"]);
} else {
if (options.emementArray[innerindex].attr("data-role") !== undefined && options.emementArray[innerindex].attr("data-role") === "dropdownlist") {
// console.log(options.emementArray[innerindex].data('kendoDropDownList'));
if (options.emementArray[innerindex].data('kendoDropDownList').options.cascadeFrom !== "") {
console.log($("#" + options.emementArray[innerindex].data('kendoDropDownList').options.cascadeFrom).val());
options.emementArray[innerindex].data('kendoDropDownList').dataSource.read({ CompanyId: $("#" + options.emementArray[innerindex].data('kendoDropDownList').options.cascadeFrom).val() });
options.emementArray[innerindex].data('kendoDropDownList').value(activeLayout.controls[index]["value"]);
} else {
options.emementArray[innerindex].data('kendoDropDownList').enable();
options.emementArray[innerindex].data('kendoDropDownList').value(activeLayout.controls[index]["value"]);
}
console.log(activeLayout.controls[index]["id"]);
console.log(activeLayout.controls[index]["value"]);
} else {
options.emementArray[innerindex].val(activeLayout.controls[index]["value"]);
}
}
}
});
});
}
Console Log:
HOME_COUNTRY_ID // Parent Control Id
322 // It Setting the value to Parent
// Blank value when I am trying to read the value Parent Control (Country)
City : // Parent triggering Chile but with Blank Value
HOME_CITY_ID // Child
342 // Value
City : 322 // after coming out of loop it's again calling Child with Parent selected value
and It's wiping out intial selected value of child.
Any help will be highly appreciated!!
ChildrenUsing these properties from your example in your child drop down you are instructing the drop down to:
.AutoBind(false) //<--Don't do any data retrieval until asked or clicked
.OptionLabel("Select Home City") //<--If the value is null display this and value will be ""
.CascadeFrom("HOME_COUNTRY_ID") //<--Get my values using the DataValueField from this control and refresh when it changes
I bet by having the cascade on like that you will need a configuration similar to this fictitious scenario:
Parent DropDown Config
.Name("ddlParent")
.OptionLabel((#Model.ForceParentSelect) ? "" : "All Parents")
.Value(Model.ParentID.HasValue ? Model.ParentID.Value.ToString() : "")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetParents", "Parent");
});
})
Child DropDown Config
<script>
function filterChild{ return { ParentID: $("#ddlParent").val() }; }
</script>
.Name("ddlChild")
.OptionLabel((#Model.ForceChildSelect) ? "" : "All Childs")
.Value(Model.ChildID.HasValue ? Model.ChildID.Value.ToString() : "")
.AutoBind(Model.ParentID.HasValue)
.CascadeFrom(Model.ParentID.HasValue ? "" : "ddlParent")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetChildren", "Child")
.Data("filterChild");
})
})

Resources