B4J Tutorial [BANanoVue] - Building WebApps/Websites with VueJS

Mashiane

Expert
Licensed User
Ola

The world has changing at a very lightning-fast speed. Gone are the days when websites were static. Ajax came for dynamic websites and soon React, Angular, Vue, the list is long.

A couple of months ago I went for an interview. Do you React? Do you Angular? Do you Vue? Sadly I never got the job. Anyway. You don't need BANano to write VueJS apps, but I like BANano and it makes things easy for me. So let's get to it. If you are reading this great. Welcome to the club of exploring VueJS with BANano.

What is Vue?

Vue is an open source JavaScript framework geared towards building user interfaces, created by Evan You. It is said to be the progressive JavaScript framework that is approachable, versatile, and performant. This means...

1. You can scale it up or down.
2. If you know HTML, CSS, Javascript, you are good to go.
3. There is an ecosystem, Vue_CLI, VueX, Vue-Router, Vue-Test Utils
4. Its uses Virtual DOM

Etc, Etc.

So how do I use it?

So fire up B4J, create a new project, establish a reference to the BANano Library and...

B4X:
BANano.Header.AddJavascriptFile("vue.min.js")
You can add any css framework you want by the way!

Then, create a code module, let's call it pgIndex, add some initialization code and let's create a Hello Vue page.

What are we going to do?

1. We will create a Vue application.

B4X:
new Vue({
// options
});
2. We will create div in our body and then tell Vue to "manage" the div as a Vue section.

B4X:
new Vue({
el: '#app',
});
3. We will then change the state of the div content we created. Remember, Vue is a reactive framework.

B4X:
new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
},
});
4. To change the state, we use something like Moustache syntax.

B4X:
<html>
<body>
<div id="app">
<h2>{{ greeting }}</h2>
</div>
</body>
</html>
Before we do this, let's run some silly assumptions

1. You have a development, webserver running. I am using laragon. This means the publish folder is 'c:\laragon\www', if you are using xampp, change that to 'c:\xampp\httpdocs' I think. You can also use Google Webserver also it however does not play well with PHP.
2. You have some know how about CSS, HTML and JavaScript.
3. You have set up your dev environment for B4J + BANano dev.

Now that you have checked all the Silly Assumptions, let's Get ready, Set and Go!

We are still on the same page ja? (In a german accent). You still get the drift isn't it? ;) Cool!

We are going to do this in 4 lines of code. Don't believe me? Check this out!

B4X:
Sub Init
    'initialize vue, this clears the body and adds an empty div identified with app.
    Vue.Initialize
    'set the default state
    Vue.SetDefaultState("greeting", "Hello World!")
   
    'let's update the app div
    BANano.GetElement("#app").Append($"<h2>{{ greeting }}</h2>"$)
   
    'execute the DOM rendering
    Vue.ux
End Sub
I told you... minus the comments its just 4 lines. Ha ha ha!

I have activated the device mode in Google Chrome, it kinda looks cute. (you can turn it on /off if you want with Crtl +Shift + I)

HelloVue.png



Now let's just get a few things out of the way.

1. The used BP (BANanoPostProcessor) generates the logs to an external file. Find it in the attached zip. I have found it easier if I do that as at times the log becomes small to see errors, they scroll out of view at times, so an external file makes sense to me.

2. With experience gained experimenting with React, thus the BANanoReact which I am still exploring, my personal choice is Vue as the learning curve is not that steep.

3. Mustache - https://github.com/janl/mustache.js/

4. As this is still an experiment, expect things to change. They always do.

5. This is going to be very short as I WONT explore CSS frameworks with it.

Let's park this for later then.

The Mash
 

Attachments

Mashiane

Expert
Licensed User
Let's up the scale a little bit: Changing State and Event Management

1. We will add more states to our project.
2. We will create a button and use a directive to add an event listener
3. We will change the state so that it updates the UI of our application.

Methods are defined inside the Vue app, so we need a way to ensure that each time we link an event to an HTML object, the methods collection is updated.

To demonstrate this, let's see below.

ChangeState.gif


We have updated our BANanoVue class with some new subs and other things.

We define a class that will render the HTML of the button to our specifications. This has an event setter method.

B4X:
'set onclick event
Sub SetOnClick(module As Object, methodName As String) As VueButton
    methodName = methodName.tolowercase
    Dim e As BANanoEvent
    Dim cb As BANanoObject = BANano.CallBack(module, methodName, e)
    'set the directive
    Button.SetAttr("v-on:click", methodName)
    'add to methods
    vue.SetMethod(methodName, cb)
    Return Me
End Sub
To ensure that our click button works, Vue uses directives, this is v-on:click for a click event listener.

As noted, when the event is set via a callback, the vue.SetMethod sub is also called to update the methods that Vue needs to know about when executing.

Let's look at the code..

B4X:
Sub Init
    'initialize vue, this clears the body and adds an empty div identified with app.
    app = Vue.Initialize
    'set the font family
    Vue.SetFontFamily($"'Ubuntu', sans-serif"$)
    'set the default state
    Dim state As Map = CreateMap()
    state.Put("greeting", "Hello World!")
    state.Put("user", "The Mash")
    state.Put("city","East London, South Africa")
    Vue.SetDefaultState(state)
 
    'let's update the app div
    app.Append($"<h2>{{ greeting }}</h2>"$)
    app.Append($"<p>by {{ user }} who lives in {{ city }}</p>"$)
    '
    Dim btn1 As VueButton
    btn1.Initialize(Vue, "btn1").SetOnClick(Me, "changeGreeting").SetText("Change Greeting").Render("#app")
     
    'execute the DOM rendering
    Vue.ux
End Sub
Now we have a greeting, the name of the user and the city. We first set these states with .SetDefaultState.

We add the button and link the changeGreeting event to the button and then add it to the app div.

When the button is clicked, it checks the current state of the greeting key in the app, and toggles it. We use BANano.IIf for this determination.

B4X:
Sub changeGreeting(e As BANanoEvent)
    'get current greeting
    Dim greeting As String = Vue.GetState("greeting")
    'toggle the state
    greeting = BANano.IIf(greeting = "Hello World!", "What's Up", "Hello World!")
    'update the state
    Vue.SetState(CreateMap("greeting":greeting))
End Sub
If we had to look at the actual Vue code for this, it's as less code as..

B4X:
new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'The Mas',
city: 'East London'
},
methods: {
changeGreeting() {
this.greeting = this.greeting === 'Hello World!' ?
'What's Up' :
'Hello World!';
}
}
});
We had to BANano our way around this one hey. I like the fact that we can just have our events separated from the main stuff here. Converting Vue to BANano is easy isnt it?

By the way, we are almost done with this tutorial. Let's summarize what we have done so far.

Summary

1. We have created our first Vue App.
2. We created DOM elements using BANanoElement.Append
3. We set the default state of our Vue app.
4. We created a button and assigned an event to it.
5. We created an event listener using directives so that the external defined event works for View.
6. We wrote some code for our event to read the sate and then set it all updating the DOM and thus making our UI reactive.

To achieve this, we made some important changes to our code.

As you have noted, there is nothing special we did to our HTML elements, its still the basic ones we know and use. But then again, let's take a look at how we would have done this using JavaSCript.

B4X:
let greetingTag = document.getElementsByTagName("h2")[0];
changeGreeting = () => {
if (greetingTag.textContent === 'Hello World!') {
greetingTag.textContent = 'What is up!';
} else {
greetingTag.textContent = 'Hello World!';
}
}
Now that looks like... :mad: isnt it?

:rolleyes::rolleyes::rolleyes::rolleyes:

Oh yes, inline javascript is perfectly acceptable here still.

Later!
 

Attachments

Last edited:

Mashiane

Expert
Licensed User
Showing Elements when a State changes with v-if and v-show directives

I hope you are catching up. This has been a very interesting journey.

In the previous example we changed the heading of the greeting by changing the state. In this exercise we want to change some states that will affect the normal HTML attributes. To change these its said one needs to use a binding directive. For this we will show the following:

1. v-if - show an element only when a particular state condition is met and
2. v-show - toggle the display of the element. (The element is existing but hidden / shown)

To indicate this, we will add to our example, change a city. When the city changes we update some states and also change the images for the cities. We can demo this with..

ConditionalDirectives.gif


When the app starts, the default states we have is..

B4X:
state.Put("image", "./assets/benz.jpg")
    state.Put("desc", "Mercerdes Benz East London Plant")
Now when we change the 'city' we need to updates the states. Automatically, the underlying DOM is updated as soon as the state information changes.

So, we add 2 paragraphs. 1 shows only when a condition is met and the other shows based on the display style toggling.

B4X:
Dim p1 As VueParagraph
    p1.Initialize(Vue, "p1").SetText($"by {{ user }} who lives in {{ city }}"$).SetIf("city", "===", "'East London'").Render("#app")
    '
    Dim p2 As VueParagraph
    p2.Initialize(Vue, "p2").SetText($"by {{ user }} who wishes to visit {{ city }}"$).SetShow("city", "===", "'Jerusalem'").Render("#app")
These are assigned using the .SetIf and the .SetShow method. Here we pass the state to check, the condition and the value, inside ''.

We add a new button named btn2 to trap the event to change the city..

B4X:
Dim btn2 As VueButton
    btn2.Initialize(Vue, "btn2").SetOnClick(Me, "changeCity").SetText("Change City").Render("#app")
When this button is clicked, the state of the city is read and depending on what it is, some actions are undertaken..

B4X:
Sub changeCity(e As BANanoEvent)
    'get the current city
    Dim city As String = Vue.GetState("city")
    Select Case city
    Case "East London"
        Dim opt As Map = CreateMap()
        opt.Put("city", "Jerusalem")
        opt.Put("image", "./assets/israel.jpg")
        opt.Put("desc", "The Jerusalem")
        Vue.SetState(opt)
    Case Else
        Dim opt As Map = CreateMap()
        opt.Put("city", "East London")
        opt.Put("image", "./assets/icc.jpg")
        opt.Put("desc", "International Convention Centre")
        Vue.SetState(opt)
    End Select
End Sub
So here, we are saying, if the state is East London, change the city, image and description states for Jerusalem and if Jerusalem, revert to something else.

We are still together, isnt it? ;)

Ta!

PS: The code in each of these posts is evolving. The latest developments and updates will always be the latest posted thread.
 

Attachments

Mashiane

Expert
Licensed User
Creating lists with v-for looping directive.

To close of the evening, we finalize with lists. We will use the v-for directive to do so. This works like 'for each item as string in list'.

1. We store the values of the list as key value pairs in the state of the app. Whilst you can have other values for each record, each record should have the key. This is recommended and thus will be enforced with BANanoVue from the beginning.
2. If you are not defining a template to render the contents of the list, also specify the 'value' for each record.

NB: The id and value for each record are kinda compulsory.

B4X:
'set the default state, lets create a list we can change
    'rather specify the id and value for each list item
    Dim numbers As List
    numbers.Initialize
    numbers.Add(CreateMap("id":"1","value":"1"))
    numbers.Add(CreateMap("id":"2","value":"10"))
    numbers.Add(CreateMap("id":"3","value":"100"))
    numbers.Add(CreateMap("id":"4","value":"1000"))
    numbers.Add(CreateMap("id":"5","value":"10000"))
    
    'this is stored as data.numbers = numbers(list)
    Dim state As Map = CreateMap()
    state.Put("numbers", numbers)
    Vue.SetDefaultState(state)
These are stores in the 'numbers' state in the data of our app.

The next step is to create the UL and the LI item structure to be rendered. This is the recommended approach for BANanoVue.

B4X:
'create an unordered list
    Dim ul As VueUL
    ul.Initialize(Vue, "ul1")
    '
    'specify the details for each item item in the list
    'and set a data source
    Dim li As VueLI
    li.Initialize(Vue, "").SetData("numbers")
    'set the UL to receive the LI that it will render
    ul.SetLI(li)
This generates this list...

list1.png


Internally, the code for the UL generated looks like this..

B4X:
<ul  ><li v-for="row in numbers" v-bind:key="row.id" >{{ row.value }}</li></ul>
Each record in the 'numbers' state is recognized as a 'row', so to reference it outside the app when creating templates, one will use, row.id, row.value.

This then creates a loop for each record in the LI inside the UL using the v-for directive, defaulting the text to the value for each record.

NB: The only thing that needs to be changed in the 'numbers' data source in the state to change the list.

To refresh

1. We created a list of records that will be rendered in our list and set the default state as 'numbers'
2. We created a UL and set each LI item to display the value of each record from the data source and set the key to id.

Simply put, this is our UL definition.

B4X:
Dim ul As VueUL
    ul.Initialize(Vue, "ul1")
    Dim li As VueLI
    li.Initialize(Vue, "").SetData("numbers")
    ul.SetLI(li)
That's all you need to do, as long as your data source has key value pairs 'id,value'.
 

Mashiane

Expert
Licensed User
Using a template with lists using v-for

list2.png


As you can see above, each list item shows a value and then a textbox. The code from the previous example still applies but we change the structure of the UL. Here we go.

This is what we will create. Here we go...

B4X:
<ul  >
    <li v-for="row in numbers" v-bind:key="row.id" >
        <div  >
            <p  >{{ row.value }}</p>
            <input placeholder="type something..." >
        </div>
    </li>
</ul>
As you note, the structure of the output here is different, we need then to create a template and set it to the LI.

B4X:
'create a ul
    Dim ul As VueUL
    ul.Initialize(Vue, "ul1")
    
    ' create a template
    Dim tmpDiv As VueDIV
    tmpDiv.Initialize(Vue, "ul1Tmp")
    Dim p1 As VueParagraph = tmpDiv.CreateParagraph("p1").SetText($"{{ row.value }}"$)
    Dim txt1 As VueInput = tmpDiv.CreateInput("txt1").SetPlaceholder("type something...")
    tmpDiv.AddElements(Array(p1.Paragraph, txt1.input))
    '
    'specify the details for each item item in the list
    Dim li As VueLI
    li.Initialize(Vue, "").SetData("numbers").SetTemplate(tmpDiv.ToString)
    '
    ul.SetLI(li)
    '
    ul.Render("#app")
That coveres it!
 

Attachments

Top