I would like to understand what is the difference between mount and render methods in Livewire components, since I have seen examples where both are used to define initial state of variables. For instance, when you instantiate a variable with records from the model, ¿which is the right place to load the data using the ORM syntax?
The mount() method is what's called a "lifecycle-hook". There are a few more of these kind of methods in Livewire, which are outlined in the official documentation - https://laravel-livewire.com/docs/2.x/lifecycle-hooks - while the render() is the final method that's called to render the actual view.
The mount() method is the construct to the component. This is where you pass in the data that the component needs. This method is only called once, on the initialization of the component, which means its also typically where you set up initial values that aren't constants.
However, since public properties of a Livewire component can only be collections, the instance of a model, arrays or native PHP types like string and integer, you can't pass more "advanced" types that relies on a state - for example like the pagination of a query of models.
That is why you would sometimes need to pass data to the component via the render() method, like you would when returning data in a normal Laravel controller. Another reason to pass data here is that the data is not exposed in JavaScript, like the public properties of the component is.
The render() method is called at the end of every lifecycle request, but before the component dehydrates. Official documentation has more detailed information https://laravel-livewire.com/docs/2.x/rendering-components#render-method - the data defined here isn't a property of the class, and thereby not accessible in the other methods in the component.
So to answer your question, it depends on what type of data you are passing, if the data should be accessible in the other methods in the class or if it's sensitive such that it shouldn't be visible in the JavaScript object attached to the component.
mount method is like any constructor and you can use it in multiple cases, in other you don't need it. For example, if you have a nested component into a full page component, but initially the nested properties are null or require any definition, you define it there. Is used also for the route model binding definitions but you need be clear that any definition you declare here will not be updated, hydrated or suffer any changes after you initialize the component. Mostly, this is the difference with the render method.
Related
I have a Laravel controller with a 'store' method. i.e. store(Request $request).
For the view I want to embed a livewire component and then utilize the existing controller 'store' method (behind the scenes) from a livewire component method. Is this possible? Currently, I'm running into the problem that there is no Request object since I'm no longer making the call from the existing route/view (i.e. POST /orders).
public function oms_order()
// this method provides the POST /orders leveraging
// Controllers\OrderController#store
{
$this->refId = app('App\Http\Controllers\OrderController')->store($this->jsonOrder);
}
Argument 1 passed to App\Http\Controllers\OrderController::store() must be an instance of Illuminate\Http\Request, null given, called in D:\xampp\htdocs\jade\livewire_hpp\app\Http\Livewire\Oms.php on line 26
I can remove the 'Request $request' from the store method but then that breaks the standard Laravel POST /orders route.
I was hoping to use the existing laravel app as the backend/API and add the livewire bit for a demo. Any advice is welcome.
I don't think it is a best practice to call a controller function from another class. If you want reusability, I suggest refactoring code by creating a separate class with a store method which accepts only required attributes and saves a record.
Then you can use this method in multiple places. It is easier to test also.
It is difficult to give an exact answer before seeing your store method.
It seems like I can declare computed properties in the model and in the controller. I'm getting to the point where I'm not sure which one to look in for a given property.
What dictates whether a property should be placed in the controller vs the model?
Typically, place in the controller if the property is presentational in nature (e.g. display, labels, formatting), and place in the model if the property is inherent to the record itself (e.g. calculations, associations)
In practical terms though:
Model if the property
needs to be accessed by other models, since models don't have access to their controllers
needs to be accessed in routes before controllers are setup
needs to persist across controllers (e.g. order.subtotal is used in OrderNewController and OrderController)
Controller if the property
only needs to be accessed by the view or template
only needs to be accessed by other controllers
You can probably go with putting most properties in the controller, until you run into situations where you need to access the property from other models, or if you find yourself writing {{controllers.modelName.property}} too many times.
Model Binding creates a new object which it maps the form fields to. It usually requires an empty contructor for this object. However what if you wished to pass a parameter to the ctor for it to say retrieve a record for the object.
I have a feeling that one needs to write a custom model builder which I am reluctant to do.
Is this possible?
Thanks.
I'm using ASP.NET MVC3 and i'm wondering that the default modelbinder binds to public properties but not to public fields.
Normally i just define the model classes with properties but sometimes i use some predefined classes which contains some fields. And everytime i have to debug and remember that the modelbinder just don't like fields.
The question: Whats the reason behind it?
but sometimes i use some predefined classes which contains some fields
While I cannot answer your question about the exact reason why the default model binder works only with properties (my guess is that it respects better encapsulation this way and avoids modifying internal state of the object which is what fields represent) I can say that what you call predefined classes should normally be view models. You should always use view models to and from your controller actions. Those view models are classes that are specifically defined to meet the requirements of the given view.
So back to the main point: fields are supposed to be modified only from within the given class. They should not be accessed directly from the outside. They represent and hold internal state of the class. Properties on the other hand is what should be exposed to the outside world. Imagine that in the property getter/setter you had some custom logic. By modifying directly the field this custom logic would be broken and potentially bring the object into an inconsistent state.
Maybe the reason for ignoring fields is to increase performance of the binder. Instead of searching all the Fields and properties. The Model Binder search for Properties only.
Though I think the Model Binder use cache to improve performance.
DefaultModelBinder exposes a public method:
DefaultModelBinder.BindModel, and a number of protected method available for overriding. All of them listed here.
Besides the model, these method refer to properties only, not fields, like
GetModelProperties,
GetFilteredModelProperties,
GetPropertyValue,
OnXYZValidating,
OnXYZValidated,
OnXYZUpdating,
OnXYZUpdated,
GetXYZValue,
where XYZ stands for either Model, or Property/ies, or both, and so on.
As you can see there is no Fields mentioned with these names whatsoever. As Darin explained no direct changes to Model's state are tolerated by the Binder. Hence no Field in its methods.
And also, you may wish to take a look at another important class: ModelBindingContext. An instance of this class gets passed to the BindModel, and subsequently to BindSimpleModel, and BindComplexModel, depending on model type (string, int,... are considered simple, everything else is complex).
So, this context has the following properties:
ModelXYZ, and
PropertyXYZ.
In other words you have no means to reference the fields in your ViewModel unless you do not override these classes and undertake special actions to do so.
But again, beware of fighting the framework, its always easier to follow it instead.
EDIT: The ModelMetadata class holds all the data needed to bind the model. Its code however, shows no sign of fields, field names, etc. Only properties are referenced and accessed. So, even if you try to inherit and override DefaultModelBinder and ModelBinderContext, you still won't be able to access fiellds, nevermind what their access modifier is: public, private, etc.
Hope this explains most of it.
I am developing an application that involves a type hierarchy and started by defining the models for each type via inheritance. When it comes to writing the corresponding controllers I am not sure how to approach the whole thing in a clean way. Should I write only one controller for the base type that is able to handle derived models or should there be one controller for each subtype? How should the view-controller bindings be set up to work with the different controllers?
You might want to check out SproutCore's new experimental polymorphism support: http://groups.google.com/group/sproutcore-dev/browse_thread/thread/b63483ab66333d15
Here's some information on defining sub-classes and overriding properties and methods:
http://wiki.sproutcore.com/w/page/12412971/Runtime-Objects.
From my (limited) use of Sproutcore, I've only been able to bind 1 view to 1 controller.
As such, if you are planning to use a single view (e.g. ListView) to display your data, then I think you will only be able to bind that view to 1 controller. This means the 1 base type that is able to handle derived models seems to be the way to go.
Typically you populate the content of ArrayController instances with the results of App.store.find calls. SC.Store#find can take an SC.Query instance, which typically looks like:
MyApp.myController.set('content') = MyApp.store.find(SC.Query.local(MyApp.MyModel));
This should return all instances of MyApp.MyModel, including any instances of MyApp.MyModel's subclasses.
The first argument to SC.Query.local can either be an SC.Record subclass or a string referring to the subclass. So if you've got some intermediary SC.Record subclasses, you might want to try using them there.
Controllers should just be proxies for objects, when dealing with single instances of your model. In other words, ObjectController can proxy anything. Here is what I mean in code:
You have two objects, Person and Student.
App.Person = SC.Object.extend({
// person stuff here
})
App.Student = App.Person.extend({
// student stuff here, you have have all Person things because you are extending person.
})
You then want to define controllers:
App.personController = SC.ObjectController.create({
contentBinding: 'App.path.to.person'
})
App.studentController = SC.ObjectController.create({
contentBinding: 'App.path.to.student'
})
note that you would only bind the controller's content to something if the person/student is a result of a selection, or some other flow where bindings fire. In other words, if you set the person manually (say from a statechart, as the result of an interaction), you would still define the controller but would do
App.personController.set('content', person);
You set up the controller differently depending on whether the Person is a 'top level' object in your app, or some intermediate object that gets selected. Also, you might only need one controller, you would only have a studentController and a personController if you were acting on a person and a student at the same time. Both are just ObjectControllers, and those can proxy anything.
Finally, in your view you would bind the relevant view element to the controller:
...
nameView: SC.LabelView.design({
layout: {/* props */},
valueBinding: SC.Binding.oneWay('App.personController.name')
})
...
note that the oneway binding is if the name is not going to be changed on the view, if the view can change the name, then just do a normal binding. Also note the path here. I am not binding to
'App.personController.content.name'
Since the personController proxies the object, you bind to the
'namespace.controller.property-on-object-controller-proxies'
If you are putting a lot of business logic in your controller, you are doing it wrong. Controllers should just be for proxying objects (at least ObjectControllers should be). Business logic should be on the models themselves, and decision making logic should be in statecharts.