I use Butterknife for binding views inside Conductor's controllers. Should I unbind these views inside a Controller's onDestroyView(), as recommended for Android Fragments?
Yes, that's how they do it in their sample.
#Override
protected void onDestroyView(#NonNull View view) {
super.onDestroyView(view);
unbinder.unbind();
unbinder = null;
}
Related
The project (WPF) has these folders:
Views
ViewModels
SubViews
SubViewModels
How to get the Prism's ViewModelLocator working with them (Views> ViewModels & SubViews> SubViewModels), the solution I found only works with one convention:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
var viewName = viewType.FullName.Replace(".ViewModels.", ".CustomNamespace.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = $"{viewName}ViewModel, {viewAssemblyName}";
return Type.GetType(viewModelName);
});
}
You could opt for registration of the pairs (not as bad as it sounds because you have to register for navigation anyway).
Alternatively, you implement both conventions one after the other - take the view's name, subtract "Views" and add "ViewModels" and try to get the view model's type. If that fails, subtract "SubViews" and add "SubViewModels" and try again. You could even check for cross combinations (e.g. "SubViews.ViewA" and "ViewModels.ViewAViewModel")...
I solved it by checking the viewType and based on it, I return the appropriate ViewModel type:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
string prefix;
if (viewType.FullName.Contains(".SubViews."))
{
prefix = viewType.FullName.Replace(".SubViews.", ".SubViewModels.");
}
else
{
prefix = viewType.FullName.Replace(".Views.", ".ViewModels.");
}
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = $"{prefix}ViewModel, {viewAssemblyName}";
return Type.GetType(viewModelName);
});
}
It seems like when combining TS and angular, everything i have on a controller is exposed to the view. In my case, myPrivate will appear on $ctrl.
class MyController extends BaseController implements SomeInterface {
private myPrivate: string = 'myPrivateString';
}
Is there any workaround around this issue?
It's pretty obvious why when you look at the generated javascript.
var MyController = (function (_super) {
__extends(MyController, _super);
function MyController() {
_super.apply(this, arguments);
this.myPrivate = 'myPrivateString';
}
return MyController;
}(BaseController));
Your private property ends up as any other property on your controller.
You can see the full transpilation here.
A solution would be to have a parameterized base controller able to set something like a view model for the view to use, instead of the regular $ctrl.
It would look something like this:
class BaseController<T> {
protected scope;
protected viewModel: T;
constructor(scope: any, modelType: { new (): T; }) {
this.scope = scope;
this.viewModel = new modelType();
this.scope["viewModel"] = this.viewModel;
}
}
class MyParticularViewModel {
public somethingForTheView: string;
}
class MyController extends BaseController<MyParticularViewModel> implements SomeInterface {
private myPrivate: string = 'myPrivateString';
constructor(scope) {
super(scope, MyParticularViewModel);
}
}
In the view you can then use the viewModel property to access the needed properties.
I have used this in a project in practice and it worked out pretty well. You can see a starter template that I used here for more info.
I created a little test typescript/angularjs website.
I have this module with a static variable which I want to bind to the html-document so that I can see the current logged-in user.
module mytest.constants {
export class CurrentSession {
public static CURRENT_USER: string = "Tobi";
}
}
the problem is:
the current scope is a controller which is separated from my CurrentSession-class.
I would like to do something like
<div class="label" style="color:green">
Logged in as: {{mytest.constants.CurrentSession.CURRENT_USER}}
</div>
Another way I could do is to add a class-member to the controller and set it in constructor like:
this.CurrentUSer = mytest.constants.CurrentSession.CURRENT_USER
but I would prefer to bind the static-variable directly to the html-file.
is this possible?
You can't bind static properties like this, Angular just doesn't check those in its digest cycle. You can however create a scope/this reference to the class itself. Something like this:
module mytest.constants {
export class CurrentSession {
public static CURRENT_USER: string = "Tobi";
CurrentSession = CurrentSession;
}
}
So basically it will create own property like this.CurrentSession = CurrentSession.
Then in template (assuming you are using controllerAs syntax with session refer to controller instance) you would be able to bind CURRENT_USER as
<div>{{session.CurrentSession.CURRENT_USER}}</div>
thank you dfsq for your quick answer.
I finally found another solution.
I first set a variable called "CurrentSession" in my app.ts and set the value to a new Instance of my Object
angular.module("app", [])
.constant("CurrentSession", new mytest.constants.CurrentSession())
then I can just inject this Constant in my controller like this:
export class MainCtrl {
public static $inject = ["$state", "$http", "CurrentSession"];
constructor($state, $http, private CurrentSession) {
...
}
the good thing here is, that I can use the "private CurrentSession" which will automatically set the a member-variable "CurrentSession".
The html looks like this:
<button ng-click="main.logOut()">Log out {{main.CurrentSession.CURRENT_USER.Name}}</button>
This might be a naive question since I am new to cakephp.
I am working on a project where there are many layouts, helpers and elements. Because this is a mutli-language site, I am intercepting the final rendered output to do some language conversion(so each visitor only sees his native language on the site including user input).
I've managed to convert most of the layouts and helpers by adding code in two places: AppController's afterFilter() and AppHeler's afterRender() function. But I can't figure out a centralized way to handle the elements and there are dozens of them.
So here are my questions: do all elements in cakephp have a common ancestor class? If so, does this ancestor class have callback functions like afterRender()?
Many thanks!
I'm not sure such a callback exists specific for 'elements', but looking at the source code, View::element() renders an element using the same _render() method as the View itself, and should trigger beforeRender() and afterRender()
Creating a custom View Class and Custom Callbacks
You may use a custom 'View' class and override the element() method, for example to have your own 'custom' callbacks being triggered in helpers
Something like this;
app/view/app_view.php
class AppViewView extends View {
/**
* custom 'element()' method, triggers custom
* before/aferRenderElement callbacks on all loaded helpers
*/
public function element($name, $params = array(), $loadHelpers = false)
{
$this->_triggerHelpers('beforeRenderElement');
$output = parent::element($name, $params, $loadHelpers);
$this->_triggerHelpers('afterRenderElement');
}
/**
* Names of custom callbacks
*/
protected $_customCallBacks = array(
'beforeRenderElement',
'afterRenderElement',
);
function _triggerHelpers($callback)
{
if (!in_array($callback, $this->_customCallbacks)) {
// it's a standard callback, let the parent class handle it
return parent::_triggerHelpers($callback);
}
if (empty($this->loaded)) {
return false;
}
$helpers = array_keys($this->loaded);
foreach ($helpers as $helperName) {
$helper =& $this->loaded[$helperName];
if (is_object($helper)) {
if (
is_subclass_of($helper, 'Helper')
&& method_exists($helper, $callback)
) {
$helper->{$callback}();
}
}
}
}
}
Then, in your AppController specify the 'view' class to use;
class AppController extends Controller {
public $view = 'AppView';
}
I need to create model which holds css properties for one element. My model looks like this:
StyleModel = Backbone.Model.extend( {
defaults : {
productName : '',
styles:{
'font-weight':'normal',
'font-style':'normal',
'text-decoration':'none',
'visibility':'visible'
'color':'blue',
'border-width':'1px',
'border-color':'white',
'font-color':'white'
}
},
initialize : function(property, values) {}
...}
How to notify view when I change value of some property or delete from list ?
(For example when user set border-width to 3px or when delete font-weight. Or is it better solution not to hold properties in hash and to set that every property be element in model ?)
Backbone won't recognize settings in your hash, on it's own. But you can create methods that handle this for you:
Backbone.Model.extend({
setCss: function(key, value){
var css = this.get("styles");
css[key] = value;
this.trigger("change", this, key, value);
this.trigger("change:css", key, value);
this.trigger("change:css:" + key, value);
}
});
Then you would call model.setCss("background-color", "#ff0faf") and it would trigger the three "change" events for you to bind to in your views.
In the view, you can bind the change events in the initializer, and have jQuery apply all of the styles to the DOM element that the view controls:
Backbone.View.extend({
initialize: function(){
this.model.on("change:css", this.setCss, this);
},
setCss: function(){
var css = this.model.get("styles");
this.$el.setCss(css);
}
});
You might need to clear existing css before applying the new set, to make sure you get rid of anything that was removed. More likely, though, you'll want to have a deleteCss method on the model, have it raise a css:deleted event from the model, and have the view respond to that event by removing the css attribute in question.
Use the change event that is available on the model http://documentcloud.github.com/backbone/#Model-change
Have your view bind to the event. http://documentcloud.github.com/backbone/#Events-on