Livewire #entangle - laravel-livewire

I am new to Livewire and have a question.
In my Livewire blade I have a JS function that calls a component method which updates $this->questions.
function QuestionBatchRequest() {
Livewire.emit('moreQuestions');
this.dispatchEvent(new Event('moreQuestions'));
}
This works as I can output to an input box and see the data change.
I then have
document.addEventListener('livewire:load', () => {
window.livewire.on('QuestionBatchListener', () => {
let data = #entangle('questions');
myUnityInstance.SendMessage("JS-Unity", "InjectQuizData", data);
});
});
How can I get the updated $this->questions to my JS variable directly?
This is in my method in my component
$this->questions = json_encode($data, JSON_UNESCAPED_SLASHES);
$this->emit('QuestionBatchListener');
This function is called from my Unity game on the same page. The initial set of questions is received on page load and works fine.

OK, so I changed
let data = #entangle('questions');
with
let data = #this.get('questions');
...

Related

Attach onAnimationComplete for Chart.js after creation

I'm creating a Blazor component as a facade of ChartJs. I opened a question on the Microsoft forum to understand how I can call a JavaScript function for the component.
My problem is how to add some events to the chart before the creation. So, from the Blazor page, I pass a model called config that contains all the configuration for the chart.
Then, I create the chart in JavaScript like
var ctx = document.getElementById(id).getContext('2d');
var chart = new Chart(ctx, eval(config));
Now, I want to add an event and send to Blazor an event. For example, I added the following code for the event onHover and it works.
chart.options.onHover = function () {
DotNet.invokeMethodAsync('PSC.Blazor.Components.Chartjs', 'ChartHover');
}
With onClick is working too.
chart.options.onClick = function (event, array) {
var rtn = 0;
if (array !== undefined && array.length > 0)
rtn = array[0].index;
DotNet.invokeMethodAsync('PSC.Blazor.Components.Chartjs', 'ChartClick', rtn);
};
Now, I want to do the same with onAnimationComplete.
chart.onAnimationComplete = window["AnimationComplete1"];
In this cases, the function is not being called. Some if I write this code
chart.onAnimationComplete = function () {
console.log('onAnimationComplete animation completed');
};
How can I fix the code?
there is no onAnimationComplete event you can configure directly on the chart. This has to be done in the options like you did for the onHover and the name has to be different:
chart.options.animation.onComplete = function () {}

(Opencart) How to load a controller in a function and not break ajax with that function?

Is there a way to load controller (product/category) within some contained space, so that the ajax to the custom function within that controller doesn't break?
I'm basically loading (from ajax) a custom function which is inside a core contoller product/category. In this function I need to reload the the product/category controller to get new product list based on ajax data I sent to the function, to then return it as a response to the original ajax.
When I try to do
$this->load->controller('product/category')
it beaks the ajax I set up with the function and in the console I see 404.
I tried using
$foo = $this->load->controller('product/category')
and it works, but I need to also execute
$this->load->view('product/category')
and I don't know how to do it without breaking ajax.
Basically I did what I essentially wanted (see the middle of my question, namely the part about the ultimate need to refresh my product list using ajax) the other way (after reading up How to get products in JSON format from OpenCart using phonegap/jQueryMobile): from the ajax I called the products/category directly and not my custom function inside of the product/category controller as in the question, and when I got reponse back containing the html output of the view, I reloaded a div I made in the product/category.twig with that html using jQuery. The ajax was
$(document).ready(function(){
$.ajax({
url: 'index.php?route=product/category&path=18&json',
type: 'get',
beforeSend: function() {
},
complete: function() {
},
success: function(data) {
console.log('success');
if (data.length > 0) {
console.log(data);
$('#mydiv').html(data);
}
}
});
});
and the code I added to product/category.php was
if(isset($this->request->get['json'])) {
$this->response->setOutput($this->load->view('product/view_for_mydiv', $data));
} else {
$this->response->setOutput($this->load->view('product/category', $data));
}
As you may notice I added a div inside product/category.twig called mydiv, which I placed exactly where I wanted the html to go and then I created a twig called view_for_mydiv.twig inside default/product/category/ folder, the html of which the product/category controller would send back instead of its general twig when it saw that an ajax call had been made to it. The #mydiv div located inside category.twig wraps the html that is the same html that gets produced when view_for_mydiv.twig is used to render product/category.

How to test VueRouter's beforeRouteEnter using '#vue/test-utils'?

I'm trying to test my 'Container' component which handles a forms logic. It is using vue-router and the vuex store to dispatch actions to get a forms details.
I have the following unit code which isn't working as intended:
it('On route enter, it should dispatch an action to fetch form details', () => {
const getFormDetails = sinon.stub();
const store = new Vuex.Store({
actions: { getFormDetails }
});
const wrapper = shallowMount(MyComponent, { store });
wrapper.vm.$options.beforeRouteEnter[0]();
expect(getFormDetails.called).to.be.true;
});
With the following component (stripped of everything because I don't think its relevant (hopefully):
export default {
async beforeRouteEnter(to, from, next) {
await store.dispatch('getFormDetails');
next();
}
};
I get the following assertion error:
AssertionError: expected false to be true
I'm guessing it is because I am not mounting the router in my test along with a localVue. I tried following the steps but I couldn't seem to get it to invoke the beforeRouteEnter.
Ideally, I would love to inject the router with a starting path and have different tests on route changes. For my use case, I would like to inject different props/dispatch different actions based on the component based on the path of the router.
I'm very new to Vue, so apologies if I'm missing something super obvious and thank you in advance for any help! 🙇🏽
See this doc: https://lmiller1990.github.io/vue-testing-handbook/vue-router.html#component-guards
Based on the doc, your test should look like this:
it('On route enter, it should dispatch an action to fetch form details', async () => {
const getFormDetails = sinon.stub();
const store = new Vuex.Store({
actions: { getFormDetails }
});
const wrapper = shallowMount(MyComponent, { store });
const next = sinon.stub()
MyComponent.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next)
await wrapper.vm.$nextTick()
expect(getFormDetails.called).to.be.true;
expect(next.called).to.be.true
});
A common pattern with beforeRouteEnter is to call methods directly at the instantiated vm instance. The documentation states:
The beforeRouteEnter guard does NOT have access to this, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
However, you can access the instance by passing a callback to next. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument:
beforeRouteEnter (to, from, next) {
next(vm => {
// access to component instance via `vm`
})
}
This is why simply creating a stub or mock callback of next does not work in this case. I solved the problem by using the following parameter for next:
// mount the component
const wrapper = mount(Component, {});
// call the navigation guard manually
Component.beforeRouteEnter.call(wrapper.vm, undefined, undefined, (c) => c(wrapper.vm));
// await
await wrapper.vm.$nextTick();

Ionic2 templateUrl display before data loaded

I have an Ionic 2 Component. It renders a html page. In the constructor, it fetches data in a promise. The html uses the data (personModel) and displays the values.
My problem is the html wants to render before the promise has completed getting the data, resulting in an error.
TypeError: self.context.personModel is undefined
How do I make sure the html waits for the data to load before it tries to render?
Thanks
html
<h2>{{personModel.firstName}} {{personModel.lastName}}</h2>
ts
#Component({
templateUrl: 'build/pages/person/person.html',
})
constructor() {
// promise that loads data
this.utilityService.getLoggedInPerson().then((data: string) => {
this.personModel = JSON.parse(data);
}
}
personModel: any[];
constructor() {
this.personModel = [];
// your call
}
Try to initialize the object.
EDIT: answered together :P
If your service can return an Observable, the async pipe might help.
https://angular.io/docs/ts/latest/guide/pipes.html
You need to define the member variable straight away, and then assign the data to it, i.e:
personModel: any;
constructor() {
// promise that loads data
this.utilityService.getLoggedInPerson().then((data: string) => {
this.personModel = JSON.parse(data);
}
}

Angular2 Component: Testing form input value change

I have a text input and i'm listening for the changes.
mycomponent.ts
ngOnInit() {
this.searchInput = new Control();
this.searchInput.valueChanges
.distinctUntilChanged()
.subscribe(newValue => this.search(newValue))
}
search(query) {
// do something to search
}
mycomponent.html
<search-box>
<input type="text" [ngFormControl]="searchInput" >
</search-box>
Running the application everything works fine, but i want to unit-test it.
So here's what i tried
mycomponent.spec.ts
beforeEach(done => {
createComponent().then(fix => {
cmpFixture = fix
mockResponse()
instance = cmpFixture.componentInstance
cmpFixture.detectChanges();
done();
})
})
describe('on searching on the list', () => {
let compiled, input
beforeEach(() => {
cmpFixture.detectChanges();
compiled = cmpFixture.debugElement.nativeElement;
spyOn(instance, 'search').and.callThrough()
input = compiled.querySelector('search-box > input')
input.value = 'fake-search-query'
cmpFixture.detectChanges();
})
it('should call the .search() method', () => {
expect(instance.search).toHaveBeenCalled()
})
})
Test fails as the .search() method is not called.
I guess i have to set the value in another way to have the test realize of the change but i really don't know how.
Anyone has ideas?
It might be a little bit late, but it seems that your code is not dispatching input event after setting input element value:
// ...
input.value = 'fake-search-query';
input.dispatchEvent(new Event('input'));
cmpFixture.detectChanges();
// ...
Updating input html field from within an Angular 2 test
Triggering the value change of FormControl is as simple as:
cmpFixture.debugElement.componentInstance.searchInput.setValue(newValue);
Custom component with #input, subscriptions, two way data binding
If you got a custom component you would need further changes in your application to be able to successfully unit test your application
have a look at the gist here this will give you some idea
https://gist.github.com/AikoPath/050ad0ffb91d628d4b10ef81736af386/raw/846c7bcfc54be8cce78eba8d12015bf749b91eee/#ViewChild(ComponentUnderTestComponent).js
More over complete reading over here carefully otherwise you can easily get confused again -
https://betterprogramming.pub/testing-angular-components-with-input-3bd6c07cfaf6