While reviewing some code in grails, I noticed that at a certain point specific code is reused in every controller and passed to the views to render a view section which can be normally be part of the main layout. The only problem is how to pass those values to the main layout from another point outside the specific controllers.
Any best-practice in dealing with similar cases of passing variables to the main layout?
You use a Filter for this:
For example:
class MyFilters {
def filters = {
all(controller:'*', action:'*') {
before = { }
after = { Map model ->
// add your common data here
model.commonData = ...
return true
}
afterView = { Exception e -> }
}
}
}
The after closure gets called after the controller execution but before the view is rendered. You can also be more specific on the filter configuration (all(controller:'*', action:'*')). For example you can to exclude certain controllers or something like that.
Related
This seems like it should have a simple answer, and probably does, but it's proving harder to find than I expected. As a specific example, let's say that I'm programming a chess game.
It seems like this is something I should be able to do just using CoreGraphics. It seems like using OpenGL or SpriteKit shouldn't be necessary.
I want to have a Board class that models the state of the board. Where should I declare my Board object? My impression is that it should be in the ViewController.
I want to have a view (actually a subview) that displays the current state of the board (by overloading drawRect). It should do this at the beginning, and should be updated when players make moves. How does the view access the data model to display the board state? Does giving the view a reference to the data violate MVC? If not, how would the reference be passed to the view? (I seem to just find lots of links about passing data between two ViewControllers.)
Should it instead be the ViewController "pushing" the data to the view whenever it needs to be drawn? My understanding, though, is that drawRect should not be called directly, and that instead setNeedsDisplay should be called, which will indirectly result in drawRect being called. This being the case, it's hard to see how the data would be passed.
Your code; your design decision. Nothing to comment on here.
You should have your model declaration in ViewController. True. That is how MVC works.
Having a reference of the data in a UIView DOES break MVC. Your view instance will not be independent anymore. Decoupling view and model is one of the main points of MVC and you are probably breaking it with this design.
What can you do about it?
Extending #Paulw11's comment, in your view controller you can declare a method that looks something like this :
func movePiece(somePiece : Piece, toSquare : Square) {
let pieceID = somePiece.id //I am just assuming the models structures
let pieceImageView = self.pieceImageViewFromID(id) //Assume that this method returns the reference of the image view. Assume that I am just working UIKit here.
let fromPoint : CGPoint = somePiece.origin
let toPoint : CGPoint = toSquare.coordinates
self.animateView(pieceImageView, fromPoint:fromPoint, toPoint:toPoint)
}
Note that in this design, the view is not holding any model references; the view controller will take care of setting its state and bring upon relevant animations.
If you are overriding drawRect:, then yes, for it be called, you should call setNeedsDisplay to update the changes. The view controller might call or you can add property observers to redraw itself based on a property change. One example for this could be:
class SillyView : UIView {
var drawPonies : Bool = false {
didSet {
if oldValue != drawPonies {
self.setNeedsDisplay()
}
}
}
override func drawRect(rect: CGRect) {
if drawPonies {
self.drawGoodLookingPony()
} else {
self.drawSomeOtherAnimal()
}
}
func drawGoodLookingPony() {
//Draw a good looking pony here
}
func drawSomeOtherAnimal() {
//Draw Something else
}
}
If your view controller decides to draw ponies all you have to do is, get the reference of the SillyView and set drawPonies to true.
self.sillyView.drawPonies = true
You are not passing your data model here, but important pieces of configuration information that will help the view redraw itself.
I have a customView. It has some condition like this(only example):
customView(viewsNeed: Bool)
...
if viewsNeeded {
self.addSubView(newView)
self.addSubView(newView2)
} else {
self.addSubView(newView3)
self.addSubView(newView4)
self.addSubView(newView5)
}
and then I can add this View to in my ViewController:
self.view.addSubView(customView(viewsNeeded))
What I want to know is what should I do? Write conditions like this, or make separate Views for this purpose. Something like:
View1
...
self.addSubView(newView)
self.addSubView(newView2)
View2
...
self.addSubView(newView3)
self.addSubView(newView4)
self.addSubView(newView5)
And add one of them in the ViewController:
if viewsNeeded {
self.view.addSubView(view1)
} else {
self.view.addSubView(view2)
}
What kind of View creating is better in what situation, and how should i decide this kind of things? I need some really wide answers with explanations if it's real.
If a view can have different states, you would take care of those different states within the view that has a certain responsibility. The UINavigationBar is a good example. It has a clear purpose, giving navigational context to the user, but it's state (and context) can make it appear different.
func pushNavigationItem(...) {
...
if self.items.count > 1 {
// show backButton
} else {
// hide backButton
}
}
If the different views don't work together for a shared purpose, I wouldn't group them together in a container-view, but instead add them separately, dependent on your needs in a ViewController.
override func viewDidLoad() {
if userDidBuyContent() {
// add view with bought content
} else {
// add view to buy content
}
}
And in general it's a good practice to keep your view-hierachy as flat as possible. The less views you introduce, the better your app will perform. The decision is ultimately up to you, but just keep in mind what the purpose of a view is and whether subviews contribute to that purpose or are really serving some other purpose.
there is no conceptual difference between options you've described. from MVC pattern perspective they are both slightly wrong. you don't have to add views manually, view must create its structure itself.
It seems like this should be very easy, but I'm missing something. I have a custom Element:
public class PostSummaryElement:StyledMultilineElement,IElementSizing
When the element's accessory is clicked on, I want to push a view onto the stack. I.e. something like this:
this.AccessoryTapped += () => {
Console.WriteLine ("Tapped");
if (MyParent != null) {
MyParent.PresentViewController(new MyDemoController("Details"),false,null);
}
};
Where MyDemoController's gui is created with monotouch.dialog.
I'm just trying to break up the gui into Views and Controlls, where a control can push a view onto the stack, wiat for something to happen, and then the user navigates back to the previous view wich contains the control.
Any thought?
Thanks.
I'd recommend you not to hardcode behavior in AccessoryTapped method, because the day when you'll want to use that component in another place of your project is very close. And probably in nearest future you'll need some another behavior or for example it will be another project without MyDemoController at all.
So I propose you to create the following property:
public Action accessoryTapped;
in your element and its view, and then modify your AccessoryTapped is that way:
this.AccessoryTapped += () => {
Console.WriteLine ("Tapped");
if (accessoryTapped != null) {
accessoryTapped();
}
};
So you'll need to create PostSummaryElement objects in following way:
var myElement = new PostSummaryElement() {
accessoryTapped = someFunction,
}
...
void someFunction()
{
NavigationController.PushViewController (new MyDemoController("Details"), true);
}
It's really easy to return a different View from the Controller:
return View("../Home/Info");
However, I need a model in the Info view. I have a lot of stuff going on in the Info() action result method. I can just copy it and have something like this:
var infoModel = new InfoModel {
// ... a lot of copied code here
}
return View("../Home/Info", infoModel);
But that is not reasonable.
Of course I can just redirect:
return RedirecToAction("Info");
But this way the URL will change. I don't want to change the URL. That's very important.
You can call right to another action from within an action, like this:
public ActionResult MyAction(){
if(somethingOrAnother){
return MyOtherAction();
}
return View();
}
//"WhichEverViewYouNeed" is required here since you are returning this view from another action
//if you don't specify it, it would return the original action's view
public ActionResult MyOtherAction(){
return View("WhichEverViewYouNeed", new InfoModel{...});
}
It looks like you want to invoke an action from a different controller. I'd suggest that you might want to simply render a view that renders that action using Html.Action() instead of trying to tie the two together in the controller. If that's unreasonable then you might want to create a base controller that both controllers can derive from and put the shared code to generate the model in base controller. Reuse the view as needed.
public ActionResult Foo()
{
return View();
}
Foo View
#Html.Action( "info", "home" )
Why not just invoke the method of the action?
I have a problem with my data binding from View to Controller
I have an object that includes 3 other objects that I want to pass from controller to view
ModelView { Product, PagingInfo, Filter }
So this is how it looks like from controller
public ViewResult List(ModelView mv, int page = 1) {
var viewModel = new ModelView() { ... }
return View(viewModel);
and the View looks like this
Inherit = "viewModel"
using(html.BeginForm()) { Html.EditorFor(x => x.Filter.Name) ... }
Questions are:
Am i right for binding ModelView as a parameter in the controller? or should I bind Filter instead?
When I used a debugger, it seemed like whatever I put in textfield (Html.editorfor) doesn't get binded back the controller
Please Help
Thanks
My bad! It was working the entire time, I totally forgot that I put the default value as null in the routing system.
Sorry for the trouble :(