How to pass refs to a component in alpine js 3? - alpine.js

I created a component named user in alpine js and add an init function to it. Something like this
<div x-data="user()" x-init="init($refs)">
<div x-ref="entry"></div>
</div>
<script type="text/javascript">
function user() {
return {
init: function($refs) {
console.log('run')
}
}
}
</script>
But what is happening, is the init function is called two times. Then, I get to know that functions called init are auto invoked in Alpine v3 (https://alpinejs.dev/directives/init#auto-evaluate-init-method). If you add x-init="init()", init will be called twice. So, I removed the init() from the HTML.
<div x-data="user()">
<div x-ref="entry"></div>
</div>
and it works fine. But, now $refs gives an error saying
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'entry')
How can i pass $refs to the init() function?

Here user() is an Alpine.js component that you can also create via Alpine.data() global method. Inside a component you can access each "magic" property like $refs, but you have to use the this. prefix:
<div x-data="user">
<div x-ref="entry"></div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('user', () => ({
init() {
// Access "entry" ref
console.log(this.$refs.entry)
}
}))
})
</script>

Related

Custom Component in Laravel Spark

I try to add my custom component to my Laravel Spark instance and always get the error:
Property or method "obj" is not defined on the instance but referenced during render..
It all works fine if i only bind a data value (ex. "testkey") without a loop...but if i add the for loop i receive this error...so my code:
app.js (spark)
require('spark-bootstrap');
require('./components/bootstrap');
//my new Component
import OmcListObjects from './components/modules/omc/objectlist.vue';
Vue.component('omc-objectlist', OmcListObjects);
var app = new Vue({
mixins: [require('spark')]
});
my Component (objectlist.vue)
<template>
<div :for="(obj in objlist)" class="property-entry card col- col-md-4 shadow-sm">
</div>
</template>
<script>
export default {
data () {
return {
objlist: [{title: 'test1'}, {title: 'test2'}],
testkey: 'testval'
}
}
}
</script>
I think you mean by :for the v-for directive, directives are always prefixed by v- like v-for one in which the compiler can recognize the obj variable as an temporary element used in the loop, but if you set :for which is recognized as a prop bound to a data or another property.

Pass data from blade to vue component

I'm trying to learn vue and with that I want to integrate it with laravel too..
I simply want to send the user id from blade to vue component so I can perform a put request there.
Let's say I have this in blade:
<example></example>
How can I send Auth::user()->id into this component and use it.
I kept searching for this but couldn't find an answer that will make this clear.
Thanks!
To pass down data to your components you can use props. Find more info about props over here. This is also a good source for defining those props.
You can do something like:
<example :userId="{{ Auth::user()->id }}"></example>
OR
<example v-bind:userId="{{ Auth::user()->id }}"></example>
And then in your Example.vue file you have to define your prop. Then you can access it by this.userId.
Like :
<script>
export default {
props: ['userId'],
mounted () {
// Do something useful with the data in the template
console.dir(this.userId)
}
}
</script>
If you are serving files through Laravel
Then here is the trick that you can apply.
In Your app.blade.php
#if(auth()->check())
<script>
window.User = {!! auth()->user() !!}
</script>
#endif
Now you can access User Object which available globally
Hope this helps.
Calling component,
<example :user-id="{{ Auth::user()->id }}"></example>
In component,
<script>
export default {
props: ['userId'],
mounted () {
console.log(userId)
}
}
</script>
Note - When adding value to prop userId you need to use user-id instead of using camel case.
https://laravel.com/docs/8.x/blade#blade-and-javascript-frameworks
Rendering JSON
Sometimes you may pass an array to your view with the intention of rendering it as JSON in order to initialize a JavaScript variable. For example:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
However, instead of manually calling json_encode, you may use the #json Blade directive. The #json directive accepts the same arguments as PHP's json_encode function. By default, the #json directive calls the json_encode function with the JSON_HEX_TAG, JSON_HEX_APOS, JSON_HEX_AMP, and JSON_HEX_QUOT flags:
<script>
var app = #json($array);
var app = #json($array, JSON_PRETTY_PRINT);
</script>
Just to add for those who still get error.
For me this <askquestionmodal :product="{{ $item->title }}"></askquestionmodal> still gives error in console and instead showing html page I saw white screen.
[Vue warn]: Error compiling template:
invalid expression: Unexpected identifier in
Coupling to connect 2 rods М14 CF-10
Raw expression: :product="Coupling to connect 2 rods М14 CF-10"
Though in error I can see that $item->title is replaced with its value.
So then I tried to do like that <askquestionmodal :product="'{{ $item->title }}'"></askquestionmodal>
And I have fully working code.
/components/askquestionmodal.vue
<template>
<div class="modal-body">
<p>{{ product }}</p>
</div>
</template>
<script>
export default {
name: "AskQuestionModal",
props: ['product'],
mounted() {
console.log('AskQuestionModal component mounted.')
}
}
</script>

Magento2 Knockout.js contentUpdated not binding observables after ajax.

I have an issue with observables being fired after content is updated via ajax and javascript is re-initialized via .trigger('contentUpdated'). This all works when the scripts are rendered initially on page load but when they are added via ajax I cannot get the observables to update. For demo purposes I've simplified by logic but basically I have a block that gets loaded via ajax for a product collection like so:
myblock.phtml
<div class="wrapper-id-1">
<!-- this is what gets appended via ajax
<div id="product-item-<?php echo $productId;?>">
<span data-bind="text: someObservable()"></span>
...
</div>
<script type="text/x-magento-init">
{
"#product-item-<?php echo $productId;?>": {
"path/to/component":{
"some":"vars"
}
}
</script>
<!--and ajax append ends here -->
</div>
In the component that gets bound to the element I have:
component.js
...
this.someObservable: ko.observable('default value'),
initialize: function () {
var self = this;
this.anotherComponentModel().value.subscribe(function(data){
self.someObservable(data['value']);
},this);
},
...
the ajax that calls and loads the collection:
ajaxComponent.js
$.ajax({
url: 'route/to/controller?cat=' + categoryId,
success: function (data) {
$('.wrapper-id-' + categoryId).empty().append('<h2>' + categoryTitle + '</h2>' + data).show().trigger('contentUpdated');
}
})
...
I see that the component(component.js) gets initialized when contentUpdated is triggered and it has all of the correct data that is needed. However the observables to not fire and the data is not updated to the DOM. This an issue with scope? Or to I need to re-initialize the observables? I tried doing this via not binding directly to the component ie:
<script type="text/x-magento-init">
{
// components initialized without binding to an element
"*": {
"<js_component3>": ...
}
}
</script>
but it achieves the same result.
What am I missing here.

CanJS on click outside of component effecting component

I would like to add an event listener in in <some-component> that reacts to the button.
<some-component></some-component>
<button class="click">click here</button>
I am sure this is really simple. I am very new to CanJS and working on it.
<can-component tag="some-component">
<style type="less">
<!-- stuff -->
</style>
<template>
<!-- stuff -->
</template>
<script type="view-model">
import $ from 'jquery';
import Map from 'can/map/';
import 'can/map/define/';
export default Map.extend({
define: {
message: {
value: 'This is the side-panels component'
}
}
});
</script>
</can-component>
I tried adding a $('body').on('click', '.click', function() {}); to the component and it didn't seem to work. Been reading a lot of documentation, but I am still missing some fundamental understanding.
UPDATE
I tried this:
<some-component-main>
<some-component></some-component>
<button class="click">click here</button>
</some-component-main>
with the event listener in some-component-main
events: {
".click click": function(){
console.log("here I am");
}
},
But that also didn't work.
<some-component-main>
<some-component></some-component>
<button class="click">click here</button>
</some-component-main>
with the event listener in some-component-main
events: {
".click click": function(){
console.log("here I am");
}
},
This did work once I realized that components ending with a number causes other issues that was preventing it.
You can make things inside your component available to the parent scope using the {^property-name} or {^#method-name} syntax. Read about it here: https://canjs.com/docs/can.view.bindings.toParent.html
Here's a fiddle: http://jsbin.com/badukipogu/1/edit?html,js,output
In the following example, <my-compontent> implements a doSomething method and we the button to call that method when clicked. We expose the method as "doFooBar".
<my-component {^#do-something}="doFooBar" />
<button ($click)="doFooBar">Button</button>
and the code:
can.Component.extend({
tag: "my-component",
template: can.view('my-component-template'),
viewModel: can.Map.extend({
doSomething: function () {
alert('We did something');
}
})
});
But why does the example use ^#do-something="..." instead of ^#doSomething="..."??
DOM node attributes are case insensitive, so there's no way to tell the difference between doSomething="", DoSomEthiNg="", or DOSOMETHING="" - all three are equivalent. CanJS is following the way browsers work by converting attributes with dashes to camelCase and vice versa.
Consider native data attributes - if you do something like <div data-my-foo="my bar">, then the value is accessible via JavaScript by doing [div].dataset.myFoo (notice the camelCasing). The same applies to css properties where css uses "background-color" but javascript uses backgroundColor. CanJS is following this convention.

jquery .empty() issue: doesn't work in plugin

I have to write a simple plugin for ajax load.
Page code. (result by razor)
<a ajaxLoad="page" href="/Brand">Brand List</a>
<div id="plc1">
some content
</div>
<script type="text/javascript">
$(function () {
$("#plc1").ajaxPageLoad();
});
</script>
In js code.
jQuery.fn.ajaxPageLoad =
function () {
$('a[ajaxLoad*="page"]').click(function () {
$(this).empty();
$(this).load(this.href);
return false;
});
}
in page without this implementation empty() work properly but plug-in there is no effect.
what is wrong?
Thanks.
Seems that you're hoping this will refer to both the div and the a at the same time.
If I understand your code, you want to empty the element on which your plugin was called when the <a> element is clicked.
Currently, in the click() handler, this is the <a> element. You need to retain a reference to the <div> against which your plugin was called outside the handler.
jQuery.fn.ajaxPageLoad = function() {
// reference the <div> container (or whatever it ends up being)
var container = this;
$('a[ajaxLoad*="page"]').click(function() {
container.empty(); // empty the container
container.load( this.href ); // load into the container from the href
return false; // of the <a> that was clicked
});
};
$(function() {
$("#plc1").ajaxPageLoad();
});

Resources