I'm using Moovweb/Tritium to make a regular desktop site responsive using the Zurb Foundation framework.
As part of this I'd like to create helper functions for enabling Foundation widgets like the the Uranium integrations already in Moovweb. But some of the Foundation components, like the top bar have complex layouts that require more configuration than seems reasonable in a single function call.
For example, enabling the top bar requires something like the following structure:
# Use the foundation topbar
insert_top("nav", class: "top-bar") {
# Create the title area
insert("ul", class: "title-area") {
inject("<li class='name'><h1><a href='/'>Website Name</a></h1></li>")
inject("<li class='toggle-topbar menu-icon'><a href='#'><span>Menu</span></a></li>")
}
# Grab the header links and put them in the top bar
insert("section", class: "top-bar-section") {
insert("ul", class: "left") {
# Move the original header links here
move_here("//span[#class='pagetop']//a"){
wrap("li")
}
}
}
}
What's the right way to abstract this into reusable tritium functions that minimize boilerplate yet allow for flexibility?
In my use case, I handled this by breaking the layout into related functions that get nested into each other. So the original code above becomes:
# enable the foundation top bar
zurb_topbar() {
zurb_topbar_title("Website Name", "Menu", "menu-icon")
zurb_topbar_left() {
move_here("//span[#class='pagetop']//a"){
wrap("li")
}
}
}
where the functions are defined as:
#func XMLNode.zurb_topbar() {
insert_top("nav", class: "top-bar") {
yield()
}
}
#func XMLNode.zurb_topbar_title(Text %name, Text %menu_btn_name, Text %menu_icon) {
insert("ul", class: "title-area") {
inject("<li class='name'><h1><a href='/'>" +%name +"</a></h1></li>")
inject("<li class='toggle-topbar "+%menu_icon+"'><a href='#'><span>"+%menu_btn_name+"</span></a></li>")
}
}
#func XMLNode.zurb_topbar_left() {
insert("section", class: "top-bar-section") {
insert("ul", class: "left") {
yield()
}
}
}
You could imagine a zurb_topbar_right function that could be optionally used for defining the right section of the topbar.
Note for the zurb_topbar_title the hrefs and structure of the <LI>s are hardcoded because it's unlikely you'll typically want to point href anywhere but root. Instead the likely parts that you'll want to customize are the title of the topbar, the title of the Menu button, and the presence of the menu bar icon.
Whereas for zurb_topbar_left the <LI> content is probably going to be filled by the programmer, hence the yield.
The tradeoff appears to be that automatically enforcing that the right combination of functions are used in the right way becomes trickier, i.e.
you have to use both zurb_topbar and zurb_topbar_title
but only one of zurb_topbar_left or zurb_topbar_right
zurb_topbar must include zurb_topbar_left
etc., etc.
Related
So my situation is as follows:
I got a component with a couple of input fields that represent a contact and are filled with data from a service:
#service('contact-data') contact;
Each field stands for one property that is accessed via
{{contact.properties.foo}}
I have the properties saved as a JS object to easily filter out empty fields when using them and I tracked it with #tracked like so:
export default class MyService extends Service {
#tracked properties = {id: 0, foo: "", bar : "abc", };
#action updateProperty(name, value) {
this.properties[name] = value;
}
}
However, the properties do not re-render properly in the component and the textfields do not get updated.
I'd appreciate any help with this! Thanks!
Any time you have a bunch of nested state like that which needs to be tracked, just tracking the top-level object won't cause updates to the internals of that object to propagate out. You need to track the internal properties, or you need to reset the whole object you're tracking.
You have basically two rough options for dealing with updates to those internal properties:
If the object has a well-known shape, extract it into a utility class which uses #tracked on the fields, and instantiate the utility class when you create the service. Then updates to those fields will update.
If the object is really being used like a hash map, then you have two variant options:
Use https://github.com/pzuraq/tracked-built-ins, if you don't need IE11 support
Do a "pure functional update", where you do something like this.properties = { ...this.properties, foo: newValue };
Of these, (1) is pretty much always going to be the cheapest and have the best performance. Doing (2.1) will be a little more expensive, because it requires the use of a Proxy, but not enough that you would normally notice. Doing (2.2) will end up triggering a re-render for every property in the properties used anywhere in the app, even if it didn't change.
In the case you've described, it appears the fields are well known, which means you should reach for that class. The solution might look something like this:
import Service from '#ember/service';
import { action } from '#ember/object';
import { tracked } from '#glimmer/tracking';
class TheProperties {
#tracked id;
#tracked foo;
#tracked bar;
}
export default class MyService extends Service {
properties = new TheProperties();
#action updateProperty(name, value) {
this.properties[name] = value;
}
}
Note that #tracked installs getters and setters in place of plain class properties, so if you need to use this for a JSON payload somewhere or similar, you'll also want to implement toJSON on the utility class:
class TheProperties {
#tracked id;
#tracked foo;
#tracked bar;
toJSON() {
let { id, foo, bar } = this;
return { id, foo, bar };
}
}
There's another add-on that does basically the same thing for Array and Objects as tracked-built-ins.
It's a proxy that basically notifies the root that an update has occurred somewhere. The advantage against tracked-built-ins is that the nesting depth is not limited as it's common for JSON to have deep nesting.
The drawbacks are similar to tracked-built-ins in terms of performance. Use it sparingly and try not to use it in tables with hundreds/thousands of rows as re-rendering is going to be not performant.
As the question states, is there any downside in referencing the service directly in the template as such :
[disabled]="stateService.selectedClient == null || stateService.currentStep == 1"
In my opinion this doesn't seem like good practice and I'd much rather keep a "selectedClient" object in whatever component needs to use it. How can I get the state and store it into local variables, while observing the changes:
example: I want to move from step1 to step2 by changing "currentStep" in the "stateService", however I want the component that keeps "currentStep" ALSO as a local variable to reflect the change in the state?
Is it good practice to reference services in html templates in Angular
2?
I'd generally avoid it. It seems to bring more chaos than good.
Cons:
Coming from OOP background, this approach looks like it breaks the Law of Demeter, but more importantly,
It's no longer MVC, where your controller (Angular2's Component) acts like a mediator between the view and the services.
Like Ced said, what if a call to a service's member is costly and we need to refer to it multiple times in the view?
At the moment my editor of choice (VS Code) does not fully support Angular2 templates; referencing too many things outside of its own Component's scope in a template makes refactoring not fun anymore.
Pros:
Sometimes it looks more elegant (because it saves you 2 lines of code), but trust me, it's not.
How can I get the state and store it into local variables, while
observing the changes
Madhu Ranjan has a good answer to this. I'll just try to make it more complete here for your particular example:
In your StateService, define:
currentStep : Subject<number> = new Subject<number>();
selectedClient: Subject<Client> = new Subject<Client>();
changeStep(nextStep: number){
this.currentStep.next(nextStep);
}
selectClient(client: Client) {
this.selectedClient.next(client);
}
In your Component:
currentStep: number;
constructor(stateService : StateService){
stateService.currentStep.combineLatest(
stateService.selectedClient,
(currStep, client) => {
if (client == null) {
// I'm assuming you are not showing any step here, replace it with your logic
return -1;
}
return currStep;
})
.subscribe(val => {
this.currentStep = val;
});
}
You may try below,
stateService
currentStep : Subject<number> = new Subject<number>();
somestepChangeMethod(){
this.currentStep.next(<set step here to depending on your logic>);
}
component
// use this in template
currentStep: number;
constructor(stateService : stateServiceClass){
stateService.currentStep.subscribe(val => {
this.currentStep = val;
});
}
Hope this helps!!
It is probably not a good idea to expose your subject inside of your state service. Something like this would be better.
StateService
private currentStep: Subject<number> = new Subject<number>();
changeStep(value: number) {
this.currentStep.next(value);
}
get theCurrentStep(): Observable<number> {
this.currentStep.asObservable();
}
Component
currentStep: number;
constructor(private stateService: StateService) {
this.currentStep = this.stateService.theCurrentStep;
}
Template
[disabled]="(currentStep | async) == 1" // Not sure if this part would work
Go provides great build in HTML templating functionality, however for me it is important that I can ensure certain fields will always be available to templates in my application. A good example of this is a title field, which needs to be shown on every HTML page.
Given the following pages:
Home
Register
Contact
I would likely create the following objects for the templating system:
HomePage struct
RegisterPage struct
ContactPage struct
Is there a recommended way to ensure that the structs for each page have certain fields available to them?
Ideally I would implement this through Polymorphism, but that isn't officially supported in Go. The alternative, embedding doesn't appear to have any enforcement, i.e all of the child structs can embed a parent struct but don't have to.
Let me know if I haven't expressed my question clearly enough.
Executing a template does not enforce anything to the parameters, Template.Execute() accepts a value of type interface{}.
You are the one creating the HomePage, RegisterPage and ContactPage structs. What stops you from embedding a BasePage struct with the required fields? Are you worried you will forget about it? You will notice it at the first testing, I wouldn't worry about that:
type BasePage struct {
Title string
Other string
// other required fields...
}
type HomePage struct {
BasePage
// other home page fields...
}
type RegisterPage struct {
BasePage
// other register page fields...
}
If you want from code to check if page structs embed the BasePage, I recommend another way: interfaces.
type HasBasePage interface {
GetBasePage() BasePage
}
Example HomePage that implements it:
type HomePage struct {
BasePage
// other home page fields...
}
func (h *HomePage) GetBasePage() BasePage {
return h.BasePage
}
Now obviously only pages that have a GetBasePage() method can be passed as a value of HasBasePage:
var page HasBasePage = &HomePage{} // Valid, HomePage implements HasBasePage
If you don't want to use interfaces, you can use the reflect package to check if a value is a struct value and if it embeds another interface. Embedded structs appear and can be accessed like ordinary fields e.g. with Value.FieldByName(), with the type name being the field name.
Example code using reflect to check if a value embeds BasePage:
page := &HomePage{BasePage: BasePage{Title: "Home page"}}
v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
fmt.Println("Error: not struct!")
return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
fmt.Println("Error: struct does not embed BasePage!")
return
}
fmt.Printf("%+v", bp)
Output (try it on the Go Playground):
{Title:Home page Other:}
I don't understand the purpose of using templates in Kohana. I see almost no difference in the process of building a view with a template controller vs a regular controller, except that the template controller is tied to a given template and so is less flexible. What are the advantages?
Building view with regular controller:
Class Controller_Hello extends Controller
{
public function action_index()
{
$view = View::factory('page');
$view->page_title = 'My Hello App';
$view->content = 'hello, world!';
$view->sidebar = View::factory('parts/sidebar');
$this->response->body($view);
}
}
Building view with template controller:
Class Controller_Hello extends Controller_Template
{
public $template = 'page';
public function action_index()
{
$this->template->page_title = 'My Hello App';
$this->template->content = 'hello, world!';
$this->template->sidebar = View::factory('parts/sidebar');
}
}
Controller_Template is just an example of how you can implement your own templating-system.
It is not ready-to-use solution (at least for my projects usually). Check this one controller (it is also not ready-to-use solution but possibly it will help you understand point of extending different controllers for different purposes): http://pastie.org/2563595
I am sure there are other, maybe better solutions for templating systems. But why am I using templates in Kohana?
Think about multiple pages, all based upon one layout/design scheme. So I build a template controller using a certain view, defining layout/design, defining content, header and footer "areas". In the template controller I am loading the CSS files and script files, setting the title and meta values of the website, because every single site is using these CSS/script files with the same meta values and title.
So in every Controller extending the template controller I don't need to load the CSS/script files anew, set the meta values and title etc... But I could change all these values, maybe add a CSS file only for a single site.
Maybe all the mentioned sites have the same footer and/or header: I assign the header/footer view to the template within the template controller, so I don't need to do that in all the controller extending the template controller. Or all actions in one controller have the same header/footer, so I assignt he header and footer few in the before() function of the controller...
For me templates in kohana are a good utility for building small web applications.
Hi
I am trying to see if anybody has a briliant idea on how to implement View mode concept in MVC. So if the user opens a page, the page should open in view mode (all controls disabled), if they dont have edit priviledges else should open as normal. Please note that the View page has partial pages as well
I think you have total control on your page under MVC framework. If you are using standard MVC method to generate input controls, you could do following ways.
#Html.TextBox("MyTextBoxID", Model==null?"":Model.MyFieldValue, new {disabled = "disabled})
If you are not using standard MVC method to generate input controls. You can create your own method to generate input controls. For example in MyExt.cs
public static class MyExt
{
public static MvcHtmlString MyTextBox(this HtmlHelper html, string id, object value)
{
// check user privilege
if (CurrentUser.CanEditThisPage /*Implement your own logic here */)
return html.TextBox(id, value);
else
return html.TextBox(id, value, new {disabled = "disabled"});
}
}
And in your page
#using MyNamespace
...
#Html.MyTextBox("MyTextBoxID", Mode==null?"":Model.MyFieldValue)
Another Way
Pass an indicator from server side to client side and using javascript or JQuery to disable all controls.
#Html.Hidden("CanEdit", CurrentUser.CanEditThisPage)
In javascript
void pageLoad() {
if ($("#CanEdit").val() == "true"))
$("input").attr("disabled", "disabled");
}
Something like that (not sure about correctness of syntax :P)